# Real GameScene/assets, delayed authoritative row, no network or account writes. extends SceneTree const GameScene = preload("res://game_scene.gd") const Fixtures = preload("res://gamescene_test.gd") var failed := false func _initialize() -> void: call_deferred("run") func run() -> void: root.size = Vector2i(1280, 720) var output := OS.get_environment("MT_RENDER_OUTPUT") if output.is_empty(): output = ProjectSettings.globalize_path("res://../build/rendering/gamescene-%d" % Time.get_unix_time_from_system()) DirAccess.make_dir_recursive_absolute(output) var fc := Fixtures.FakeClient.new() root.add_child(fc) var scene := GameScene.new() root.add_child(scene) await scene.setup(fc, AssetRoot.path(), "OutdoorA1/metin2_map_a1") # Choose C1 to exercise late map correction too (setup initially had no VID). var use_c1 := "--c1" in OS.get_cmdline_user_args() var server := Vector2(959109, 269267) if use_c1 else Vector2(454600, 934000) fc.main = 1000 fc.ents[1000] = {"vid": 1000, "name": "RenderTest", "race": 0, "pos": Vector3(server.x * 0.01, 0, -server.y * 0.01), "pos_cm": server, "is_main": true, "angle_deg": 0.0, "func": 0, "moving": false, "hp": 100, "max_hp": 100, "dead": false} for i in 30: await process_frame var expected := MapCoord.to_world(fc.ents[1000].pos) expected.y = scene.world.sample_height(expected.x, expected.z) failed = not scene._model_built or scene.player.position.distance_to(expected) > 0.02 if use_c1: failed = failed or scene.map_path != "OutdoorC1/metin2_map_c1" var surfaces := 0 for mi in scene.player.find_children("*", "MeshInstance3D", true, false): if mi.mesh: surfaces += mi.mesh.get_surface_count() failed = failed or surfaces == 0 or not scene._map_loaded() if scene._model_built and scene.player.anim: scene.player.anim.set_time(0.5) scene.player.anim.set_process(false) scene.cam.snap_to_target() if DisplayServer.get_name() != "headless": await RenderingServer.frame_post_draw failed = failed or root.get_texture().get_image().save_png(output.path_join("game.png")) != OK var report := {"passed": not failed, "map": scene.map_path, "server_cm": str(server), "world_position": str(scene.player.position), "expected": str(expected), "surfaces": surfaces, "map_report": scene.world.get_load_report(), "engine": Engine.get_version_info(), "viewport": str(root.size), "camera_transform": str(scene.cam.global_transform), "fov": scene.cam.fov, "assets": AssetRoot.path(), "headless": DisplayServer.get_name() == "headless", "scope": "real GameScene, delayed main row, no main signal; no server/40250 parity"} var hashes := {} for path in ["res://game_scene.gd", "res://ui/player_view.gd", "res://net/mapcoord.gd", "res://gamescene_visual_test.gd", "res://bin/libmtgodot.macos.template_debug.dylib"]: hashes[path] = FileAccess.get_sha256(path) if scene._model_built: for property in ["gr2_path", "hair_gr2", "hair_skin"]: var path := String(scene.player.model.get(property)) if not path.is_empty(): hashes[path] = FileAccess.get_sha256(path) var motion := String(scene.player.anim.get("anim_path")) if scene.player.anim else "" if not motion.is_empty(): hashes[motion] = FileAccess.get_sha256(motion) report["sha256"] = hashes report["animation_sample_seconds"] = 0.5 var file := FileAccess.open(output.path_join("report.json"), FileAccess.WRITE) file.store_string(JSON.stringify(report, "\t")) file.close() print("GAMESCENE_VISUAL: ", JSON.stringify(report)) scene.queue_free() fc.queue_free() await process_frame quit(1 if failed else 0)