8.0 KiB
8.0 KiB
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 件事:
- 新增
server-rs/crates/module-npc/真实 crate,而不是继续停留在目录占位。 - 在
module-npc中冻结relation_state / stance_profile / npc_state的首版领域类型与校验 helper。 - 在
module-npc中补齐build_initial_stance_profile、normalize_npc_state_snapshot、apply_npc_social_action等最小规则原语。 - 在
server-rs/crates/spacetime-module/中新增npc_state表。 - 在
spacetime-module中新增upsert_npc_state、resolve_npc_social_action及对应 procedure,形成最小可编译写入口。 - 在
module-npc中新增resolve_npc_interaction的首版领域 contract,并在spacetime-module中补对应 reducer / procedure。
2. 本轮新增的真实工程落点
2.1 新增 crate
server-rs/crates/module-npc/Cargo.tomlserver-rs/crates/module-npc/src/lib.rs
2.2 workspace 与主工程聚合
server-rs/Cargo.toml- 已把
crates/module-npc纳入 workspace members
- 已把
server-rs/crates/spacetime-module/Cargo.toml- 已接入
module-npc依赖
- 已接入
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
当前首版冻结为:
affinitystance
stance 当前只冻结 5 档:
HostileGuardedNeutralCooperativeBonded
当前阈值直接对齐现有前端 / Node 原语:
< 0->Hostile< 15->Guarded< 30->Neutral< 60->Cooperative>= 60->Bonded
3.2 stance_profile
当前首版冻结为:
trustwarmthideological_fitfear_or_guardloyaltycurrent_conflict_tagrecent_approvalsrecent_disapprovals
字段策略:
- 数值统一收敛到
0 ~ 100。 - 最近好评 / 反感文本统一只保留最近
3条。 current_conflict_tag仍允许为空,不在本轮强绑世界线程 ID。
3.3 npc_state
当前首版字段冻结为:
npc_state_idruntime_session_idnpc_idnpc_nameaffinityrelation_statehelp_usedchatted_countgifts_givenrecruitedtrade_stock_signaturerevealed_factsknown_attribute_rumorsfirst_meaningful_contact_resolvedseen_backstory_chapter_idsstance_profilecreated_atupdated_at
当前策略:
npc_state保持 private 真相表口径。npc_state_id允许由runtime_session_id + npc_id自动派生,避免外部每次重复拼接。relation_state作为显式冗余字段落表,避免每次读取都重复派生。npc_name当前保留为调试与兼容聚合字段,不承担唯一键职责。
4. 当前 reducer / procedure 口径
4.1 upsert_npc_state
当前负责:
- 校验
runtime_session_id / npc_id / npc_name - 归一化
stance_profile - 归一化
relation_state - 以
npc_state_id为主键执行幂等写入
4.2 resolve_npc_social_action
当前只承接 纯 NPC 关系状态 的最小变更,不负责背包、任务、队伍、战斗副作用。
当前动作冻结为:
ChatHelpGiftRecruitQuestAccept
当前规则:
Chat- 默认按
max(2, 6 - chatted_count)推进好感 - 递增
chatted_count - 强制标记
first_meaningful_contact_resolved = true
- 默认按
Help- 若已使用过援手则拒绝
- 默认推进
4点好感 - 写入
help_used = true
Gift- 递增
gifts_given - 默认按
4点好感推进,允许外部显式传入覆盖值
- 递增
Recruit- 若当前好感
< 60则拒绝 - 写入
recruited = true - 同时标记首遇已完成
- 若当前好感
QuestAccept- 默认推进
3点好感 - 只改 NPC 关系侧立场数据,不直接落 quest 真相
- 默认推进
当前 procedure 仅返回最新 NpcStateSnapshot,不在本轮提前扩出 story patch / UI 文案 contract。
4.3 resolve_npc_interaction
当前首版 resolve_npc_interaction 不直接承担所有跨子域副作用,而是先固定 NPC 单次正式交互 的最小统一结果口径。
当前输入冻结为:
runtime_session_idnpc_idnpc_nameinteraction_function_idupdated_at_microsrelease_npc_id(仅为后续招募换队预留,当前不在 Rust 侧正式消费)
当前支持的 function 只冻结为:
npc_preview_talknpc_chatnpc_helpnpc_recruitnpc_fightnpc_sparnpc_leave
当前输出冻结为:
npc_stateinteraction_statusaction_textresult_textstory_textbattle_modeencounter_closedaffinity_changedprevious_affinitynext_affinity
当前规则:
npc_preview_talk- 只把交互状态切到
Previewed - 不改好感
- 只把交互状态切到
npc_chat- 复用
resolve_npc_social_action(Chat)的关系推进 - 返回
interaction_status = Dialogue
- 复用
npc_help- 复用
resolve_npc_social_action(Help) - 返回
interaction_status = Resolved
- 复用
npc_recruit- 当前只负责把
npc_state.recruited = true - 不在本轮承担 companion / roster 真相写入
- 返回
interaction_status = Recruited
- 当前只负责把
npc_fight- 不改
npc_state.affinity - 返回
interaction_status = BattlePending battle_mode = Fight
- 不改
npc_spar- 不改
npc_state.affinity - 返回
interaction_status = BattlePending battle_mode = Spar
- 不改
npc_leave- 不改关系真相
- 返回
interaction_status = Left encounter_closed = true
当前刻意不做:
- 不直接生成
RuntimeStoryPatch - 不直接写
companions / roster / inventory_slot - 不直接把玩家 HP / MP、切磋战斗目标、战斗奖励塞进这个 reducer
也就是说,这一层当前只负责把 Node 侧 resolveNpcInteraction 的统一入口语义 先冻结为可编译 contract,不宣称已经迁完全部副作用。
5. 当前刻意未做
本轮明确没有扩到以下范围:
- 还没有落
npc_trade的库存与价格正式结算 - 还没有落
npc_gift的背包扣减与物品收益结算 - 还没有落
npc_recruit的队伍替换与 companion 真相迁移 npc_fight / npc_spar的正式battle_state初始化编排不在module-npccrate 内部完成,而是下沉到spacetime-module聚合 procedure- 还没有把
custom world的narrativeProfile / backstoryReveal真正投影进 SpacetimeDB - 还没有把 Node 侧
npcInteractionService全量切到server-rs - 还没有给前端接入
SpacetimeDB的 NPC 订阅读模型
也就是说,本轮只是把 NPC 关系状态基座 立起来,不宣称已经完成完整 NPC 子域迁移。
6. 下一步建议
后续应继续按下面顺序推进:
- 把
npc_recruit的 companion / roster 真相迁移拆成module-npc + module-runtime + module-story的联合 reducer 设计。 - 在
spacetime-client/ Axum 侧继续把npc_fight / npc_spar的battle_state联合编排接口接出来。 - 把
npc_trade / npc_gift的正式库存、扣减与收益迁到inventory / runtime-item联动链。 - 把
backstoryReveal / privateChatUnlockAffinity / narrativeProfile的可见性规则投成显式读模型。 - 再接
api-server的 NPC facade 与前端 runtime action。