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() world.name = "Python3DSurface" ui = PythonUISurface.new() ui.name = "PythonUISurface" root.add_child(ui) root.add_child(world) var err: String = ui.run_app() if err != "": print("ERROR: run_app: ", err) quit(1) return 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(60, func(): return _py( "assert isinstance(_stream.curPhaseWindow, game.GameWindow) and _stream.curPhaseWindow.IsShow()" )) await _pump_until(10, func(): return world.draw_count > 0) for i in 30: await process_frame var draws: Array = Metin2PythonHost.render3d_draws() print("Game 3D draws count: ", draws.size()) # Find actual actors var actor_draws: Array = [] for draw in draws: var tex = draw.get("texture0", "").to_lower() var is_actor = false for k in ["pc/", "pc2/", "monster/", "npc/", "warrior", "assassin", "sura", "shaman", "dog", "wolf"]: if k in tex: is_actor = true break if is_actor: actor_draws.append(draw) print("Actual actor draws count: ", actor_draws.size()) var actor_low := {} for draw in actor_draws: var draw_xform: Transform3D = world.d3d_transform(draw["world"]) var key := str(draw["world"]) for vertex in draw["positions"]: var p: Vector3 = draw_xform * vertex if not actor_low.has(key) or p.z < actor_low[key].z: actor_low[key] = p print("Distinct actors count: ", actor_low.size()) if world.terrain: var worst_actor_gap_cm := 0.0 for key in actor_low: var low: Vector3 = actor_low[key] var ground := float(world.terrain.call("sample_height", low.x / 100.0, -low.y / 100.0)) * 100.0 var gap = absf(low.z - ground) print("Actor at (%f, %f): foot_z=%f, ground_z=%f, gap=%f cm" % [low.x, low.y, low.z, ground, gap]) worst_actor_gap_cm = maxf(worst_actor_gap_cm, gap) print("WORST ACTUAL ACTOR GAP: ", worst_actor_gap_cm, " cm") quit(0)