40250 classic W2: EntityStore data-layer only + net_world visibility (增量 50)

CLIENT-GAP-FIX §2.1/§2.3/§2.4/§2.5 (+ §2.2 steps 4-5):

- §2.1 two-layer model: EntityStore is data-only. Entries are created only by
  mut_spawn / mut_spawn_full / mut_spawn_main and removed only by mut_despawn /
  main-vid table clear. Every other mut_* now does find-or-return with a
  MT_NET_TRACE-gated debug log; no pending / replay buffer. Visibility moves to
  net_world.gd::_update_visibility() — per frame it walks client.get_entities()
  and mirrors is_main / AFFECT_SHOW_ALWAYS(bit 7) / IsWall(14201-14204) /
  planar-distance < CHAR_STAGE_VIEW_BOUND (200*100 cm), building a node on entry
  and fading + freeing it past +10 while keeping the data row (_fading guard).
  Gate is fail-open when the data layer is not queryable (no get_entities /
  _main_vid == 0 / no pos_cm) so existing fake-client tests are unaffected.
- §2.3 one overwrite rule: mut_spawn_full() does a clean whole-row value replace
  (m_ents[vid] = e), no preservation of prior move state on VID reuse; mut_spawn()
  now delegates to it. net_world._on_spawn destroys + recreates a node when the
  vid already has one; mount/dismount rebuild keeps the old global_position.
- §2.4 points bounds: mut_set_points() clamps counts > POINT_MAX_NUM (255) and
  zeroes the tail of a short snapshot; mut_set_point() rejects type >= 255.
- §2.5 field apply order + affect flags: new EntityStore::mut_affect_flags(vid,
  lo, hi) assembles v = lo | (hi << 32) and queues a dirty tick; mut_char_update
  guild/alignment/pk changes now also push Info. net_world._apply_field_updates()
  applies fields in the reference call order (NetworkActorManager.cpp:470).
- §2.2 steps 4-5: mut_char_info() / mut_shop_sign() drop the touch()-creates-
  entity path. Steps 1-3 (m_pending_actor two-packet merge) remain W1-owned.

Tests: ./build.sh Debug exit 0; ctest 16/16 (net.entity_store gains a §2.1
data-layer assertion block). New project/net_world_vis_test.gd for scene-node
visibility. GDScript regressions green: netbridge_test, gamescene_test,
netplay_test, p9_test, combat_fx_test, player_motion_test, net_world_vis_test.

