Files
mtgodot-poc/project/python_host_check.gd
T
shenleiandClaude Opus 5 0a1cdf3d3e feat(python): 在 app 进程里起 40250 脚本层(2P step 4,macOS)
移植 UserInterface/RunMainScript 到 platform/ScriptLib/PythonBoot:40250 把
CPythonLauncher 放在 Main() 的栈上,这里进程归 Godot,launcher 必须活过创建它的
那次调用,所以拆成 Start / RunMainScript / RunLine / Stop。37 个模块初始化函数里
只调已移植的 initpack(),system.py 因此仍停在第一个 import。

脚本报错能传回 GDScript,靠的是 platform/EterBase/Debug.cpp 实现 LogBox/LogBoxf:
ScriptLib 的 Traceback() 在 PyErr_Fetch 之后只剩这一条路,LastLogBoxMessage()
(platform/EterBase/LogBox.h)是 traceback 唯一幸存的地方。

GDScript 侧新增 Metin2PythonHost(python_host_node.{h,cpp})——唯一同时认识
godot-cpp 和 port 树的单元,PythonBoot 保持 godot-free。检查脚本
python_host_check.gd 分两段:解释器 + 标准库那段不需要 40250 的 pack,哪个平台都
能跑;system.py 那段需要 pack,没有就跳过。桌面入口 python_host_test.gd,真机入口
MT_TEST_MODE=python(导出模板不接受 --script)。

验证(macOS):标准库可 import、codec 注册表可用、sys.path 非空,system.py 停在和
ctest port.python_launcher 完全相同的 ImportError: No module named app;
ctest 27/27。Android/iOS 进程内未做,正式启动路径仍不起解释器(2V0)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 10:43:10 +09:00

61 lines
2.8 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# python_host_check —— 在 app 进程里把内嵌解释器起来并跑 system.pydocs/PORT-PLAN.md 批次 2P step 4)。
#
# 和 ctest 的 `port.python_launcher` 跑的是同一条序列,区别只有一个:那边是独立可执行文件,这边在
# Godot 进程里,由 GDScript 发起 —— step 4 要验的就是这个差别。2V0 把 C++ 模块注册上之前,
# system.py 停在第一个 import`No module named app`),这就是当前的验收结果。
#
# 真机上通常没有 40250 的 Client/packAPK 里只有 python27.zip),所以分两段:解释器 + 标准库这一段
# 不需要 pack,任何平台都跑;system.py 这一段需要 pack,没有就跳过并说清楚。
#
# 桌面:python_host_test.gd--script);真机:MT_TEST_MODE=python 从 client_main.gd 进来。
extends RefCounted
# 2V0 之前 system.py 必然停在这里;2V0 之后这一行要跟着改。
const EXPECTED_STOP := "No module named app"
var fail := 0
func _ck(ok: bool, message: String) -> void:
if not ok:
fail += 1
printerr("FAIL: " + message)
else:
print("ok: " + message)
# 返回失败数;0 表示落在预期的停止点上(或按上面的规则跳过)。
func run() -> int:
if not ClassDB.class_exists("Metin2PythonHost"):
printerr("FAIL: Metin2PythonHost extension is not registered")
return 1
if not FileAccess.file_exists("res://python27.zip"):
print("SKIP: python_host_check(没有 res://python27.zip,需要 -DMTGODOT_EMBED_PYTHON=ON 的构建)")
return 0
# 40250 Main() 先注册 pack,再造 CPythonLauncher —— system.py 是从 pack 里读出来的。
var has_pack := AssetRoot.pack_ready()
var start_error: String = Metin2PythonHost.start("")
_ck(start_error == "", "解释器在 app 进程里起来了:" + start_error)
if start_error != "":
return fail
_ck(Metin2PythonHost.is_running(), "is_running() 为真")
_ck(Metin2PythonHost.start("") == "", "重复 start() 是空操作")
# 标准库真的在 sys.path 上(真机是沙盒里那份 python27.zip),codec 注册表也重建过。
_ck(Metin2PythonHost.run_line("import os, string, types, codecs") == "", "标准库可 import")
_ck(Metin2PythonHost.run_line("assert u'\\xe4'.encode('cp1252') == '\\xe4'") == "", "codec 注册表可用")
_ck(Metin2PythonHost.run_line("import sys; assert sys.path") == "", "sys.path 非空")
if has_pack:
# RunMainScriptinitpack() + __DEBUG__ + __COMMAND_LINE__ + RunFile("system.py")。
var err: String = Metin2PythonHost.run_main_script("")
print("system.py -> %s" % err)
_ck(err.contains(EXPECTED_STOP),
"system.py 停在预期的 '%s'2V0 之后这里会往前走)" % EXPECTED_STOP)
else:
print("SKIP: system.py 这一段(没有 40250 Client/pack%s" % AssetRoot.client_path())
Metin2PythonHost.stop()
_ck(not Metin2PythonHost.is_running(), "stop() 之后 is_running() 为假")
return fail