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>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
// See PythonBoot.h. The sequence is 40250 UserInterface.cpp:241-352 (RunMainScript) and 419-434 (Main),
|
||||
// split so the launcher can outlive the call that creates it.
|
||||
#include "ScriptLib/StdAfx.h"
|
||||
#include "ScriptLib/PythonLauncher.h"
|
||||
#include "UserInterface/StdAfx.h" // initpack and the rest of the module initializer list
|
||||
#include "PythonBoot.h"
|
||||
#include "PythonHost.h"
|
||||
#include "../EterBase/LogBox.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace
|
||||
{
|
||||
// 40250 has this on Main()'s stack. CPythonLauncher is a CSingleton, so there is still only ever one.
|
||||
std::unique_ptr<CPythonLauncher> g_launcher;
|
||||
|
||||
bool fail(std::string* error, const std::string& text)
|
||||
{
|
||||
if (error)
|
||||
*error = text;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace PythonBoot
|
||||
{
|
||||
|
||||
bool IsRunning()
|
||||
{
|
||||
return g_launcher != nullptr;
|
||||
}
|
||||
|
||||
bool Start(const char* stdlib_path, std::string* error)
|
||||
{
|
||||
if (g_launcher)
|
||||
return true;
|
||||
|
||||
const std::string stdlib = (stdlib_path && *stdlib_path) ? stdlib_path : PythonHost::DefaultStdLibPath();
|
||||
if (stdlib.empty())
|
||||
return fail(error, "no Python standard library path (Metin2Python.stdlib_path / $MT_PYTHON_STDLIB)");
|
||||
|
||||
// The flags have to be set before the interpreter starts, i.e. before the constructor below.
|
||||
PythonHost::Configure();
|
||||
|
||||
g_launcher = std::make_unique<CPythonLauncher>(); // Py_Initialize
|
||||
if (!g_launcher->Create())
|
||||
{
|
||||
Stop();
|
||||
return fail(error, "CPythonLauncher::Create failed");
|
||||
}
|
||||
|
||||
std::string stdlib_error;
|
||||
if (!PythonHost::InstallStdLib(stdlib.c_str(), &stdlib_error))
|
||||
{
|
||||
Stop();
|
||||
return fail(error, "InstallStdLib(" + stdlib + "): " + stdlib_error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 40250: RunMainScript
|
||||
bool RunMainScript(const char* lpCmdLine, std::string* error)
|
||||
{
|
||||
if (!g_launcher)
|
||||
return fail(error, "PythonBoot::Start has not run");
|
||||
|
||||
CPythonLauncher& pyLauncher = *g_launcher;
|
||||
|
||||
// The module initializers, in 40250's order. Everything after initpack (initdbg, initime, initgrp,
|
||||
// initgrpImage, initgrpText, initwndMgr, initudp, initapp, ... initServerStateChecker) is a 2V slice:
|
||||
// system.py stops at the first import of one of them, today `app`.
|
||||
initpack();
|
||||
|
||||
ClearLogBoxMessage();
|
||||
|
||||
NANOBEGIN
|
||||
|
||||
// RegisterDebugFlag
|
||||
{
|
||||
std::string stRegisterDebugFlag;
|
||||
|
||||
#ifdef _DISTRIBUTE
|
||||
stRegisterDebugFlag ="__DEBUG__ = 0";
|
||||
#else
|
||||
stRegisterDebugFlag ="__DEBUG__ = 1";
|
||||
#endif
|
||||
|
||||
if (!pyLauncher.RunLine(stRegisterDebugFlag.c_str()))
|
||||
{
|
||||
TraceError("RegisterDebugFlag Error");
|
||||
return fail(error, "RegisterDebugFlag Error: " + LastLogBoxMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterCommandLine.
|
||||
// PORT: the "-cs"/"-ncs" auto-login argument is passed through unchanged. Decoding it needs
|
||||
// EterBase/Utils.cpp SplitLine and EterLib/Util.cpp base64_decode, which are declared but not ported
|
||||
// yet; they belong to the login track (批次 2V1) and nothing in this host passes -cs today.
|
||||
{
|
||||
std::string stRegisterCmdLine;
|
||||
|
||||
std::string stCmdLine = lpCmdLine ? lpCmdLine : "";
|
||||
|
||||
stRegisterCmdLine ="__COMMAND_LINE__ = ";
|
||||
stRegisterCmdLine+='"';
|
||||
stRegisterCmdLine+=stCmdLine;
|
||||
stRegisterCmdLine+='"';
|
||||
|
||||
const CHAR* c_szRegisterCmdLine=stRegisterCmdLine.c_str();
|
||||
if (!pyLauncher.RunLine(c_szRegisterCmdLine))
|
||||
{
|
||||
TraceError("RegisterCommandLine Error");
|
||||
return fail(error, "RegisterCommandLine Error: " + LastLogBoxMessage());
|
||||
}
|
||||
}
|
||||
// PORT: 40250 also honours "--pause-before-create-window" here with system("pause") and the XTrap
|
||||
// init check. Neither exists on this platform (no console to pause, no XTrap), so only the RunFile
|
||||
// is kept.
|
||||
{
|
||||
if (!pyLauncher.RunFile("system.py"))
|
||||
{
|
||||
TraceError("RunMain Error");
|
||||
return fail(error, "RunMain Error: " + LastLogBoxMessage());
|
||||
}
|
||||
}
|
||||
|
||||
NANOEND
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RunLine(const char* source, std::string* error)
|
||||
{
|
||||
if (!g_launcher)
|
||||
return fail(error, "PythonBoot::Start has not run");
|
||||
|
||||
ClearLogBoxMessage();
|
||||
if (!g_launcher->RunLine(source ? source : ""))
|
||||
return fail(error, LastLogBoxMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
if (!g_launcher)
|
||||
return;
|
||||
g_launcher->Clear(); // Main() calls Clear() explicitly before the launcher leaves scope
|
||||
g_launcher.reset();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user