Files
Genarrative/docs/technical/M4_MODULE_NPC_SPACETIMEDB_BASELINE_2026-04-21.md
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

8.0 KiB
Raw Permalink Blame History

M4 module-npc SpacetimeDB 基座记录2026-04-21

更新时间:2026-04-21

0. 文档目标

本文件只记录一件事:

module-npc 从“只有占位 README”推进到“已有可编译 Rust 领域 contract并接入 SpacetimeDB 最小 npc_state 真相表与社交动作 reducer/procedure”的真实落地结果。

本轮只做最小基座,不扩到完整 npc_trade / npc_gift / npc_recruit 的全链结算迁移,也不改前端 UI。


1. 本轮落地范围

本轮只落实下面 6 件事:

  1. 新增 server-rs/crates/module-npc/ 真实 crate而不是继续停留在目录占位。
  2. module-npc 中冻结 relation_state / stance_profile / npc_state 的首版领域类型与校验 helper。
  3. module-npc 中补齐 build_initial_stance_profilenormalize_npc_state_snapshotapply_npc_social_action 等最小规则原语。
  4. server-rs/crates/spacetime-module/ 中新增 npc_state 表。
  5. spacetime-module 中新增 upsert_npc_stateresolve_npc_social_action 及对应 procedure形成最小可编译写入口。
  6. module-npc 中新增 resolve_npc_interaction 的首版领域 contract并在 spacetime-module 中补对应 reducer / procedure。

2. 本轮新增的真实工程落点

2.1 新增 crate

  1. server-rs/crates/module-npc/Cargo.toml
  2. server-rs/crates/module-npc/src/lib.rs

2.2 workspace 与主工程聚合

  1. server-rs/Cargo.toml
    • 已把 crates/module-npc 纳入 workspace members
  2. server-rs/crates/spacetime-module/Cargo.toml
    • 已接入 module-npc 依赖
  3. server-rs/crates/spacetime-module/src/lib.rs
    • 已接入 module-npc 类型
    • 已新增 npc_state
    • 已新增 upsert_npc_state
    • 已新增 upsert_npc_state_and_return
    • 已新增 resolve_npc_social_action
    • 已新增 resolve_npc_social_action_and_return
    • 已新增 resolve_npc_interaction
    • 已新增 resolve_npc_interaction_and_return

3. 当前冻结的数据口径

3.1 relation_state

当前首版冻结为:

  1. affinity
  2. stance

stance 当前只冻结 5 档:

  1. Hostile
  2. Guarded
  3. Neutral
  4. Cooperative
  5. Bonded

当前阈值直接对齐现有前端 / Node 原语:

  1. < 0 -> Hostile
  2. < 15 -> Guarded
  3. < 30 -> Neutral
  4. < 60 -> Cooperative
  5. >= 60 -> Bonded

3.2 stance_profile

当前首版冻结为:

  1. trust
  2. warmth
  3. ideological_fit
  4. fear_or_guard
  5. loyalty
  6. current_conflict_tag
  7. recent_approvals
  8. recent_disapprovals

字段策略:

  1. 数值统一收敛到 0 ~ 100
  2. 最近好评 / 反感文本统一只保留最近 3 条。
  3. current_conflict_tag 仍允许为空,不在本轮强绑世界线程 ID。

3.3 npc_state

当前首版字段冻结为:

  1. npc_state_id
  2. runtime_session_id
  3. npc_id
  4. npc_name
  5. affinity
  6. relation_state
  7. help_used
  8. chatted_count
  9. gifts_given
  10. recruited
  11. trade_stock_signature
  12. revealed_facts
  13. known_attribute_rumors
  14. first_meaningful_contact_resolved
  15. seen_backstory_chapter_ids
  16. stance_profile
  17. created_at
  18. updated_at

当前策略:

  1. npc_state 保持 private 真相表口径。
  2. npc_state_id 允许由 runtime_session_id + npc_id 自动派生,避免外部每次重复拼接。
  3. relation_state 作为显式冗余字段落表,避免每次读取都重复派生。
  4. npc_name 当前保留为调试与兼容聚合字段,不承担唯一键职责。

4. 当前 reducer / procedure 口径

4.1 upsert_npc_state

当前负责:

  1. 校验 runtime_session_id / npc_id / npc_name
  2. 归一化 stance_profile
  3. 归一化 relation_state
  4. npc_state_id 为主键执行幂等写入

4.2 resolve_npc_social_action

当前只承接 纯 NPC 关系状态 的最小变更,不负责背包、任务、队伍、战斗副作用。

