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
65 lines
3.2 KiB
C++
65 lines
3.2 KiB
C++
// AssetResolver —— 虚拟路径(`d:\ymir work\...`)-> 真实散文件路径。
|
||
// SHINSOO §4.2 / §9-W0 / BACKLOG G7。无 godot 依赖(放 formats/ 以便 CTest;
|
||
// SHINSOO §7.1 原列在 extension/src,此处按「解析层不依赖 godot」的原则前移)。
|
||
//
|
||
// 散文件按原 eterpack 名散在多个一级目录(Zone/ Terrain/ ETC/ PC/ Tree/ Property/
|
||
// metin2_patch_*/ ...),每个下面有 `ymir work/`。所以按「ymir work/ 之后的后缀」建索引,
|
||
// 而不是假设 `assets/ymir work/...`。
|
||
#pragma once
|
||
#include <map>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
namespace fmt {
|
||
|
||
struct AssetResolver {
|
||
struct Entry {
|
||
std::string real_path; // 采用的真实路径,**相对 assets_root**(resolve 时拼回)
|
||
std::vector<std::string> all_candidates; // 全部命中(开发模式查 patch 覆盖;load_index 后为空)
|
||
};
|
||
|
||
// key = 规范化后的索引键(小写 / '/')。两套:
|
||
// by_ymir : "ymir work/..." 之后的后缀 (虚拟路径主用)
|
||
// by_rel : assets 根起的相对路径 (地图数据等直接相对寻址)
|
||
std::map<std::string, Entry> by_ymir;
|
||
std::map<std::string, Entry> by_rel;
|
||
|
||
int files_indexed = 0;
|
||
int ymir_conflicts = 0; // 同 key 多个真实文件
|
||
std::vector<std::string> conflict_keys; // 去重后的冲突 key(前若干个)
|
||
std::string assets_root;
|
||
|
||
// pack 优先级:越靠前越优先(先注册者胜)。默认基础包在前、patch 包在后。
|
||
// 未列出的一级目录按字母序排在「基础」与「patch」之间。
|
||
bool build(const std::string& assets_root,
|
||
const std::vector<std::string>& pack_priority = default_priority(),
|
||
std::string* err = nullptr);
|
||
|
||
// 烘焙 / 装载索引(PCK 里没法 std::filesystem 扫目录)。
|
||
// save_index() -> 一段文本(行式,路径相对 assets_root)
|
||
// load_index() <- 该文本 + 运行时 assets_root(resolve 拼回真实路径)
|
||
std::string save_index() const;
|
||
bool load_index(const std::string& assets_root, const std::string& text,
|
||
std::string* err = nullptr);
|
||
|
||
// assets_root 下有 asset_index.txt 就 load,否则 build() 扫盘。
|
||
// 读文件走 fmt::read_file(host 可注入 FileAccess -> res:// 可用)。
|
||
bool build_or_load(const std::string& assets_root,
|
||
const std::vector<std::string>& pack_priority = default_priority(),
|
||
std::string* err = nullptr);
|
||
|
||
// 规范化:'\'->'/',去盘符,去开头 '/',小写,折叠 '//',处理 '.'/'..'。
|
||
static std::string normalize(const std::string& virtual_path);
|
||
|
||
// 解析。成功返回真实路径;失败返回 "" 且(可选)写 reason。
|
||
std::string resolve(const std::string& virtual_path, std::string* reason = nullptr) const;
|
||
bool exists(const std::string& virtual_path) const;
|
||
|
||
// 所有已索引文件的相对路径(by_rel 的 real_path 去重)。PropertyRegistry::scan_list 用。
|
||
std::vector<std::string> all_rel() const;
|
||
|
||
static std::vector<std::string> default_priority();
|
||
};
|
||
|
||
} // namespace fmt
|