274 lines
12 KiB
GDScript
274 lines
12 KiB
GDScript
extends SceneTree
|
|
|
|
## Headless contract and wiring regression for the first M03 renderer facade seam.
|
|
|
|
const WORLD_RENDER_FACADE_SCRIPT := preload("res://src/render/world_render_facade.gd")
|
|
const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
|
|
const STREAMING_FOCUS_SCRIPT := preload("res://src/domain/streaming/streaming_focus.gd")
|
|
const RENDERED_GROUND_SAMPLE_SCRIPT := preload("res://src/render/terrain/rendered_ground_sample.gd")
|
|
const WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT := preload("res://src/render/environment/world_environment_snapshot.gd")
|
|
const WOW_SKY_CONTROLLER_SCRIPT := preload("res://src/scenes/sky/wow_sky_controller.gd")
|
|
|
|
const RUNTIME_SCENE_PATHS: Array[String] = [
|
|
"res://src/scenes/streaming/eastern_kingdoms_streaming.tscn",
|
|
"res://src/scenes/streaming/kalimdor_streaming.tscn",
|
|
]
|
|
const TOOL_PATHS: Array[String] = [
|
|
"res://src/tools/capture_render_checkpoints.gd",
|
|
"res://src/tools/probe_render_camera_occluders.gd",
|
|
"res://src/tools/probe_render_terrain_height.gd",
|
|
]
|
|
const STREAMING_WORLD_LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
|
|
|
|
|
|
class StreamingWorldLoaderDouble extends Node:
|
|
var current_focus: StreamingFocus
|
|
var refresh_count := 0
|
|
var metrics := {"tiles": 2, "queues": {"tile": 1}}
|
|
var ground_sample: RefCounted = RENDERED_GROUND_SAMPLE_SCRIPT.available(42.25)
|
|
var ground_snapshot := {
|
|
"status": "sampled",
|
|
"terrain_height": 42.25,
|
|
"diagnostic": {"state_present": true},
|
|
}
|
|
|
|
func set_streaming_focus(streaming_focus: StreamingFocus) -> void:
|
|
current_focus = streaming_focus
|
|
|
|
func refresh_streaming_focus(_force: bool = false) -> bool:
|
|
refresh_count += 1
|
|
return current_focus != null
|
|
|
|
func render_baseline_snapshot() -> Dictionary:
|
|
return metrics
|
|
|
|
func sample_rendered_ground_height(_godot_world_position: GodotWorldPosition) -> RefCounted:
|
|
return ground_sample
|
|
|
|
func render_ground_query_snapshot(_godot_world_position: GodotWorldPosition) -> Dictionary:
|
|
return ground_snapshot
|
|
|
|
|
|
class WorldEnvironmentControllerDouble extends Node:
|
|
var applied_snapshot: RefCounted
|
|
|
|
func apply_environment_snapshot(world_environment_snapshot: RefCounted) -> bool:
|
|
applied_snapshot = world_environment_snapshot
|
|
return true
|
|
|
|
|
|
func _initialize() -> void:
|
|
var failures: Array[String] = []
|
|
_verify_rendered_ground_sample_contract(failures)
|
|
_verify_sky_controller_application(failures)
|
|
await _verify_delegation_and_snapshot_isolation(failures)
|
|
_verify_repository_wiring(failures)
|
|
|
|
if not failures.is_empty():
|
|
for failure in failures:
|
|
push_error("WORLD_RENDER_FACADE: %s" % failure)
|
|
quit(1)
|
|
return
|
|
|
|
print("WORLD_RENDER_FACADE PASS delegation=6 ground_contract=2 environment_contract=7 runtime_scenes=2 tools=3")
|
|
quit(0)
|
|
|
|
|
|
func _verify_rendered_ground_sample_contract(failures: Array[String]) -> void:
|
|
var non_finite_sample = RENDERED_GROUND_SAMPLE_SCRIPT.available(INF)
|
|
_expect_true(not non_finite_sample.is_available, "non-finite rendered ground is unavailable", failures)
|
|
_expect_true(
|
|
non_finite_sample.failure_code == &"render_terrain_height_not_finite",
|
|
"non-finite rendered ground failure code",
|
|
failures
|
|
)
|
|
var default_unavailable_sample = RENDERED_GROUND_SAMPLE_SCRIPT.unavailable(&"")
|
|
_expect_true(
|
|
default_unavailable_sample.failure_code == &"render_terrain_unavailable",
|
|
"empty rendered ground failure code normalizes",
|
|
failures
|
|
)
|
|
|
|
|
|
func _verify_sky_controller_application(failures: Array[String]) -> void:
|
|
var controller: Node = WOW_SKY_CONTROLLER_SCRIPT.new()
|
|
controller.set("use_system_time", true)
|
|
controller.set("time_speed", 4.0)
|
|
controller.set("fixed_time_hours", 3.0)
|
|
var snapshot: RefCounted = WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT.at_time_of_day(18.75)
|
|
_expect_true(
|
|
bool(controller.call("apply_environment_snapshot", snapshot)),
|
|
"sky controller accepts snapshot",
|
|
failures
|
|
)
|
|
_expect_true(not bool(controller.get("use_system_time")), "snapshot disables system clock", failures)
|
|
_expect_near(float(controller.get("time_speed")), 0.0, "snapshot freezes local clock", failures)
|
|
_expect_near(float(controller.get("fixed_time_hours")), 18.75, "snapshot applies exact time", failures)
|
|
controller.free()
|
|
|
|
|
|
func _verify_delegation_and_snapshot_isolation(failures: Array[String]) -> void:
|
|
var test_root := Node.new()
|
|
var loader := StreamingWorldLoaderDouble.new()
|
|
loader.name = "StreamingWorldLoaderDouble"
|
|
var source := Node3D.new()
|
|
source.name = "FocusSource"
|
|
source.position = Vector3(10.0, 20.0, 30.0)
|
|
var environment_controller := WorldEnvironmentControllerDouble.new()
|
|
environment_controller.name = "WorldEnvironmentControllerDouble"
|
|
var facade = WORLD_RENDER_FACADE_SCRIPT.new()
|
|
facade.name = "WorldRenderFacade"
|
|
facade.streaming_world_loader_path = NodePath("../StreamingWorldLoaderDouble")
|
|
facade.streaming_focus_source_path = NodePath("../FocusSource")
|
|
facade.world_environment_controller_path = NodePath("../WorldEnvironmentControllerDouble")
|
|
test_root.add_child(loader)
|
|
test_root.add_child(source)
|
|
test_root.add_child(environment_controller)
|
|
test_root.add_child(facade)
|
|
get_root().add_child(test_root)
|
|
await process_frame
|
|
|
|
_expect_true(loader.current_focus != null, "ready captures explicit source", failures)
|
|
if loader.current_focus != null:
|
|
_expect_near(loader.current_focus.world_position.x_units, 10.0, "source X", failures)
|
|
_expect_near(loader.current_focus.world_position.y_units, 20.0, "source Y", failures)
|
|
_expect_near(loader.current_focus.world_position.z_units, 30.0, "source Z", failures)
|
|
|
|
var typed_focus = STREAMING_FOCUS_SCRIPT.new(GODOT_WORLD_POSITION_SCRIPT.new(40.0, 50.0, 60.0))
|
|
facade.streaming_focus_source_path = NodePath()
|
|
facade.set_streaming_focus(typed_focus)
|
|
_expect_true(facade.refresh_streaming_focus(true), "typed focus refresh delegates", failures)
|
|
_expect_true(loader.current_focus == typed_focus, "typed focus reference delegates", failures)
|
|
|
|
var snapshot: Dictionary = facade.renderer_metrics_snapshot()
|
|
snapshot["tiles"] = 999
|
|
(snapshot["queues"] as Dictionary)["tile"] = 999
|
|
_expect_true(int(loader.metrics.tiles) == 2, "top-level metrics are detached", failures)
|
|
_expect_true(int(loader.metrics.queues.tile) == 1, "nested metrics are detached", failures)
|
|
|
|
var query_position = GODOT_WORLD_POSITION_SCRIPT.new(100.0, 200.0, 300.0)
|
|
var ground_sample: RefCounted = facade.sample_ground_height(query_position)
|
|
_expect_true(ground_sample.is_available, "rendered ground sample delegates", failures)
|
|
_expect_near(ground_sample.height_units, 42.25, "rendered ground height", failures)
|
|
loader.ground_sample = RENDERED_GROUND_SAMPLE_SCRIPT.unavailable(&"render_terrain_mesh_not_ready")
|
|
var unavailable_ground_sample: RefCounted = facade.sample_ground_height(query_position)
|
|
_expect_true(not unavailable_ground_sample.is_available, "unavailable ground sample delegates", failures)
|
|
_expect_true(
|
|
unavailable_ground_sample.failure_code == &"render_terrain_mesh_not_ready",
|
|
"ground failure code delegates",
|
|
failures
|
|
)
|
|
var ground_snapshot: Dictionary = facade.renderer_ground_query_snapshot(query_position)
|
|
ground_snapshot["terrain_height"] = 999.0
|
|
(ground_snapshot["diagnostic"] as Dictionary)["state_present"] = false
|
|
_expect_near(float(loader.ground_snapshot.terrain_height), 42.25, "ground snapshot top-level detached", failures)
|
|
_expect_true(bool(loader.ground_snapshot.diagnostic.state_present), "ground snapshot nested detached", failures)
|
|
|
|
var environment_snapshot: RefCounted = WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT.at_time_of_day(18.75)
|
|
_expect_true(
|
|
facade.apply_environment_snapshot(environment_snapshot),
|
|
"environment snapshot delegates",
|
|
failures
|
|
)
|
|
_expect_true(
|
|
environment_controller.applied_snapshot == environment_snapshot,
|
|
"environment snapshot reference delegates",
|
|
failures
|
|
)
|
|
var invalid_environment_snapshot: RefCounted = WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT.at_time_of_day(NAN)
|
|
_expect_true(
|
|
not facade.apply_environment_snapshot(invalid_environment_snapshot),
|
|
"invalid environment snapshot rejected",
|
|
failures
|
|
)
|
|
test_root.queue_free()
|
|
|
|
|
|
func _verify_repository_wiring(failures: Array[String]) -> void:
|
|
for scene_path in RUNTIME_SCENE_PATHS:
|
|
var scene_source := _read_text(scene_path, failures)
|
|
_expect_true(scene_source.contains('path="res://src/render/world_render_facade.gd"'), "%s loads facade" % scene_path, failures)
|
|
_expect_true(scene_source.contains('[node name="WorldRenderFacade" type="Node" parent="."]'), "%s owns facade node" % scene_path, failures)
|
|
_expect_true(scene_source.contains('streaming_focus_source_path = NodePath("../ThirdPersonPlayer")'), "%s facade uses player focus" % scene_path, failures)
|
|
_expect_true(scene_source.contains('world_environment_controller_path = NodePath("../WowSkyController")'), "%s facade uses sky controller" % scene_path, failures)
|
|
|
|
for tool_path in TOOL_PATHS:
|
|
var tool_source := _read_text(tool_path, failures)
|
|
_expect_true(tool_source.contains('get_node_or_null("WorldRenderFacade")'), "%s resolves facade" % tool_path, failures)
|
|
_expect_true(not tool_source.contains('world.call("refresh_streaming_focus"'), "%s avoids direct streamer refresh" % tool_path, failures)
|
|
_expect_true(not tool_source.contains('world.set("streaming_focus_source_path"'), "%s avoids direct streamer focus config" % tool_path, failures)
|
|
|
|
var terrain_probe_source := _read_text("res://src/tools/probe_render_terrain_height.gd", failures)
|
|
_expect_true(
|
|
terrain_probe_source.contains('"renderer_ground_query_snapshot"'),
|
|
"terrain probe delegates ground diagnostics",
|
|
failures
|
|
)
|
|
_expect_true(
|
|
not terrain_probe_source.contains('world.get("_'),
|
|
"terrain probe avoids private streamer state",
|
|
failures
|
|
)
|
|
for forbidden_probe_text in [
|
|
"_tile_states",
|
|
"_tile_load_queue",
|
|
"_tile_loading_tasks",
|
|
"_available_tiles",
|
|
"generate_triangle_mesh",
|
|
]:
|
|
_expect_true(
|
|
not terrain_probe_source.contains(forbidden_probe_text),
|
|
"terrain probe omits %s" % forbidden_probe_text,
|
|
failures
|
|
)
|
|
|
|
var capture_source := _read_text("res://src/tools/capture_render_checkpoints.gd", failures)
|
|
_expect_true(
|
|
capture_source.contains('call("apply_environment_snapshot", environment_snapshot)'),
|
|
"capture applies environment through facade",
|
|
failures
|
|
)
|
|
for forbidden_capture_text in [
|
|
'get_node_or_null("WowSkyController")',
|
|
'sky.set("use_system_time"',
|
|
'sky.set("time_speed"',
|
|
'sky.set("fixed_time_hours"',
|
|
]:
|
|
_expect_true(
|
|
not capture_source.contains(forbidden_capture_text),
|
|
"capture omits direct sky mutation %s" % forbidden_capture_text,
|
|
failures
|
|
)
|
|
|
|
var loader_source := _read_text(STREAMING_WORLD_LOADER_PATH, failures)
|
|
for required_loader_text in [
|
|
"func sample_rendered_ground_height(godot_world_position: GodotWorldPosition)",
|
|
"func render_ground_query_snapshot(godot_world_position: GodotWorldPosition)",
|
|
"RENDER_GROUND_QUERY_RAY_HEIGHT_UNITS := 5000.0",
|
|
"for radius_units in [2.0, 5.0, 10.0, 20.0, 40.0]",
|
|
"generate_triangle_mesh()",
|
|
]:
|
|
_expect_true(
|
|
loader_source.contains(required_loader_text),
|
|
"streamer ground query retains %s" % required_loader_text,
|
|
failures
|
|
)
|
|
|
|
|
|
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_near(actual_value: float, expected_value: float, label: String, failures: Array[String]) -> void:
|
|
if not is_equal_approx(actual_value, expected_value):
|
|
failures.append("%s expected %.3f, got %.3f" % [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)
|