Files
shenandClaude Sonnet 5 47baf6c0c6 Metin2 game client (P0–P11) + mobile asset pipeline
Networked client on the existing Godot 4.7 + libgr2 renderer:
- net: m2dev wire protocol (libsodium KX + XChaCha20), auth/select/game
  phases, EntityStore world model, ~all GC/CG headers. char create/delete,
  private shop / mall / cube, SHOP_GC_START_EX, guild, party (+ CG_PARTY_SET_STATE),
  quests, dragon soul, refine, safebox, exchange.
- UI: in-game windows migrated 1:1 from the reference uiscript/root .py —
  char status (/stat), inventory+equipment, select-item ([SELECT_ITEM] quest
  token), system-option + game-option + ESC system menu, private-shop 39-grid,
  party info board, shop tabs, atlas, minimap, quickbar, chat, …
- EterGrnLib polish: GR2 material blend/two-sided, LOD crossfade, motion-event
  dispatch, contact shadow, ray-AABB picking, weapon grip pre-transform.

Portable asset IO (A1) — all extension/libgr2/formats/mtproto reads routed
through godot::FileAccess (res:// PCK works on iOS/Android); standalone-lib
*_path() kept for the non-Godot CTests. AssetResolver + PropertyRegistry
switched to a baked index (bake_asset_index.gd) instead of std::filesystem.

Mobile builds: build-{android,ios}.sh, export-android.sh, pack-assets.sh,
gen-debug-keystore.sh. Assets ship as a zip mounted at runtime by
project/asset_pack.gd (adb push now; HTTP download is a drop-in later).

ctest 10/10, 34 GDScript suites, macOS/iOS/Android all build.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EJxkHiNKS4kybHS3XKyAJ
2026-08-31 20:02:12 +09:00

4.3 KiB
Raw Permalink Blame History

oracle 运行手册(Oracle-Lite

oracle.c 只用 granny2 的公开 C APIgranny.h):文件 IO + 数学 + 写二进制,不碰 GPU / D3D / EterGrnLib。 所以除了原生 Windows,Wine 也能跑。

依赖

  • granny.h + granny2_x64.lib + granny2_x64.dll —— 泄露 SDK 里就有: MobileSource/Cross Platform/Granny-3D-SDK-main/{include,lib/win64}/ 32 位版在 lib/win32/。)
  • 这套 DLL 是 SDK 2.9.12。资产由 2.4.0.7 导出(M0 T2 从解压后的 gr2 里读到)。 容器格式 + 组合公式(BuildCompositeTransform4x4 / ColumnMatrixMultiply4x3 / B 样条系数) 在 2.42.11 稳定,libgr2 的 self-check 6.1e-5 已印证。差异风险在 B 样条边界 / 少数曲线细节。 → 先跑 selfcheck(下)确认坐标约定和 t=0 == bind pose,再决定是否换 2.4.x runtime(见 GRANNY-VERSION.md)。

A. 原生 WindowsMSVC

cmake -S oracle -B oracle\build -DGRANNY_SDK="C:\path\to\Granny-3D-SDK-main"
cmake --build oracle\build --config Release
oracle\build\Release\oracle.exe probe "assets\PC\ymir work\pc\warrior\warrior_cheongrin.gr2"

B. macOS + Wine —— 已验证(macOS 26.4 / Apple Silicon

brew install mingw-w64
# wine-stable cask 需要 sudo 装 gstreamer 依赖;无 sudo 时直接解包用:
tar -xf "$(ls ~/Library/Caches/Homebrew/downloads/*wine-stable*.tar.xz)" -C /tmp/wine
xattr -rc "/tmp/wine/Wine Stable.app"
WINE="/tmp/wine/Wine Stable.app/Contents/Resources/wine/bin/wine"

# 编译(脚本封装了 MinGW 的坑:granny.h 的 __int64 关键字)
oracle/build-wine.sh          # → oracle/oracle.exe + oracle/granny2_x64.dll

# ⚠ WINEPREFIX 不能在 /tmpWine 拒绝在非属主目录建 prefix)
export WINEPREFIX="$HOME/.wine-oracle" WINEDEBUG=-all
"$WINE" wineboot --init       # 首次,Rosetta 下 38 分钟

cd oracle
GR2='Z:\Users\me\...\assets\PC\ymir work\pc\warrior\warrior_cheongrin.gr2'
"$WINE" oracle.exe probe "$GR2"
"$WINE" oracle.exe dump "$GR2" - 0.0 \
    -o 'Z:\Users\me\...\xrender-poc\test\oracle_dumps\cheongrin_bind_t0.bin' --mesh 4
"$WINE" oracle.exe dump "$GR2" 'Z:\...\action\dance_1.gr2' 11.333 \
    -o 'Z:\...\test\oracle_dumps\cheongrin_dance1_t11.333.bin' --mesh 4

坑:① MinGW GCC 不认 granny.h 里的 __int64build-wine.sh-D__int64="long long" ② Wine prefix 不能放 /tmp(非属主);③ wine-stable 首启会刷一大堆 MoltenVK 日志到 stderr,无害,WINEDEBUG=-all 挡不住它,grep 掉即可。

C.stretch)从 SDK source/ 移植 POSIX libgranny

Granny-3D-SDK-main/source/ 有全套源码(62k 行)+ CMakeLists,但重度 Windows 化 winxx/win64/*.cppwinxx_granny_windows.cppWINDOWS_IGNORE_PACKING_MISMATCH)。 wii/wiiu/ps3/ 目录里有 ansi_* 的通用实现可参考。真要做是几小时的平台层移植, 不在关键路径 —— 先走 A / B。

对拍(tools/oracle_diff

build/tools/oracle_diff <model.gr2> <anim.gr2|-> <dump.bin> [--t <sec>] [--mesh N]

libgr2 侧用 gr2::sample_pose / bind_pose + 手写 LBS 算一遍,报:

  • gate 1:骨骼世界矩阵 vs oracle 层① max|Δ|
  • gate 2mesh N 蒙皮顶点 vs oracle max‖Δ‖
  • verdict:阈值 1e-3noise_floor 就绪前)

已跑结果Granny 2.9.12.0 vs libgr2):

用例 mat max|Δ| vtx max‖Δ‖
warrior_cheongrin bind 1.5e-5 4.6e-5
warrior_cheongrin + dance_1 @ t=0/4.7/11.333/20 ≤ 4.1e-5 ≤ 6.5e-5
assassin bind(双 root + 非单位 InitialPlacement 3.1e-5 4.6e-5

全部 ≈ float32 累积误差 → libgr2 的类型树 / 世界姿势 / OldCurve 求值 / CPU LBS 与真 Granny 数值一致

关键:oracle.cGrannyBuildWorldPose 要把 model 的 InitialPlacementOffset4x4GrannyGetModelInitialPlacement4x4),否则非单位 InitialPlacement 的模型(如 assassin)会差一个平移。 libgr2 的 skeleton.initial_placement 做同一件事。踩过一次。

产出

  • dump → test/oracle_dumps/(布局见 FORMAT.md
  • 噪声地板 → test/noise_floor.jsonoracle dump 跑同 (model,anim,t) 100 次 + 换一版 相邻 DLL 一次,取矩阵 / 顶点的 max|Δ|M2 的 ε = 这个 ×10