|
|
|
@@ -0,0 +1,138 @@
|
|
|
|
|
extends SceneTree
|
|
|
|
|
|
|
|
|
|
## Verifies that the pinned AzerothCore spawn is routed through the coordinate
|
|
|
|
|
## contract into an executable renderer checkpoint. GUI capture is performed by
|
|
|
|
|
## capture_render_checkpoints.gd with the validated dedicated manifest.
|
|
|
|
|
|
|
|
|
|
const COORDINATE_MAPPER_SCRIPT := preload("res://src/domain/coordinates/coordinate_mapper.gd")
|
|
|
|
|
const SERVER_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/server_world_position.gd")
|
|
|
|
|
|
|
|
|
|
const MANIFEST_PATH := "res://src/tools/server_spawn_render_manifest.json"
|
|
|
|
|
const EXPECTED_FIXTURE_PATH := "res://src/tests/fixtures/coordinate_golden_points.json"
|
|
|
|
|
const EXPECTED_SPAWN_NAME := "azerothcore_human_warrior_start"
|
|
|
|
|
const EXPECTED_PROFILE := "Blizzlike335"
|
|
|
|
|
const POSITION_TOLERANCE_YARDS := 0.002
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _initialize() -> void:
|
|
|
|
|
var failures: Array[String] = []
|
|
|
|
|
var manifest := _load_json_object(MANIFEST_PATH, failures)
|
|
|
|
|
var fixture_path := String(manifest.get("source_fixture", ""))
|
|
|
|
|
_expect_equal(fixture_path, EXPECTED_FIXTURE_PATH, "source fixture path", failures)
|
|
|
|
|
var fixture := _load_json_object(fixture_path, failures) if not fixture_path.is_empty() else {}
|
|
|
|
|
if manifest.is_empty() or fixture.is_empty():
|
|
|
|
|
_report_and_quit(failures)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
_expect_equal(int(manifest.get("schema_version", -1)), 1, "manifest schema version", failures)
|
|
|
|
|
_expect_equal(String(manifest.get("profile", "")), EXPECTED_PROFILE, "renderer profile", failures)
|
|
|
|
|
_expect_equal(String(manifest.get("map", "")), "Azeroth", "renderer map", failures)
|
|
|
|
|
_expect_true(ResourceLoader.exists(String(manifest.get("scene", ""))), "renderer scene exists", failures)
|
|
|
|
|
_expect_equal(String(manifest.get("source_spawn_name", "")), EXPECTED_SPAWN_NAME, "source spawn name", failures)
|
|
|
|
|
|
|
|
|
|
var spawn := _find_named_dictionary(fixture.get("server_spawns", []), EXPECTED_SPAWN_NAME)
|
|
|
|
|
_expect_true(not spawn.is_empty(), "pinned server spawn exists", failures)
|
|
|
|
|
var checkpoints: Array = manifest.get("checkpoints", [])
|
|
|
|
|
_expect_equal(checkpoints.size(), 1, "renderer checkpoint count", failures)
|
|
|
|
|
if spawn.is_empty() or checkpoints.size() != 1 or not (checkpoints[0] is Dictionary):
|
|
|
|
|
_report_and_quit(failures)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
var checkpoint: Dictionary = checkpoints[0]
|
|
|
|
|
var server_values := _three_floats(spawn.get("server_position", null), "server position", failures)
|
|
|
|
|
if server_values.is_empty():
|
|
|
|
|
_report_and_quit(failures)
|
|
|
|
|
return
|
|
|
|
|
var server_position = SERVER_WORLD_POSITION_SCRIPT.new(server_values[0], server_values[1], server_values[2])
|
|
|
|
|
var canonical_position = COORDINATE_MAPPER_SCRIPT.server_to_canonical(server_position)
|
|
|
|
|
var godot_position = COORDINATE_MAPPER_SCRIPT.canonical_to_godot(canonical_position)
|
|
|
|
|
var tile_coordinate = COORDINATE_MAPPER_SCRIPT.canonical_to_adt_tile(canonical_position)
|
|
|
|
|
var tile_local_position = COORDINATE_MAPPER_SCRIPT.canonical_to_adt_tile_local(canonical_position)
|
|
|
|
|
var chunk_coordinate = COORDINATE_MAPPER_SCRIPT.adt_tile_local_to_chunk(tile_coordinate, tile_local_position)
|
|
|
|
|
var mapped_position := [godot_position.x_units, godot_position.y_units, godot_position.z_units]
|
|
|
|
|
|
|
|
|
|
_expect_equal(String(checkpoint.get("name", "")), EXPECTED_SPAWN_NAME, "checkpoint name", failures)
|
|
|
|
|
_expect_true("server_spawn" in checkpoint.get("coverage", []), "server-spawn coverage", failures)
|
|
|
|
|
_expect_true(bool(checkpoint.get("diagnostic_spawn_marker", false)), "diagnostic spawn marker enabled", failures)
|
|
|
|
|
_expect_three_near(checkpoint.get("target", null), mapped_position, "checkpoint target", failures)
|
|
|
|
|
_expect_three_near(checkpoint.get("player", null), mapped_position, "checkpoint player", failures)
|
|
|
|
|
_expect_pair(checkpoint.get("expected_adt_tile", null), [tile_coordinate.tile_x, tile_coordinate.tile_y], "ADT tile", failures)
|
|
|
|
|
_expect_pair(checkpoint.get("expected_adt_chunk", null), [chunk_coordinate.chunk_x, chunk_coordinate.chunk_y], "ADT chunk", failures)
|
|
|
|
|
_expect_three_near(spawn.get("expected_godot_position", null), mapped_position, "fixture Godot position", failures)
|
|
|
|
|
var camera_values := _three_floats(checkpoint.get("camera", null), "checkpoint camera", failures)
|
|
|
|
|
if not camera_values.is_empty():
|
|
|
|
|
_expect_true(camera_values[1] > godot_position.y_units, "checkpoint camera is above spawn", failures)
|
|
|
|
|
_expect_true(Vector3(camera_values[0], camera_values[1], camera_values[2]).distance_to(Vector3(mapped_position[0], mapped_position[1], mapped_position[2])) > 1.0, "checkpoint camera is distinct from spawn", failures)
|
|
|
|
|
|
|
|
|
|
if not failures.is_empty():
|
|
|
|
|
_report_and_quit(failures)
|
|
|
|
|
return
|
|
|
|
|
print("SERVER_SPAWN_RENDERER PASS spawn=1 checkpoint=1 tile=%d_%d chunk=%d_%d profile=%s" % [
|
|
|
|
|
tile_coordinate.tile_x,
|
|
|
|
|
tile_coordinate.tile_y,
|
|
|
|
|
chunk_coordinate.chunk_x,
|
|
|
|
|
chunk_coordinate.chunk_y,
|
|
|
|
|
EXPECTED_PROFILE,
|
|
|
|
|
])
|
|
|
|
|
quit(0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _find_named_dictionary(entries: Array, expected_name: String) -> Dictionary:
|
|
|
|
|
for entry_variant in entries:
|
|
|
|
|
if entry_variant is Dictionary and String(entry_variant.get("name", "")) == expected_name:
|
|
|
|
|
return entry_variant
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _load_json_object(path: String, failures: Array[String]) -> Dictionary:
|
|
|
|
|
var file := FileAccess.open(path, FileAccess.READ)
|
|
|
|
|
if file == null:
|
|
|
|
|
failures.append("cannot open JSON: %s" % path)
|
|
|
|
|
return {}
|
|
|
|
|
var parsed = JSON.parse_string(file.get_as_text())
|
|
|
|
|
if not (parsed is Dictionary):
|
|
|
|
|
failures.append("JSON is not an object: %s" % path)
|
|
|
|
|
return {}
|
|
|
|
|
return parsed
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _three_floats(value_variant, label: String, failures: Array[String]) -> Array[float]:
|
|
|
|
|
if not (value_variant is Array) or value_variant.size() != 3:
|
|
|
|
|
failures.append("%s must contain three numbers" % label)
|
|
|
|
|
return []
|
|
|
|
|
return [float(value_variant[0]), float(value_variant[1]), float(value_variant[2])]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _expect_three_near(actual_variant, expected_values: Array, label: String, failures: Array[String]) -> void:
|
|
|
|
|
var actual_values := _three_floats(actual_variant, label, failures)
|
|
|
|
|
if actual_values.is_empty() or expected_values.size() != 3:
|
|
|
|
|
return
|
|
|
|
|
for component_index in range(3):
|
|
|
|
|
if absf(actual_values[component_index] - float(expected_values[component_index])) > POSITION_TOLERANCE_YARDS:
|
|
|
|
|
failures.append("%s[%d] expected %.9f, got %.9f" % [label, component_index, expected_values[component_index], actual_values[component_index]])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _expect_pair(actual_variant, expected_values: Array, label: String, failures: Array[String]) -> void:
|
|
|
|
|
if not (actual_variant is Array) or actual_variant.size() != 2:
|
|
|
|
|
failures.append("%s must contain two integers" % label)
|
|
|
|
|
return
|
|
|
|
|
for component_index in range(2):
|
|
|
|
|
_expect_equal(int(actual_variant[component_index]), int(expected_values[component_index]), "%s[%d]" % [label, component_index], failures)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _report_and_quit(failures: Array[String]) -> void:
|
|
|
|
|
if failures.is_empty():
|
|
|
|
|
failures.append("verification stopped without a diagnostic")
|
|
|
|
|
for failure in failures:
|
|
|
|
|
push_error("SERVER_SPAWN_RENDERER: %s" % failure)
|
|
|
|
|
quit(1)
|