当前动作冻结为:

  1. Chat
  2. Help
  3. Gift
  4. Recruit
  5. QuestAccept

当前规则:

  1. Chat
    • 默认按 max(2, 6 - chatted_count) 推进好感
    • 递增 chatted_count
    • 强制标记 first_meaningful_contact_resolved = true
  2. Help
    • 若已使用过援手则拒绝
    • 默认推进 4 点好感
    • 写入 help_used = true
  3. Gift
    • 递增 gifts_given
    • 默认按 4 点好感推进,允许外部显式传入覆盖值
  4. Recruit
    • 若当前好感 < 60 则拒绝
    • 写入 recruited = true
    • 同时标记首遇已完成
  5. QuestAccept
    • 默认推进 3 点好感
    • 只改 NPC 关系侧立场数据,不直接落 quest 真相

当前 procedure 仅返回最新 NpcStateSnapshot,不在本轮提前扩出 story patch / UI 文案 contract。

4.3 resolve_npc_interaction

当前首版 resolve_npc_interaction 不直接承担所有跨子域副作用,而是先固定 NPC 单次正式交互 的最小统一结果口径。

当前输入冻结为:

  1. runtime_session_id
  2. npc_id
  3. npc_name
  4. interaction_function_id
  5. updated_at_micros
  6. release_npc_id(仅为后续招募换队预留,当前不在 Rust 侧正式消费)

当前支持的 function 只冻结为:

  1. npc_preview_talk
  2. npc_chat
  3. npc_help
  4. npc_recruit
  5. npc_fight
  6. npc_spar
  7. npc_leave

当前输出冻结为:

  1. npc_state
  2. interaction_status
  3. action_text
  4. result_text
  5. story_text
  6. battle_mode
  7. encounter_closed
  8. affinity_changed
  9. previous_affinity
  10. next_affinity

当前规则:

  1. npc_preview_talk
    • 只把交互状态切到 Previewed
    • 不改好感
  2. npc_chat
    • 复用 resolve_npc_social_action(Chat) 的关系推进
    • 返回 interaction_status = Dialogue
  3. npc_help
    • 复用 resolve_npc_social_action(Help)
    • 返回 interaction_status = Resolved
  4. npc_recruit
    • 当前只负责把 npc_state.recruited = true
    • 不在本轮承担 companion / roster 真相写入
    • 返回 interaction_status = Recruited
  5. npc_fight
    • 不改 npc_state.affinity
    • 返回 interaction_status = BattlePending
    • battle_mode = Fight
  6. npc_spar
    • 不改 npc_state.affinity
    • 返回 interaction_status = BattlePending
    • battle_mode = Spar
  7. npc_leave
    • 不改关系真相
    • 返回 interaction_status = Left
    • encounter_closed = true

当前刻意不做:

  1. 不直接生成 RuntimeStoryPatch
  2. 不直接写 companions / roster / inventory_slot
  3. 不直接把玩家 HP / MP、切磋战斗目标、战斗奖励塞进这个 reducer

也就是说,这一层当前只负责把 Node 侧 resolveNpcInteraction 的统一入口语义 先冻结为可编译 contract不宣称已经迁完全部副作用。


5. 当前刻意未做

本轮明确没有扩到以下范围:

  1. 还没有落 npc_trade 的库存与价格正式结算
  2. 还没有落 npc_gift 的背包扣减与物品收益结算
  3. 还没有落 npc_recruit 的队伍替换与 companion 真相迁移
  4. npc_fight / npc_spar 的正式 battle_state 初始化编排不在 module-npc crate 内部完成,而是下沉到 spacetime-module 聚合 procedure
  5. 还没有把 custom worldnarrativeProfile / backstoryReveal 真正投影进 SpacetimeDB
  6. 还没有把 Node 侧 npcInteractionService 全量切到 server-rs
  7. 还没有给前端接入 SpacetimeDB 的 NPC 订阅读模型

也就是说,本轮只是把 NPC 关系状态基座 立起来,不宣称已经完成完整 NPC 子域迁移。


6. 下一步建议

后续应继续按下面顺序推进:

  1. npc_recruit 的 companion / roster 真相迁移拆成 module-npc + module-runtime + module-story 的联合 reducer 设计。
  2. spacetime-client / Axum 侧继续把 npc_fight / npc_sparbattle_state 联合编排接口接出来。
  3. npc_trade / npc_gift 的正式库存、扣减与收益迁到 inventory / runtime-item 联动链。
  4. backstoryReveal / privateChatUnlockAffinity / narrativeProfile 的可见性规则投成显式读模型。
  5. 再接 api-server 的 NPC facade 与前端 runtime action。