Follow-up for W1: call mut_affect_flags(vid, affect_flag[0], affect_flag[1])
from classic_parser.cpp after GC_CHARACTER_ADD[2] / GC_CHARACTER_UPDATE
(mut_char_update has no affect param). wire_classic.h already has the wanted
static_assert on GCPoints.points.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014yvAPqPivoY7vmBbzgqK4W
This commit is contained in:
shenlei
2026-09-02 16:06:06 +09:00
co-authored by Claude Sonnet 5
parent f32064c74a
commit 3487df2a16
8 changed files with 597 additions and 50 deletions
+22 -1
View File
@@ -57,7 +57,7 @@
|---|---|---|---|
| W0 公共基础 / 集成 | 🟡 | DirectEnter、Classic Warp endpoint、`world_reset`、场景重建首版已落地;增量 49 接口冻结批次(`Entity` 补字段 + `mut_spawn_full/mut_ownership/mut_map_bgm/drain_dirty``bgm_changed` 信号、`bgm_director.gd`、§8.6 键位收敛)已提交 | Phase 1 4 工作流并行完成后执行 G1 集成 |
| W1 协议 / 账号会话 | 🟡 | 角色槽地址透传、发送结果、unknown / unhandled 错误链路、断线回 LOGIN 已覆盖;`m_pending_actor` 暂存表成员已就位 | §1.5 真实版本包、§1.10 定长文本编码、§2.2 两包合并逻辑;真实双连接、enter delay |
| W2 实体 / 世界 / 战斗 | 🟡 | 地图实体 / 掉落清理、地图重建首版及离线回归已覆盖;`drain_dirty` / `mut_spawn_full` 骨架可用 | §2.1 两层模型 + 可见性、§2.3 `mut_spawnmut_spawn_full`、§2.4 点数数组尾部、§2.5 应用顺序 + affect_flags |
| W2 实体 / 世界 / 战斗 | 🟡 | §2.1 两层模型(`EntityStore` 纯数据层 + `net_world.gd` 每帧 `_update_visibility()``CHAR_STAGE_VIEW_BOUND` / `AFFECT_SHOW_ALWAYS` / `IsWall` 建 / 淡出节点)、§2.3`mut_spawn``mut_spawn_full` 整行替换 + 节点销毁重建 + 上下坐骑保坐标)、§2.4点数快照截断 / 尾部清零 + 单索引边界)、§2.5(`mut_affect_flags` 两段拼装 + 参考调用序节点刷新)已落地并回归(增量 50) | 远服 40250 端到端可见性 / VID 复用 / affect 表现验收;W1 在 `ClassicParser` 接线 `mut_affect_flags()``state_flags` 表现层枚举与眩晕 / 隐身等表现 |
| W3 玩法 UI / 输入 | 🟡 | 多数窗口、背包 / 任务 / 社交、键位和输入已有首版;§8.6 数字键 / F1–F4 / Ctrl+数字入口已收敛 | §8.3 帮助窗;§8.6 剩余逐行键位与表情包 |
| W4 渲染 / 资产 / 音频 | 🟡 | GR2 / `.msa` / `.mse`、地图 / 水体 / 天气 / BGM 部分链路已有;`bgm_director.gd` 消费点已就位 | §9.1 地图 BGM 曲目解析 + 音量、§9.2 后台恢复曲目、§9.3 生命周期单一属主 |
| W5 打包 / 发布 / 验收 | ⬜ | 已有 Godot 导出配置和构建入口 | 自包含资源包、Intel / Apple Silicon、签名和 Mac App 验收 |
@@ -115,6 +115,27 @@
> `net_classic_session_test`、`gamescene_test`、`p9_test`、`p2b_test`、`netbridge_test`、
> `p8_test` 已通过;受限环境下 localhost TCP 目标测试仍自动 skip。真实 40250 换图 / 第二次
> 连接 E2E、地图 BGM 选择和 `MT_CLASSIC_ENTER_DELAY` 仍待完成。
> - **2026-09-02 增量 50Phase 1 W2:实体两层模型 + 可见性 / 单次覆盖 / 点数边界 / 应用顺序)**:
> 把可见性从 `EntityStore` 彻底移到 `net_world.gd`。**§2.1**`EntityStore` 只做数据层——
> 仅 `mut_spawn / mut_spawn_full / mut_spawn_main` 建行,`mut_mount / mut_char_info /
> mut_shop_sign` 遇未知 VID 只 `dbg_ignored()``MT_NET_TRACE` 门控)后返回,不再 `touch()`
> 出半行、不做 replay 缓冲;`net_world.gd` 每帧 `_update_visibility()` 按
> `is_main / AFFECT_SHOW_ALWAYS(bit7) / IsWall(1420114204) / 平面距主角 <
> CHAR_STAGE_VIEW_BOUND(200*100 cm)` 决定建节点,超 `+10` 淡出并保留数据行(`_fading` 集合
> 防淡出中重建),只在主角坐标已知时启用、否则沿用 `entity_spawned` 建节点。**§2.3**
> `mut_spawn()` 改为委托 `mut_spawn_full()`,后者整行值替换(`m_ents[vid] = e`,不再保留旧
> move 状态,对齐 `SNetworkActorData::__copy__`);`net_world._on_spawn` 对已有节点的 vid
> 先销毁再重建,上/下坐骑触发的重建复用旧世界坐标。**§2.4**`mut_set_points()` 全量快照
> `> POINT_MAX_NUM(255)` 截断、`< 255` 清零尾部;`mut_set_point()` 索引 `>= 255` 拒绝。
> **§2.5**:新增 `EntityStore::mut_affect_flags(vid, lo, hi)``v = lo | (hi<<32)` 两段拼装),
> `mut_char_update()` 的 guild/alignment/pk 变化也触发 Info`net_world._apply_field_updates()`
> 按参考调用序(`NetworkActorManager.cpp:470`Armor→Weapon→Hair→Guild→AffectFlag→MoveSpeed→
> AttackSpeed→Alignment→PKMode→StateFlags)刷新节点。C++ `ctest` 16/16`net.entity_store`
> 加 §2.1 数据层断言)、新增 `net_world_vis_test.gd`、`netbridge_test` / `gamescene_test` /
> `netplay_test` / `p9_test` / `combat_fx_test` / `player_motion_test` 全绿。待 W1 跟进:
> `wire_classic.h` 的 `GCPoints` 已有 `static_assert(sizeof == 1 + 255*4)``ClassicParser`
> 需在 `GC_CHARACTER_ADD[2]` / `GC_CHARACTER_UPDATE` 后调用 `mut_affect_flags()` 把
> `affect_flag[2]` 送进实体(`mut_char_update` 无 affect 形参,未接线)。
> - **2026-09-02 增量 49W0 接口冻结批次)**:为 Phase 1 的 4 个并行工作流一次性落地跨工作流
> 共享文件的骨架,冻结接口后再拆分。`EntityStore``Entity` 补 `empire / affect_flags /
> owner_vid / state_flags` 字段;新增 `mut_spawn_full()`(§2.3 两包合并的单次落地入口)、