@@ -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 )