feat: complete native client rendering parity updates

This commit is contained in:
shen
2026-09-27 19:44:41 -07:00
parent 9a9dc6d224
commit 25dd65f2ce
99 changed files with 21121 additions and 921 deletions
+115
View File
@@ -0,0 +1,115 @@
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, app\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()"
))
print("=== IN-GAME LOADED ===")
for i in 10:
await process_frame
print("--- EQUIPPING WEAPON 10 ---")
_py("chr.SelectInstance(player.GetMainCharacterIndex())\nchr.SetWeapon(10)")
for i in 10:
await process_frame
var check_draws = func(label: String):
var draws: Array = Metin2PythonHost.render3d_draws()
var body_draw = null
var weapon_draws = []
for d in draws:
var tex: String = d.get("texture0", "").to_lower()
if tex.contains("warrior") and not tex.contains("hair"):
body_draw = d
elif tex.contains("weapon") or tex.contains("sword") or tex.contains("00010") or tex.contains("00040"):
weapon_draws.append(d)
print("[%s] Total draws=%d, weapon_draws=%d" % [label, draws.size(), weapon_draws.size()])
var b_xf: Transform3D = Transform3D()
if body_draw != null:
var wm: PackedFloat32Array = body_draw["world"]
b_xf = Python3DSurface.d3d_transform(wm)
print(" Body origin: ", b_xf.origin, " tex=", body_draw.get("texture0", ""))
else:
print(" Body draw not found!")
for w in weapon_draws:
var wm: PackedFloat32Array = w["world"]
var xf: Transform3D = Python3DSurface.d3d_transform(wm)
var dist := xf.origin.distance_to(b_xf.origin) if body_draw != null else 0.0
print(" Weapon origin: ", xf.origin, " dist_to_body=%.2f cm tex=%s" % [dist, w.get("texture0", "")])
check_draws.call("IDLE_FRAME_0")
# Start moving!
print("--- STARTING MOVEMENT (DIK_UP) ---")
_py("player.SetSingleDIKKeyState(app.DIK_UP, True)")
for frame in range(1, 40):
await process_frame
if frame % 5 == 0:
check_draws.call("MOVING_FRAME_%d" % frame)
_py("player.SetSingleDIKKeyState(app.DIK_UP, False)")
for frame in range(1, 15):
await process_frame
check_draws.call("STOPPED")
quit(0)