port: complete 100% logic layer, eliminate pending dir, and unify client_main on port route

- Completed batch 4-f (Messenger, Guild, GuildMark), 4-g (PythonSystem, systemSetting), 4-h (PythonSoundManagerModule, MilesLib CSoundManager)
- Ported UserInterface/Locale.cpp, EterLocale/Japanese.cpp, EterLib/PathStack, EterLib/NetDatagram* UDP suite
- Modularized PythonApplication into Camera and Event handlers aligning with 40250 signatures
- Completely removed extension/src/platform/pending/ directory
- Logic layer reached 100.0% completion (3338/3338 functions, 0 TODO, 0 DIVERGENT) with empty port-map queue
- Removed legacy AppFlow route and fallback from project/client_main.gd, switching main loop unconditionally to native port route
- Verified 15/15 port.login_flow integration steps and 19/19 port_gate suites passing
This commit is contained in:
shen
2026-09-23 22:17:59 -07:00
parent ee7a9cd3e2
commit 837c830182
85 changed files with 10228 additions and 1545 deletions
+2
View File
@@ -8,6 +8,8 @@ reloadable = true
macos.debug = "res://bin/libmtgodot.macos.template_debug.dylib"
macos.release = "res://bin/libmtgodot.macos.template_release.dylib"
macos.editor = "res://bin/libmtgodot.macos.template_debug.dylib"
macos.editor.arm64 = "res://bin/libmtgodot.macos.template_debug.dylib"
ios.debug = "res://bin/libmtgodot.ios.template_debug.a"
ios.release = "res://bin/libmtgodot.ios.template_release.a"
+16 -63
View File
@@ -1,25 +1,19 @@
# client_main —— 打包客户端的入口场景根(main_scene = res://client_main.tscn)。
#
# 默认走 port 路由(PORT-PLAN §2 总开关):40250 system.py 在内嵌解释器里跑,界面画在
# 走 40250 原生 port 路径:40250 system.py 在内嵌解释器里跑,界面画在
# python_ui_surface.gd、RenderGame 的 3D 绘制画在 python_3d_surface.gd。
# MT_PORT_PATH=legacy 回到旧的 GDScript AppFlow(登录 → 选人 → 进游戏 串场控制器);
# MT_TEST_MODE=smoke/playable 是 AppFlow 的真服测试,固定走 legacy。
# 资源目录由 AssetRoot 决定:默认 res://../assets(开发树),打包后设环境变量
# MT_ASSETS 指向真实资源目录即可(见 project/asset_root.gd)。
# 方向键/鼠标点地走动走 40250 原路径(2V3);app.Exit() 退出进程。
# 资源目录由 AssetRoot 决定:40250 Client/pack 资源包。
extends Node
const AppFlow = preload("res://app_flow.gd")
const LiveSmokeTest = preload("res://live_smoke_test.gd")
const PackageRenderTest = preload("res://package_render_test.gd")
const PlayableLiveTest = preload("res://playable_live_test.gd")
const ForestMapRenderTest = preload("res://forest_map_render_test.gd")
const NetTrace = preload("res://net_trace.gd")
func _ready() -> void:
if OS.get_environment("MT_TEST_MODE") == "particle_exit":
add_child(preload("res://particle_exit_test.gd").new())
return
# MT_TEST_MODE=render:包内离线渲染自检,不起 AppFlow、不连服务器。
# MT_TEST_MODE=render:包内离线渲染自检,不起 CPythonApplication、不连服务器。
# 导出模板不接受 --script,这是导出包里唯一能跑离线渲染回归的入口。
if OS.get_environment("MT_TEST_MODE") == "render":
AssetPack.ensure()
@@ -43,30 +37,22 @@ func _ready() -> void:
forest.name = "ForestMapRenderTest"
add_child(forest)
return
# port 路由:40250 system.py 的界面直接画在 Godot Canvas 上(PORT-PLAN 2V0-f),
# 原生 port 路径:40250 system.py 的界面直接画在 Godot Canvas 上(PORT-PLAN 2V0-f),
# Logo → 登录 → 选人 → 进游戏,RenderGame 的 3D 绘制由 python_3d_surface.gd 画出(2V2-e),
# 方向键/鼠标点地走动走 40250 原路径(2V3);app.Exit() 退出进程。
# MT_TEST_MODE=python_ui 是切换默认值之前的入口,保留为 MT_PORT_PATH=port 的别名。
var mode := OS.get_environment("MT_TEST_MODE")
var route := OS.get_environment("MT_PORT_PATH")
if mode == "python_ui":
route = "port"
elif mode == "smoke" or mode == "playable":
route = "legacy"
if route != "legacy":
if route != "" and route != "port":
push_warning("[client] 未知的 MT_PORT_PATH=%s,按 port 处理" % route)
if _start_port_route(route != ""):
return
_start_legacy_route()
if OS.has_environment("MT_PORT_PATH") and OS.get_environment("MT_PORT_PATH") == "legacy":
push_warning("[client] MT_PORT_PATH=legacy 已废弃并移除,40250 原生 port 路径已接管主循环")
# Returns false when the port route cannot start and the default (unset MT_PORT_PATH) should fall
# back to legacy; an explicit MT_PORT_PATH=port quits with the error instead.
func _start_port_route(explicit: bool) -> bool:
_start_port_route()
# 启动 40250 原生 port 路径
func _start_port_route() -> void:
AssetPack.ensure()
if not explicit and not (ClassDB.class_exists("Metin2PythonHost") and AssetRoot.pack_ready()):
push_warning("[client] port 路由缺 Metin2PythonHost 或 40250 Client/pack(%s),退回 legacy AppFlow" % AssetRoot.client_path())
return false
if not (ClassDB.class_exists("Metin2PythonHost") and AssetRoot.pack_ready()):
printerr("[client] port 路由缺少 Metin2PythonHost 或 40250 Client/pack(%s)" % AssetRoot.client_path())
get_tree().quit(1)
return
var surface: Control = preload("res://python_ui_surface.gd").new()
surface.name = "PythonUISurface"
add_child(surface)
@@ -79,36 +65,3 @@ func _start_port_route(explicit: bool) -> bool:
if err != "":
printerr("port: " + err)
get_tree().quit(1)
return true
func _start_legacy_route() -> void:
# 打包 app 从 Finder/open 启动时没有 shell 环境;40250 服务器使用 classic
# DH2/CTR 协议,因此默认固定到 classic,同时保留 MT_PROTOCOL 覆盖能力。
if OS.get_environment("MT_PROTOCOL").is_empty():
OS.set_environment("MT_PROTOCOL", "classic")
# 移动端:APK/IPA 里没有资源 —— 挂载外部的 assets.zip(adb push / ios-deploy 上传)。
AssetPack.ensure()
var root := AssetRoot.path()
if not AssetRoot.available():
push_warning("[client] 资源目录不存在:%s —— MT_ASSETS 环境变量 / assets.zip(adb push 到 files/)" % root)
var af := AppFlow.new()
af.name = "AppFlow"
add_child(af)
af.start(root)
# 主角 vid 生命周期取证(见 net_trace.gd):排在 AppFlow 之后,保证每帧
# 在 client 派发完信号之后再读 main_vid。
if af.client != null:
var trace := NetTrace.new()
trace.name = "NetTrace"
add_child(trace)
trace.setup(af.client)
if OS.get_environment("MT_TEST_MODE") == "smoke":
var smoke := LiveSmokeTest.new()
smoke.name = "LiveSmokeTest"
add_child(smoke)
smoke.setup(af, af.client)
if OS.get_environment("MT_TEST_MODE") == "playable":
var playable := PlayableLiveTest.new()
playable.name = "PlayableLiveTest"
add_child(playable)
playable.setup(af, af.client)
+1
View File
@@ -0,0 +1 @@
uid://07xdrb10ur7c
+1
View File
@@ -0,0 +1 @@
uid://g37mwhmliwc5
+1
View File
@@ -0,0 +1 @@
uid://chhb21kpt72wc
+1
View File
@@ -0,0 +1 @@
uid://d1vnobm0q3dhc
+1
View File
@@ -0,0 +1 @@
uid://bi1ruj2meedvx