Files
mtgodot-poc/extension/CMakeLists.txt
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

243 lines
11 KiB
CMake

# mtgodot GDExtension — registers Metin2Model / Metin2AnimPlayer, links libgr2.
# libgr2 is added at the top-level CMakeLists (vendored under ../libgr2), so here
# we just consume the xrender::libgr2 target.
# --- godot-cpp (submodule pinned at 101ae38034304346a46ea9ea84ae156d3e860496) ---
# The parent gitlink is the source-of-truth; .gitmodules intentionally has no
# moving branch. The selected bundle targets Godot 4.7's extension API.
set(GODOTCPP_API_VERSION "4.7" CACHE STRING "Target Godot API version" FORCE)
add_subdirectory(godot-cpp)
# --- vendored native deps: libsodium / libzstd / miniLZO ---
# Built from source (pinned submodules + vendored miniLZO) so the same tree
# compiles on the Android NDK and iOS SDK, which have no Homebrew. Exposes the
# aliases mt3p::sodium / mt3p::zstd / mt3p::minilzo. See docs/THIRD-PARTY.md.
add_subdirectory(third_party)
# --- mtnet: Metin2 net protocol core (no godot-cpp dep; libsodium only) ---
add_library(mtnet STATIC
src/net/text_codec.cpp
src/net/secure_cipher.cpp
src/net/net_stream.cpp
src/net/entity_store.cpp
src/net/mark_image.cpp
src/net/classic/classic_stream.cpp # MT_PROTOCOL=classic (40250)
src/net/classic/classic_parser.cpp
src/net/classic/classic_session.cpp
src/net/classic/classic_cipher.cpp
)
target_include_directories(mtnet PUBLIC src/net)
target_link_libraries(mtnet PUBLIC mt3p::sodium mt3p::minilzo mt3p::cryptopp)
# macOS ships iconv as a system library rather than libc. Linux/glibc and
# Windows use the platform implementation without this extra link item.
find_library(MT_ICONV_LIBRARY NAMES iconv)
if(MT_ICONV_LIBRARY)
target_link_libraries(mtnet PUBLIC ${MT_ICONV_LIBRARY})
endif()
target_compile_features(mtnet PUBLIC cxx_std_20)
# §1.5: non-EUROPE CG_CLIENT_VERSION timestamp, in C __TIMESTAMP__ form
# ("Www Mmm dd hh:mm:ss yyyy"); regenerated on each CMake configure.
string(TIMESTAMP MT_BUILD_TS "%a %b %d %H:%M:%S %Y")
target_compile_definitions(mtnet PRIVATE MT_BUILD_TIMESTAMP="${MT_BUILD_TS}")
# --- mtpack: m2dev-fork asset pack reader/writer (libsodium + zstd) ---
add_library(mtpack STATIC
src/pack/eterpack.cpp
src/pack/pack_mount.cpp
src/pack/asset_source.cpp
)
target_include_directories(mtpack PUBLIC src/pack)
target_link_libraries(mtpack PUBLIC mt3p::sodium mt3p::zstd xrender::formats)
target_compile_features(mtpack PUBLIC cxx_std_20)
# --- mtproto: item_proto / mob_proto reader, 40250 format (CLZO/TEA from port_platform) ---
add_library(mtproto STATIC src/proto/proto.cpp)
target_include_directories(mtproto PUBLIC src/proto)
target_link_libraries(mtproto PUBLIC port_platform)
target_compile_features(mtproto PUBLIC cxx_std_20)
# --- port_logic + port_platform: 40250 mirror (extension/src/port) and its platform layer
# (extension/src/platform), docs/PORT-PLAN.md ---
add_subdirectory(src/port)
# Host-only CLIs + tests: they run on the build machine, so skip them entirely
# when cross-compiling the extension for a device.
set(MT_HOST_BUILD FALSE)
if(CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME)
set(MT_HOST_BUILD TRUE)
endif()
if(MT_HOST_BUILD)
add_executable(packtool tools/packtool.cpp)
target_link_libraries(packtool PRIVATE mtpack)
# net_probe: connect to an auth server, run the handshake + CG_LOGIN3, report.
add_executable(net_probe tools/net_probe.cpp)
target_link_libraries(net_probe PRIVATE mtnet)
# net_e2e: full auth -> game -> char list -> select -> PHASE_GAME against a
# real server; dumps entities / points / inventory. Live integration check.
add_executable(net_e2e tools/net_e2e.cpp)
target_link_libraries(net_e2e PRIVATE mtnet)
# net_classic_e2e: live 40250 classic handshake -> login -> char select -> PHASE_GAME probe.
add_executable(net_classic_e2e tools/net_classic_e2e.cpp)
target_link_libraries(net_classic_e2e PRIVATE mtnet)
endif()
if(BUILD_TESTING AND MT_HOST_BUILD)
add_executable(net_cipher_test tests/net_cipher_test.cpp)
target_link_libraries(net_cipher_test PRIVATE mtnet)
add_test(NAME net.cipher_roundtrip COMMAND $<TARGET_FILE:net_cipher_test>)
add_executable(net_loopback_test tests/net_loopback_test.cpp)
target_link_libraries(net_loopback_test PRIVATE mtnet)
add_test(NAME net.loopback_flow COMMAND $<TARGET_FILE:net_loopback_test>)
add_executable(net_entity_test tests/net_entity_test.cpp)
target_link_libraries(net_entity_test PRIVATE mtnet)
add_test(NAME net.entity_store COMMAND $<TARGET_FILE:net_entity_test>)
add_executable(net_state_queue_test tests/net_state_queue_test.cpp)
target_link_libraries(net_state_queue_test PRIVATE mtnet)
add_test(NAME net.state_queue COMMAND $<TARGET_FILE:net_state_queue_test>)
# 40250 "classic" backend (MT_PROTOCOL=classic).
add_executable(net_classic_wire_test tests/net_classic_wire_test.cpp)
target_include_directories(net_classic_wire_test PRIVATE src/net)
target_compile_features(net_classic_wire_test PRIVATE cxx_std_20)
add_test(NAME net.classic_wire COMMAND $<TARGET_FILE:net_classic_wire_test>)
add_executable(net_classic_stream_test tests/net_classic_stream_test.cpp)
target_link_libraries(net_classic_stream_test PRIVATE mtnet)
add_test(NAME net.classic_stream COMMAND $<TARGET_FILE:net_classic_stream_test>)
add_executable(net_classic_session_test tests/net_classic_session_test.cpp)
target_link_libraries(net_classic_session_test PRIVATE mtnet)
add_test(NAME net.classic_session COMMAND $<TARGET_FILE:net_classic_session_test>)
add_executable(net_classic_cipher_test tests/net_classic_cipher_test.cpp)
target_link_libraries(net_classic_cipher_test PRIVATE mtnet)
add_test(NAME net.classic_cipher COMMAND $<TARGET_FILE:net_classic_cipher_test>)
add_executable(net_classic_encstream_test tests/net_classic_encstream_test.cpp)
target_link_libraries(net_classic_encstream_test PRIVATE mtnet)
add_test(NAME net.classic_encstream COMMAND $<TARGET_FILE:net_classic_encstream_test>)
add_executable(net_classic_mark_test tests/net_classic_mark_test.cpp)
target_link_libraries(net_classic_mark_test PRIVATE mtnet)
add_test(NAME net.classic_mark COMMAND $<TARGET_FILE:net_classic_mark_test>)
add_executable(net_text_codec_test tests/net_text_codec_test.cpp)
target_link_libraries(net_text_codec_test PRIVATE mtnet)
add_test(NAME net.text_codec COMMAND $<TARGET_FILE:net_text_codec_test>)
add_executable(net_bounds_test tests/net_bounds_test.cpp)
target_link_libraries(net_bounds_test PRIVATE mtnet)
add_test(NAME net.bounds COMMAND $<TARGET_FILE:net_bounds_test>)
add_executable(net_mark_test tests/net_mark_test.cpp)
target_link_libraries(net_mark_test PRIVATE mtnet mt3p::minilzo)
add_test(NAME net.guild_mark COMMAND $<TARGET_FILE:net_mark_test>)
add_executable(pack_roundtrip_test tests/pack_roundtrip_test.cpp)
target_link_libraries(pack_roundtrip_test PRIVATE mtpack)
add_test(NAME pack.roundtrip COMMAND $<TARGET_FILE:pack_roundtrip_test>)
add_executable(proto_test tests/proto_test.cpp)
target_link_libraries(proto_test PRIVATE mtproto)
# MT_40250_CLIENT comes from src/port/CMakeLists.txt; skipped (77) without the client, failed under
# MT_ASSETS_STRICT=1.
add_test(NAME proto.item_mob COMMAND $<TARGET_FILE:proto_test> ${MT_40250_CLIENT})
set_tests_properties(proto.item_mob PROPERTIES SKIP_RETURN_CODE 77)
add_executable(proto_item_layout_test tests/proto_item_layout_test.cpp)
target_link_libraries(proto_item_layout_test PRIVATE mtproto)
add_test(NAME proto.item_layout COMMAND $<TARGET_FILE:proto_item_layout_test>)
# The embedded interpreter, when third_party built it (-DMTGODOT_EMBED_PYTHON=ON).
if(TARGET mtpython)
add_executable(py_embed_test tests/py_embed_test.cpp)
target_link_libraries(py_embed_test PRIVATE mtpython)
add_dependencies(py_embed_test mtpython_stdlib)
# The stdlib exactly as the host ships it: python27.zip, read through zipimport (批次 2P step 3).
add_test(NAME python.embed COMMAND $<TARGET_FILE:py_embed_test> "${MT_PYTHON_STDLIB_ZIP}")
endif()
endif()
# --- the extension library ---
# macOS/Android: SHARED (Godot dlopen()s it). iOS: STATIC — the platform forbids
# loading dynamic libraries, so the archive is linked into the app at export time
# (see docs/PLATFORMS.md for the remaining F1 export/sign steps).
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(MT_LIB_KIND STATIC)
set(MT_PLAT_TAG "ios")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
set(MT_LIB_KIND SHARED)
set(MT_PLAT_TAG "android")
else()
set(MT_LIB_KIND SHARED)
set(MT_PLAT_TAG "macos")
endif()
add_library(mtgodot ${MT_LIB_KIND}
src/register_types.cpp
src/metin2_model.cpp
src/metin2_anim.cpp
src/metin2_world.cpp
src/terrain_splat.cpp
src/static_object.cpp
src/tree_placeholder.cpp
src/environment_builder.cpp
src/water_builder.cpp
src/gr2_bridge.cpp
src/m2_material.cpp
src/dxt.cpp
src/asset_io.cpp
src/texture_util.cpp
src/net/m2_client.cpp
src/proto/proto_node.cpp
src/pack40250_node.cpp
src/python_stdlib.cpp
src/python_host_node.cpp
)
target_compile_features(mtgodot PRIVATE cxx_std_20)
target_link_libraries(mtgodot PRIVATE godot::cpp xrender::libgr2 xrender::formats mtnet mtproto port_platform)
# The embedded interpreter's standard library (批次 2P step 3c). python27.zip and its digest are
# copied next to project.godot so the exporter packs them into the APK/IPA (the export presets'
# include_filter lists them); src/python_stdlib.cpp stages them out of the PCK at runtime.
if(TARGET mtpython_stdlib)
target_compile_definitions(mtgodot PRIVATE MTGODOT_HAVE_PYTHON)
# A target rather than a custom command on the two files: the command that produces them lives in
# third_party/, and a Makefile generator only resolves a generated-file dependency inside the
# directory that declared it. copy_if_different keeps the no-op build a compare.
add_custom_target(mtpython_stdlib_project ALL
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${MT_PYTHON_STDLIB_ZIP}" "${CMAKE_SOURCE_DIR}/project/python27.zip"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${MT_PYTHON_STDLIB_SHA}" "${CMAKE_SOURCE_DIR}/project/python27.zip.sha256"
COMMENT "Copying python27.zip into project/ (res://)")
add_dependencies(mtpython_stdlib_project mtpython_stdlib)
add_dependencies(mtgodot mtpython_stdlib_project)
endif()
# Godot's .gdextension expects, per platform:
# project/bin/libmtgodot.macos.template_{debug,release}.dylib
# project/bin/libmtgodot.ios.template_{debug,release}.a
# project/bin/libmtgodot.android.template_{debug,release}.<arch>.so
set(MT_CFG_TAG "$<IF:$<CONFIG:Release>,template_release,template_debug>")
if(CMAKE_SYSTEM_NAME STREQUAL "Android")
# godot_arch_name-style suffix; OnePlus 13 = arm64.
set(MT_ARCH_SUFFIX ".${CMAKE_ANDROID_ARCH_ABI}")
string(REPLACE "arm64-v8a" "arm64" MT_ARCH_SUFFIX "${MT_ARCH_SUFFIX}")
else()
set(MT_ARCH_SUFFIX "")
endif()
set_target_properties(mtgodot PROPERTIES
PREFIX "lib"
OUTPUT_NAME "mtgodot.${MT_PLAT_TAG}.${MT_CFG_TAG}${MT_ARCH_SUFFIX}"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/project/bin"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/project/bin"
)