extends SceneTree const PythonUISurface = preload("res://python_ui_surface.gd") const Python3DSurface = preload("res://python_3d_surface.gd") var ui: Control var world: Node3D func _initialize() -> void: call_deferred("run") func _py(source: String) -> bool: return Metin2PythonHost.run_line(source) == "" func _pump_until(seconds: float, condition: Callable) -> bool: var deadline := Time.get_ticks_msec() + int(seconds * 1000.0) while Time.get_ticks_msec() < deadline: if condition.call(): return true await process_frame return condition.call() func run() -> void: root.size = Vector2i(1024, 768) world = Python3DSurface.new() ui = PythonUISurface.new() root.add_child(ui) root.add_child(world) ui.run_app() await _pump_until(15, func(): return _py( "import __main__, os, networkModule, introLogin, introSelect, game, player, background, chr, chrmgr\n" + "__main__._stream = [o for o in __import__('gc').get_objects() if isinstance(o, networkModule.MainStream)][0]" )) await _pump_until(20, func(): return _py( "assert isinstance(_stream.curPhaseWindow, introLogin.LoginWindow)" )) var host = OS.get_environment("MT_LIVE_HOST") var login = OS.get_environment("MT_LIVE_LOGIN") var pwd = OS.get_environment("MT_LIVE_PASSWORD") var auth_port = int(OS.get_environment("MT_LIVE_AUTH_PORT")) var game_port = int(OS.get_environment("MT_LIVE_GAME_PORT")) _py("_stream.SetConnectInfo('%s', %d, '%s', %d)\n" % [host, game_port, host, auth_port] + "_stream.curPhaseWindow.Connect('%s', '%s')" % [login, pwd]) await _pump_until(30, func(): return _py( "assert isinstance(_stream.curPhaseWindow, introSelect.SelectCharacterWindow)" )) _py("_stream.curPhaseWindow.SelectSlot(0)\n_stream.curPhaseWindow.StartGame()") await _pump_until(30, func(): return _py( "assert isinstance(_stream.curPhaseWindow, game.GameWindow) and _stream.curPhaseWindow.IsShow()" )) for i in 60: await process_frame print("=== INVESTIGATING ALL 518 ACTORS ===") var draws: Array = Metin2PythonHost.render3d_draws() var actor_draws := {} for d in draws: var wm: PackedFloat32Array = d["world"] var key := "%.1f,%.1f,%.1f" % [wm[12], wm[13], wm[14]] if not actor_draws.has(key): actor_draws[key] = [] actor_draws[key].append(d) print("Unique actor positions: ", actor_draws.size()) var anomalies := [] for key in actor_draws: var ds: Array = actor_draws[key] var wm: PackedFloat32Array = ds[0]["world"] var map_x := wm[12] var map_y := -wm[13] var actor_z := wm[14] var bg_h = float(Metin2PythonHost.evaluate("str(background.GetHeight(%f, %f))" % [map_x, map_y])) var terrain_h = float(world.terrain.call("sample_height", map_x / 100.0, map_y / 100.0)) * 100.0 if world.terrain else 0.0 var diff_bg = actor_z - bg_h var diff_terrain = actor_z - terrain_h # Find textures var texs := [] var min_vert_z := 1e9 for d in ds: var t: String = d.get("texture0", "") if not t.is_empty(): texs.append(t.get_file()) for p in d["positions"]: min_vert_z = minf(min_vert_z, actor_z + p.z) if absf(diff_bg) > 5.0 or absf(diff_terrain) > 5.0: anomalies.append({ "pos": [map_x, map_y, actor_z], "bg_h": bg_h, "terrain_h": terrain_h, "diff_bg": diff_bg, "diff_terrain": diff_terrain, "vert_min_diff": min_vert_z - bg_h, "textures": texs }) print("Found %d anomalies with gap > 5cm!" % anomalies.size()) for a in anomalies.slice(0, 30): print("Anomaly: pos=(%.1f, %.1f, %.1f), bg_h=%.1f, terrain_h=%.1f, diff_bg=%.1f, diff_terrain=%.1f, vert_min_diff=%.1f, tex=%s" % [ a["pos"][0], a["pos"][1], a["pos"][2], a["bg_h"], a["terrain_h"], a["diff_bg"], a["diff_terrain"], a["vert_min_diff"], a["textures"] ]) quit(0)