CScreen::SetCursorPosition and CPythonGraphic::SetCursorPosition are the 40250 bodies, so RenderGame builds the pick ray under the cursor each frame. CPythonBackground::GetPickingPoint* march that ray as CMapOutdoor::__PickTerrainHeight does, against the native CMapManager height (PR_FLOAT_TO_INT's x87 rounding becomes floor); object picking waits for CMapOutdoor. CMapManager::isAttrOn reads the packed <map>/<xxxyyy>/attr.atr as CMapOutdoor::isAttrOn + CTerrain::isAttrOn do, and CheckAdvancing with its collision checkers is verbatim (no map objects are registered yet). All of this stays in the pending/platform layer and is marked ADAPTED. port.login_flow clicks the ground below the actor after the key walk. The actor walks to the picked point (334 cm offline), and the fake server sees FUNC_MOVE, then FUNC_WAIT. It also checks that the actor's cell is free and that its terrain block has blocked cells. port.login_live against the real server passed (key 133 cm, click 296 cm), and python_game_render_test.sh passes offline. MT_TEST_MODE=python_ui now adds the 3D surface, so the port route can be played by hand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
91 lines
4.0 KiB
GDScript
91 lines
4.0 KiB
GDScript
# client_main —— 打包客户端的入口场景根(main_scene = res://client_main.tscn)。
|
||
#
|
||
# 只做一件事:起 AppFlow(登录 → 选人 → 进游戏 串场控制器)。
|
||
# 资源目录由 AssetRoot 决定:默认 res://../assets(开发树),打包后设环境变量
|
||
# MT_ASSETS 指向真实资源目录即可(见 project/asset_root.gd)。
|
||
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、不连服务器。
|
||
# 导出模板不接受 --script,这是导出包里唯一能跑离线渲染回归的入口。
|
||
if OS.get_environment("MT_TEST_MODE") == "render":
|
||
AssetPack.ensure()
|
||
var t := PackageRenderTest.new()
|
||
t.name = "PackageRenderTest"
|
||
add_child(t)
|
||
return
|
||
# MT_TEST_MODE=python:在 app 进程里起内嵌解释器跑 system.py(PORT-PLAN 批次 2P step 4)。
|
||
# 导出模板不接受 --script,这是在真机 APK/IPA 里跑这条检查的唯一入口。
|
||
if OS.get_environment("MT_TEST_MODE") == "python":
|
||
AssetPack.ensure()
|
||
var fail: int = preload("res://python_host_check.gd").new().run()
|
||
if fail == 0:
|
||
print("PASS: python_host_check (app 进程内 system.py)")
|
||
else:
|
||
printerr("%d check(s) failed" % fail)
|
||
get_tree().quit(1 if fail else 0)
|
||
return
|
||
# MT_TEST_MODE=python_ui:40250 system.py 的界面直接画在 Godot Canvas 上(PORT-PLAN 2V0-f),
|
||
# Logo → 登录 → 选人 → 进游戏,RenderGame 的 3D 绘制由 python_3d_surface.gd 画出(2V2-e),
|
||
# 方向键/鼠标点地走动走 40250 原路径(2V3);app.Exit() 退出进程。
|
||
if OS.get_environment("MT_TEST_MODE") == "python_ui":
|
||
AssetPack.ensure()
|
||
var surface: Control = preload("res://python_ui_surface.gd").new()
|
||
surface.name = "PythonUISurface"
|
||
add_child(surface)
|
||
# The UI surface runs CPythonApplication::Process in its _process; the 3D surface reads that
|
||
# frame's draws after it, so it comes second in the tree.
|
||
var world: Node3D = preload("res://python_3d_surface.gd").new()
|
||
world.name = "Python3DSurface"
|
||
add_child(world)
|
||
var err: String = surface.run_app()
|
||
if err != "":
|
||
printerr("python_ui: " + err)
|
||
get_tree().quit(1)
|
||
return
|
||
if OS.get_environment("MT_TEST_MODE") == "forest_render":
|
||
var forest := ForestMapRenderTest.new()
|
||
forest.name = "ForestMapRenderTest"
|
||
add_child(forest)
|
||
return
|
||
# 打包 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)
|