#pragma once // Starting the script layer inside a host process (docs/PORT-PLAN.md 批次 2P step 4). // // 40250 runs the interpreter on Main()'s stack (UserInterface/UserInterface.cpp:419-434): construct // CPythonLauncher, Create(), RunMainScript(), Clear(), all inside one WinMain. Godot owns the process // here and calls in from GDScript, so the launcher has to outlive the call that starts it — that split // (Start / RunMainScript / Stop) is the only difference from the original control flow. // // It lives under platform/ScriptLib rather than platform/UserInterface because it is only buildable // with the embedded interpreter, and that is the directory both port_logic and port_platform drop when // `mtpython` is absent (extension/src/port/CMakeLists.txt). // // godot-free, like everything else under platform/: extension/src/python_host_node.cpp is what GDScript // talks to. #include namespace PythonBoot { // PythonHost::Configure() -> CPythonLauncher -> Create() -> PythonHost::InstallStdLib(stdlib_path). // An empty stdlib_path means PythonHost::DefaultStdLibPath(). Starting twice is a no-op that succeeds. // On failure *error is set and the interpreter is shut down again, so the next attempt starts clean. // The packs must already be registered (platform/PackBackend.h), as 40250 registers them in Main() // before the launcher exists. bool Start(const char* stdlib_path, std::string* error); bool IsRunning(); // 40250: RunMainScript(CPythonLauncher&, const char*) — the module initializers, __DEBUG__, // __COMMAND_LINE__ and RunFile("system.py"). 2V0-e registers temporary observable gameplay modules; // they are replaced by the real modules in later 2V slices. // False leaves *error set to what the launcher reported through LogBox (platform/EterBase/LogBox.h). // // PORT: system.py runs on a script thread. RunMainScript returns once prototype.RunApp() has reached // app.Loop() (IsAppLooping) or the script has ended; from then on every AppFrame() runs exactly one // CPythonApplication::Process() while the host thread waits, so the two never run concurrently. bool RunMainScript(const char* lpCmdLine, std::string* error); bool IsAppLooping(); bool AppFrame(); // Native CPythonBackground map selection, in the local coordinate frame of RenderGame. std::string CurrentMapName(); // Used by the platform CPythonApplication: Loop() calls AppLoopWait() before each Process() and stops // when it returns false; Exit()/Abort() call AppRequestQuit() (40250: PostQuitMessage). bool AppLoopWait(); void AppRequestQuit(); // CPythonLauncher::RunLine, with the reported text handed back rather than only shown. The host's own // check uses it to prove the interpreter and the standard library work in-process on a device that has // no 40250 packs, which is where RunMainScript cannot run at all. bool RunLine(const char* source, std::string* error); // str(expression) evaluated in __main__ (a host/test helper; 40250 has no counterpart). bool Evaluate(const char* expression, std::string* result, std::string* error); // Forward physical Godot input to the original 40250 window manager. Coordinates // are viewport pixels; SetUISize also establishes its logical resolution. void SetUISize(int width, int height); void UIMouseMove(int x, int y); void UIMouseButton(int button, bool pressed, int x, int y); void UIKey(int key, bool pressed); // WM_CHAR (a Unicode code point) and WM_KEYDOWN (a Win32 VK code) for CPythonIME and OnIMEKeyDown. void UIChar(unsigned codepoint); void UIIMEKeyDown(int vkey); // While IsAppLooping(), UIUpdate is AppFrame() and UIRender keeps the commands that Process() drew. void UIUpdate(); void UIRender(); // 40250 Main() calls Clear() and lets the launcher leave scope. void Stop(); }