|
|
|
@@ -0,0 +1,140 @@
|
|
|
|
|
extends SceneTree
|
|
|
|
|
|
|
|
|
|
## Repository-wide M01 gate against manual cross-space world coordinate formulas.
|
|
|
|
|
## Model-local M2/WMO basis transforms and ordinary Godot-space geometry are not
|
|
|
|
|
## world-space conversions and are intentionally outside these signatures.
|
|
|
|
|
|
|
|
|
|
const SCAN_ROOTS: Array[String] = ["res://src", "res://addons"]
|
|
|
|
|
const SOURCE_EXTENSIONS: Array[String] = ["gd", "cpp", "h", "hpp"]
|
|
|
|
|
const EXCLUDED_DIRECTORY_PREFIXES: Array[String] = ["res://src/native/build/"]
|
|
|
|
|
const GATE_PATH := "res://src/tools/verify_coordinate_conversion_boundaries.gd"
|
|
|
|
|
const COORDINATE_MAPPER_PATH := "res://src/domain/coordinates/coordinate_mapper.gd"
|
|
|
|
|
const CALIBRATION_ORACLE_PATH := "res://src/tools/verify_render_coordinate_calibration.gd"
|
|
|
|
|
const NATIVE_BOUNDARY_PATH := "res://src/native/src/wow_chunk_reader.h"
|
|
|
|
|
const ALLOWED_LEGACY_NAME_PATHS: Array[String] = [
|
|
|
|
|
NATIVE_BOUNDARY_PATH,
|
|
|
|
|
"res://src/native/src/adt_loader.cpp",
|
|
|
|
|
"res://src/native/src/wdt_loader.cpp",
|
|
|
|
|
CALIBRATION_ORACLE_PATH,
|
|
|
|
|
]
|
|
|
|
|
const REQUIRED_MAPPER_CALLS := {
|
|
|
|
|
"res://src/scenes/sky/wow_sky_controller.gd": ["godot_to_canonical", "godot_to_adt_tile", "godot_to_adt_tile_local"],
|
|
|
|
|
"res://src/scenes/streaming/streaming_world_loader.gd": ["godot_to_adt_tile", "godot_to_adt_tile_local", "adt_tile_local_to_godot"],
|
|
|
|
|
"res://src/scenes/player/third_person_wow_controller.gd": ["godot_to_adt_tile", "adt_tile_local_to_godot"],
|
|
|
|
|
"res://src/tools/probe_render_terrain_height.gd": ["godot_to_adt_tile"],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _initialize() -> void:
|
|
|
|
|
var failures: Array[String] = []
|
|
|
|
|
var source_paths: Array[String] = []
|
|
|
|
|
for scan_root in SCAN_ROOTS:
|
|
|
|
|
_collect_source_paths(scan_root, source_paths, failures)
|
|
|
|
|
|
|
|
|
|
var native_boundary_definition_count := 0
|
|
|
|
|
var independent_oracle_definition_count := 0
|
|
|
|
|
for source_path in source_paths:
|
|
|
|
|
if source_path == GATE_PATH:
|
|
|
|
|
continue
|
|
|
|
|
var source := _read_text(source_path, failures)
|
|
|
|
|
var source_lines := source.split("\n")
|
|
|
|
|
for line_index in range(source_lines.size()):
|
|
|
|
|
var source_line := String(source_lines[line_index])
|
|
|
|
|
var violation := _coordinate_violation(source_path, source_line)
|
|
|
|
|
if not violation.is_empty():
|
|
|
|
|
failures.append("%s:%d %s" % [source_path, line_index + 1, violation])
|
|
|
|
|
if source_path == NATIVE_BOUNDARY_PATH and source_line.contains("inline void wow_to_godot"):
|
|
|
|
|
native_boundary_definition_count += 1
|
|
|
|
|
if source_path == CALIBRATION_ORACLE_PATH and source_line.contains("func _wow_to_godot"):
|
|
|
|
|
independent_oracle_definition_count += 1
|
|
|
|
|
|
|
|
|
|
_expect_equal(native_boundary_definition_count, 1, "native world conversion definition count", failures)
|
|
|
|
|
_expect_equal(independent_oracle_definition_count, 1, "independent calibration oracle count", failures)
|
|
|
|
|
_verify_required_mapper_calls(failures)
|
|
|
|
|
_verify_classifier_guards(failures)
|
|
|
|
|
|
|
|
|
|
if not failures.is_empty():
|
|
|
|
|
for failure in failures:
|
|
|
|
|
push_error("COORDINATE_CONVERSION_BOUNDARIES: %s" % failure)
|
|
|
|
|
quit(1)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("COORDINATE_CONVERSION_BOUNDARIES PASS files=%d native_boundary=1 oracle=1 consumers=4 classifier_cases=6" % source_paths.size())
|
|
|
|
|
quit(0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _coordinate_violation(source_path: String, source_line: String) -> String:
|
|
|
|
|
var stripped_line := source_line.strip_edges()
|
|
|
|
|
if stripped_line.is_empty():
|
|
|
|
|
return ""
|
|
|
|
|
if (source_line.contains("wow_to_godot") or source_line.contains("godot_to_wow")) and source_path not in ALLOWED_LEGACY_NAME_PATHS:
|
|
|
|
|
return "manual WoW/Godot conversion name outside mapper boundary"
|
|
|
|
|
if (source_line.contains("17066.666") or source_line.contains("WOW_WORLD_CENTER")) and source_path not in [NATIVE_BOUNDARY_PATH, CALIBRATION_ORACLE_PATH]:
|
|
|
|
|
return "manual WoW world-center formula outside native boundary/oracle"
|
|
|
|
|
if source_line.contains("1600.0 / 3.0") and source_path != COORDINATE_MAPPER_PATH:
|
|
|
|
|
return "duplicated ADT tile-size formula outside CoordinateMapper"
|
|
|
|
|
if source_path != COORDINATE_MAPPER_PATH and source_line.contains("floor(") and source_line.contains("TILE_SIZE") and (source_line.contains(".x") or source_line.contains(".z")):
|
|
|
|
|
return "manual Godot world-position to ADT tile formula"
|
|
|
|
|
if source_path != COORDINATE_MAPPER_PATH and source_line.contains("fposmod(") and source_line.contains("TILE_SIZE"):
|
|
|
|
|
return "manual Godot world-position to ADT tile-local formula"
|
|
|
|
|
if source_path != COORDINATE_MAPPER_PATH and source_line.contains("+ 0.5) * TILE_SIZE"):
|
|
|
|
|
return "manual ADT tile-center to Godot world-position formula"
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _verify_required_mapper_calls(failures: Array[String]) -> void:
|
|
|
|
|
for source_path in REQUIRED_MAPPER_CALLS:
|
|
|
|
|
var source := _read_text(source_path, failures)
|
|
|
|
|
for required_call in REQUIRED_MAPPER_CALLS[source_path]:
|
|
|
|
|
if not source.contains(String(required_call)):
|
|
|
|
|
failures.append("%s must call CoordinateMapper.%s" % [source_path, required_call])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _verify_classifier_guards(failures: Array[String]) -> void:
|
|
|
|
|
_expect_true(not _coordinate_violation("res://src/example.gd", "const CENTER := 17066.666").is_empty(), "world-center classifier", failures)
|
|
|
|
|
_expect_true(not _coordinate_violation("res://src/example.gd", "func wow_to_godot():").is_empty(), "conversion-name classifier", failures)
|
|
|
|
|
_expect_true(not _coordinate_violation("res://src/example.gd", "floor(world_position.x / TILE_SIZE)").is_empty(), "world-to-tile classifier", failures)
|
|
|
|
|
_expect_true(not _coordinate_violation("res://src/example.gd", "fposmod(world_position.z, TILE_SIZE)").is_empty(), "world-to-tile-local classifier", failures)
|
|
|
|
|
_expect_true(not _coordinate_violation("res://src/example.gd", "(tile_x + 0.5) * TILE_SIZE").is_empty(), "tile-center classifier", failures)
|
|
|
|
|
_expect_true(_coordinate_violation(NATIVE_BOUNDARY_PATH, "inline void wow_to_godot(float wx) { gx = -(wx - 17066.666f); }").is_empty(), "native boundary exception", failures)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _collect_source_paths(directory_path: String, source_paths: Array[String], failures: Array[String]) -> void:
|
|
|
|
|
var directory := DirAccess.open(directory_path)
|
|
|
|
|
if directory == null:
|
|
|
|
|
failures.append("cannot scan directory %s" % directory_path)
|
|
|
|
|
return
|
|
|
|
|
for entry_name in directory.get_files():
|
|
|
|
|
var source_path := directory_path.path_join(entry_name)
|
|
|
|
|
if entry_name.get_extension().to_lower() in SOURCE_EXTENSIONS:
|
|
|
|
|
source_paths.append(source_path)
|
|
|
|
|
for child_directory_name in directory.get_directories():
|
|
|
|
|
var child_directory_path := directory_path.path_join(child_directory_name) + "/"
|
|
|
|
|
if _has_excluded_prefix(child_directory_path):
|
|
|
|
|
continue
|
|
|
|
|
_collect_source_paths(child_directory_path.trim_suffix("/"), source_paths, failures)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _has_excluded_prefix(path: String) -> bool:
|
|
|
|
|
for excluded_prefix in EXCLUDED_DIRECTORY_PREFIXES:
|
|
|
|
|
if path.begins_with(excluded_prefix):
|
|
|
|
|
return true
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _read_text(path: String, failures: Array[String]) -> String:
|
|
|
|
|
var file := FileAccess.open(path, FileAccess.READ)
|
|
|
|
|
if file == null:
|
|
|
|
|
failures.append("cannot open %s" % path)
|
|
|
|
|
return ""
|
|
|
|
|
return file.get_as_text()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _expect_equal(actual_value, expected_value, label: String, failures: Array[String]) -> void:
|
|
|
|
|
if actual_value != expected_value:
|
|
|
|
|
failures.append("%s expected %s, got %s" % [label, expected_value, actual_value])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _expect_true(actual_value: bool, label: String, failures: Array[String]) -> void:
|
|
|
|
|
if not actual_value:
|
|
|
|
|
failures.append("%s expected true" % label)
|