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

89 lines
4.3 KiB
Markdown
Raw Permalink 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.
# oracle 运行手册(Oracle-Lite
`oracle.c` 只用 `granny2` 的公开 C API`granny.h`):文件 IO + 数学 + 写二进制,**不碰 GPU / D3D / EterGrnLib**。
所以除了原生 WindowsWine 也能跑。
## 依赖
- `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
```bat
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
```sh
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` 里的 `__int64``build-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/*.cpp``winxx_granny_windows.cpp``WINDOWS_IGNORE_PACKING_MISMATCH`)。
`wii/``wiiu/``ps3/` 目录里有 `ansi_*` 的通用实现可参考。真要做是几小时的平台层移植,
不在关键路径 —— 先走 A / B。
## 对拍(`tools/oracle_diff`
```sh
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 2**mesh 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.c` 的 `GrannyBuildWorldPose` 要把 model 的 `InitialPlacement` 当 `Offset4x4` 传
> `GrannyGetModelInitialPlacement4x4`),否则非单位 InitialPlacement 的模型(如 assassin)会差一个平移。
> libgr2 的 `skeleton.initial_placement` 做同一件事。踩过一次。
## 产出
- dump → `test/oracle_dumps/`(布局见 `FORMAT.md`
- 噪声地板 → `test/noise_floor.json``oracle dump` 跑同 `(model,anim,t)` 100 次 + 换一版
相邻 DLL 一次,取矩阵 / 顶点的 `max|Δ|`M2 的 ε = 这个 ×10