# datang **Repository Path**: waidais/datang ## Basic Information - **Project Name**: datang - **Description**: 纯AI实现的村落养成文字游戏 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-22 - **Last Updated**: 2026-05-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 大唐无名村流民逆袭记 面向小规模私人群聊的文字肉鸽社交经营游戏后端。 玩家扮演灾年流落到无名村的流民,通过群聊发言进行行动、整活、打工、修炼、战斗, 程序底层负责数值与公平性,大模型负责把结果讲成有味道的市井传说。 > 设计理念:**程序判官,AI 说书。** --- ## 文档入口 | 类别 | 文件 | |---|---| | 玩法设计 | [游戏设计文档](大唐无名村流民逆袭记_游戏设计文档.md) | | 开发流程硬约束 | [Claude 开发总控文档](大唐无名村流民逆袭记_Claude开发总控文档.md) | | 给未来 Claude 实例 | [CLAUDE.md](CLAUDE.md) | | 工程架构 | [docs/architecture/architecture.md](docs/architecture/architecture.md) · [依赖图](docs/architecture/dependency_graph.md) | | 任务路线 | [主任务树](docs/tasks/master_task_tree.md) · [当前任务](docs/tasks/current_task.md) | | 架构决策记录 | [ADR 目录](docs/adr/) | | 待启动规划 | [多村庄](docs/planning/2026-05-22-multi-village.md)(已落地,M1~M5)· [Rhai 脚本接入](docs/planning/2026-05-22-rhai-scripting.md)(R1~R5 已落地) | | 各模块设计 | [docs/modules/](docs/modules/) | --- ## 当前进度 **Task 000 ~ Task 010 + Task M1 ~ M5 + Task R1 ~ R5 全部完成**——脚本基建落地,技能 / 主动 / 被动框架就位。 主要端到端链已跑通: - **多村庄**:`datang --villages-dir ... create-village main` / `... create-village test` → 同 ID 跨村独立角色,物理隔离到 `./villages/main.db` 与 `./villages/test.db`。 - **脚本驱动战斗**:`CombatEngine::resolve_combat` 编排多回合 + 胜负判定;`scripts/combat/turn.rhai` 管单回合普攻;`scripts/skills/.rhai` 管主动 / 被动技能;同 seed + 同 snap + 同 loadout ⇒ 同 `CombatLog`。 - **脚本驱动行动**:`scripts/actions/idle.rhai` / `chop_firewood.rhai` 通过 `ActionResultBuilder` sink 写状态变化。 11 个 crate 测试现状: | Crate | 职责 | 测试数 | |---|---|---:| | `game-domain` | 纯领域模型(ID / 五维 / 状态 / 钱包 / 背包 / 病情 / 玩家聚合 / Village 聚合) | 90 | | `game-scripting` | Rhai 脚本宿主 + 注入式确定性 RNG + game-domain 类型注册 + `register_extra` 扩展口 | 19 | | `game-rules` | 行动结算 trait + `IdleAction` / `ChopFirewoodAction` 脚本化 + `ActionResultBuilder` sink + `ActionSnapshot` | 51 | | `game-ai-contracts` | LLM 请求 / 响应 JSON 协议(`PlayerSnapshot` / `NpcSnapshot` 含 `village_id`) | 40 | | `game-vault` | AI 金库 + `AppliedReward` 单向构造 + `validate` | 27 | | `game-injury` | 伤情授权 + 4 种治疗类型枚举 + 阳寿倒计时 | 18 | | `game-combat` | 战斗快照 + 战报数据 + 脚本驱动单回合公式 + `CombatEngine` 主循环 + `SkillRegistry` + 主动 / 被动 dispatch | 87 | | `game-memory` | 三层记忆数据形态 + `PromptContext` | 27 | | `game-persistence` | SQLite + sqlx + `VillagePool` + `VillageRepository` + `SqlitePlayerRepository` | 17 | | `game-service` | `VillageService` + `PlayerService`(按 `village_id` 路由 `Arc`) | 12 | | `game-adapter-cli` | `datang` CLI + `--village` 全局参数 + 四子命令(create-village / list-villages / create-player / find-player) | 21 | | **合计** | | **409** | **架构决策记录**(7 份 ADR 已落地): - ADR-0001:初始架构与技术栈 - ADR-0002:AI 协议迭代策略与厂商无关性 - ADR-0003:`ActionResult` 归属与依赖方向调整 - ADR-0004:`AppliedReward` 单向构造与 `DailyVault` 余额封装 - ADR-0005:持久化层对 only-Serialize 类型的反序列化策略 - ADR-0006:迁移文件不可变性(多村庄改造中 0001 一次性重写决策) - ADR-0007:Rhai 脚本宿主 + 类型注册契约(含 R5 dispatch 模型 + sink 全集 mutator 表) **多村庄基建(M 系列,已完成)**: | Task | 模块 | 核心产出 | |---|---|---| | M1 | game-domain | `Village` 聚合(id / name / created_at) | | M2 | game-ai-contracts | `PlayerSnapshot` / `NpcSnapshot` 加 `village_id`(给 LLM 区分跨村身份) | | M3 | game-persistence | `VillagePool`(约定式 `villages/*.db` 扫描)+ `VillageRepository` + ADR-0006 | | M4 | game-service | `VillageService` + `PlayerService` 按 `village_id` 路由 | | M5 | game-adapter-cli | `--village` 全局参数 + 四子命令 + 端到端两村庄手测 | **Rhai 脚本基建(R 系列,已完成 R1 ~ R5)**: | Task | 模块 | 核心产出 | |---|---|---| | R1 | game-scripting | Rhai 宿主 + `ScriptEngine` + 确定性 RNG + game-domain 类型注册 + ADR-0007 | | R2 | game-rules | `IdleAction` 脚本化 + `ActionResultBuilder` sink 模式落地 + `register_extra` 扩展口 | | R3 | game-rules + game-ai-contracts | `ChopFirewoodAction` + `ActionSnapshot` + sink 全集 mutator + 双参 `(snap, out)` 标准签名 | | R4 | game-combat | 战斗回合脚本化 + `CombatEngine` 主循环 + `TurnBuilder` sink + `CombatStateSnapshot` | | R5 | game-combat | 技能框架——`SkillRegistry` + `SkillLoadoutView` 暴露 + Rust 端 dispatch + MP / cooldown + 主动 / 被动各 1 个 PoC 技能 | **核心约束达成**: - 1 QQ 群 = 1 村庄 = 1 SQLite 文件;玩家/NPC 聚合**不**带 `village_id` 字段(物理隔离);同 QQ 号跨群 = 跨村庄的独立游戏角色。 - 战斗 / 行动 / 技能脚本化但 Rust 端管 dispatch、循环、胜负、MP/cd 时序——脚本即使写错也不能影响战斗终止逻辑(§8.3 程序判官原则)。 - 同 seed + 同输入 ⇒ 同输出(战斗、行动、技能释放轨迹)。 **下一阶段路线**: - **R6(候选)**:随机事件框架(路遇道贼 / 神仙 / 同行流民等触发性事件由脚本驱动;Rust 端控触发概率 + 校验后续状态变更)。详见 [docs/tasks/current_task.md](docs/tasks/current_task.md)。 - 之后:service 层串联 + LLM 接入 + adapter 升级。 --- ## 快速命令 ```bash # 验收命令 cargo fmt --all -- --check cargo build --workspace cargo test --workspace cargo clippy --workspace --all-targets -- -D warnings # 端到端 demo(M5 已跑通:两村庄场景) SMOKE=/tmp/datang_smoke rm -rf $SMOKE && mkdir -p $SMOKE # 1. 创建两个村庄 cargo run -p game-adapter-cli -- --villages-dir $SMOKE create-village main --name "无名村" cargo run -p game-adapter-cli -- --villages-dir $SMOKE create-village test --name "测试村" # 2. 同 ID 玩家跨村独立 cargo run -p game-adapter-cli -- --villages-dir $SMOKE --village main create-player villager_001 cargo run -p game-adapter-cli -- --villages-dir $SMOKE --village test create-player villager_001 \ --strength 80 --agility 120 # 3. 查询验证隔离 cargo run -p game-adapter-cli -- --villages-dir $SMOKE --village main find-player villager_001 cargo run -p game-adapter-cli -- --villages-dir $SMOKE --village test find-player villager_001 cargo run -p game-adapter-cli -- --villages-dir $SMOKE list-villages ``` 部分输出: ```text 已创建村庄:main(无名村),created_at = 1779684361 已创建玩家:villager_001(村庄 main) 五维:力道 50 / 体力 50 / 元气 50 / 根骨 50 / 身法 50 钱包:0 文 已创建玩家:villager_001(村庄 test) 五维:力道 80 / 体力 50 / 元气 50 / 根骨 50 / 身法 120 钱包:0 文 村庄列表: main: 无名村(created_at = ...) test: 测试村(created_at = ...) ``` --- ## 工程边界(贯穿所有任务) - **LLM 调用入口集中在 `game-service`**——`game-combat` / `game-injury` / `game-persistence` / adapter 内部都不允许调用 LLM。 - **AI 输出不能直接落库**——所有奖励先变 `RewardProposal` → 经 `game-vault::validate` 校验/裁剪 → `AppliedReward`。`AppliedReward` 只 Serialize 不 Deserialize,让"凭空构造"在编译期被拒。 - **治疗类型只有 4 种枚举变体**——`TreatmentKind::Incurable { countdown }` 把阳寿倒计时绑定到变体,类型系统层面禁止 AI 发明第五种。 - **所有关键随机必须接受外部 seed 注入**——同 seed + 同输入 ⇒ 同输出。R 系列脚本通过 `ScriptEngine::new(seed)` 注入 `ChaCha8Rng`;战斗、行动、技能释放轨迹端到端确定性。 - **跨村庄物理隔离**:聚合根(Player / NPC / 记忆 / 金库)**不**带 `village_id` 字段;不同村庄数据落到不同 SQLite 文件,无需 SQL 层过滤。`PlayerSnapshot` / `NpcSnapshot` 仅在 LLM 协议层暴露 `village_id` 给说书使用。 - **依赖方向严格单向**:`game-ai-contracts → game-rules → game-domain`(ADR-0003 调整后);`game-scripting → game-domain`(无业务 crate 依赖);其他规则模块(vault / injury / combat / memory)只依赖 `game-domain`;service 顶层串联;adapter 不绕过 service。 - **脚本只是策划入口,Rust 端管所有时序**——脚本通过 sink builder 累加事件;MP 扣费、cooldown 演化、HP 减伤、胜负判定、回合数上限全由 Rust 控(ADR-0007 §D7.4)。 详细禁止项见 [Claude 开发总控文档](大唐无名村流民逆袭记_Claude开发总控文档.md) §13。 --- ## 工程结构 ```text datang/ ├─ Cargo.toml # workspace + dependencies(含 rhai / rand_chacha) ├─ CLAUDE.md # 给未来 Claude 实例的简报 ├─ crates/ # 11 个 crate │ ├─ game-domain/ # 领域模型层(含 Village 聚合) │ ├─ game-scripting/ # Rhai 宿主 + 类型注册 + 确定性 RNG(R1) │ ├─ game-rules/ # IdleAction / ChopFirewoodAction 脚本化(R2 / R3) │ ├─ game-ai-contracts/ # PlayerSnapshot/NpcSnapshot.village_id │ ├─ game-vault/ │ ├─ game-injury/ │ ├─ game-combat/ # CombatEngine + 脚本化战斗 + SkillRegistry(R4 / R5) │ ├─ game-memory/ │ ├─ game-persistence/ # sqlx + VillagePool + migrations/ │ ├─ game-service/ # VillageService + PlayerService │ └─ game-adapter-cli/ # datang 二进制 + --village 多村庄子命令 ├─ scripts/ # 业务脚本(git 跟踪,编译期 include_str! 嵌入) │ ├─ actions/ # idle.rhai / chop_firewood.rhai │ ├─ combat/ # turn.rhai(单回合公式) │ └─ skills/ # fist_form_1.rhai(主动)/ iron_skin.rhai(被动) ├─ adapters/ │ └─ koishi/ # 占位,未启动 ├─ villages/ # 运行时 SQLite 目录(默认 ./villages,可 --villages-dir 覆盖) ├─ docs/ │ ├─ architecture/ # 总体架构 + 依赖图 │ ├─ modules// # 每个 crate 6 份设计文档 │ ├─ tasks/ # master_task_tree + current_task + 各完成报告 │ ├─ adr/ # ADR-0001 ~ ADR-0007 │ └─ planning/ # 多村庄(已落地)/ Rhai(R1~R5 已落地) └─ 大唐无名村流民逆袭记_*.md # 原始设计文档 ```