系统架构与核心设计文档
前言:从宏观到微观
欢迎加入天命三国项目。这份文档将带您从宏观的游戏定位出发,逐步深入到具体的子系统、技术选型、核心流程,最后探讨未来的扩展方向。


第一章:概述
1.1 天命三国
天命三国不是一款传统的、规则固定的三国策略游戏。它是一款 基于Web的回合制策略叙事游戏,其核心乐趣在于:
- 沉浸式叙事:系统通过大语言模型(LLM)动态生成富有文言风格的剧情,让每一次选择都独一无二。
- 多维决策 :玩家不仅需要在军事 上运筹帷幄,还要在政治、外交、占星等多个维度进行权衡,影响天下大势。
- 蝴蝶效应:游戏会通过"世界变化"提示,让玩家清晰地感知到自己的决策如何像蝴蝶扇动翅膀一样,引发后续一系列事件。
简而言之,我们是在用现代化的AI技术,去实现一个充满古典浪漫与不确定性的三国幻想世界。
1.2 技术目标
为了支撑上述体验,我们确立了四个核心的技术目标,这也是我们所有架构设计的基石:
- 绝对的类型安全:使用 TypeScript (TS) 和 Prisma,从前端到数据库,确保数据类型在编译时就能发现错误,杜绝运行时因数据结构不一致导致的"灵异事件"。
- 高可维护性:严格的代码分层(UI、业务逻辑、数据持久化分离),让每一部分代码职责清晰,便于定位问题、修改功能和新人上手。
- 可观测性:通过结构化日志、实时推送 (SSE) 和定时任务 (Cron),让系统的运行状态、玩家行为和AI决策过程都"透明可见",方便调试和运营。
- 可扩展性:API路由设计为插件化,剧情脚本和LLM提示词可热插拔、可配置,为未来的功能增加和多语言支持留足空间。
第二章:系统的骨架与血肉 ------ 关键子系统概览
#mermaid-svg-8odpwOMrqp1VaI2B{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8odpwOMrqp1VaI2B .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8odpwOMrqp1VaI2B .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8odpwOMrqp1VaI2B .error-icon{fill:#552222;}#mermaid-svg-8odpwOMrqp1VaI2B .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8odpwOMrqp1VaI2B .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8odpwOMrqp1VaI2B .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8odpwOMrqp1VaI2B .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8odpwOMrqp1VaI2B .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8odpwOMrqp1VaI2B .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8odpwOMrqp1VaI2B .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8odpwOMrqp1VaI2B .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8odpwOMrqp1VaI2B .marker.cross{stroke:#333333;}#mermaid-svg-8odpwOMrqp1VaI2B svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8odpwOMrqp1VaI2B p{margin:0;}#mermaid-svg-8odpwOMrqp1VaI2B .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8odpwOMrqp1VaI2B .cluster-label text{fill:#333;}#mermaid-svg-8odpwOMrqp1VaI2B .cluster-label span{color:#333;}#mermaid-svg-8odpwOMrqp1VaI2B .cluster-label span p{background-color:transparent;}#mermaid-svg-8odpwOMrqp1VaI2B .label text,#mermaid-svg-8odpwOMrqp1VaI2B span{fill:#333;color:#333;}#mermaid-svg-8odpwOMrqp1VaI2B .node rect,#mermaid-svg-8odpwOMrqp1VaI2B .node circle,#mermaid-svg-8odpwOMrqp1VaI2B .node ellipse,#mermaid-svg-8odpwOMrqp1VaI2B .node polygon,#mermaid-svg-8odpwOMrqp1VaI2B .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8odpwOMrqp1VaI2B .rough-node .label text,#mermaid-svg-8odpwOMrqp1VaI2B .node .label text,#mermaid-svg-8odpwOMrqp1VaI2B .image-shape .label,#mermaid-svg-8odpwOMrqp1VaI2B .icon-shape .label{text-anchor:middle;}#mermaid-svg-8odpwOMrqp1VaI2B .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8odpwOMrqp1VaI2B .rough-node .label,#mermaid-svg-8odpwOMrqp1VaI2B .node .label,#mermaid-svg-8odpwOMrqp1VaI2B .image-shape .label,#mermaid-svg-8odpwOMrqp1VaI2B .icon-shape .label{text-align:center;}#mermaid-svg-8odpwOMrqp1VaI2B .node.clickable{cursor:pointer;}#mermaid-svg-8odpwOMrqp1VaI2B .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8odpwOMrqp1VaI2B .arrowheadPath{fill:#333333;}#mermaid-svg-8odpwOMrqp1VaI2B .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8odpwOMrqp1VaI2B .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8odpwOMrqp1VaI2B .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8odpwOMrqp1VaI2B .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8odpwOMrqp1VaI2B .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8odpwOMrqp1VaI2B .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8odpwOMrqp1VaI2B .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8odpwOMrqp1VaI2B .cluster text{fill:#333;}#mermaid-svg-8odpwOMrqp1VaI2B .cluster span{color:#333;}#mermaid-svg-8odpwOMrqp1VaI2B div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8odpwOMrqp1VaI2B .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8odpwOMrqp1VaI2B rect.text{fill:none;stroke-width:0;}#mermaid-svg-8odpwOMrqp1VaI2B .icon-shape,#mermaid-svg-8odpwOMrqp1VaI2B .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8odpwOMrqp1VaI2B .icon-shape p,#mermaid-svg-8odpwOMrqp1VaI2B .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8odpwOMrqp1VaI2B .icon-shape .label rect,#mermaid-svg-8odpwOMrqp1VaI2B .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8odpwOMrqp1VaI2B .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8odpwOMrqp1VaI2B .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8odpwOMrqp1VaI2B :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 持久化与辅助
业务子系统
后端核心
前端
用户操作
调用
构造Prompt
返回叙事/结果
调用具体逻辑
调用具体逻辑
...
...
...
读写数据
推送事件
解锁成就
推送更新
更新
驱动
触发季节/存档
用户界面 React
全局状态管理 Zustand
实时通知 Hook
API 路由层
游戏引擎 Narratve Engine
大语言模型 ZAI
战斗系统
外交系统
占星系统
政策系统
成就系统
数据库 PostgreSQL
定时任务 Cron
实时推送 SSE
- Narrative Engine (核心大脑) :位于
src/lib/game/engine.ts。它是整个游戏的"导演",负责接收玩家指令,拼装上下文给AI,解析AI返回的剧情,并协调所有子系统(战斗、外交等)完成一次"回合"的变更。 - 业务子系统 (五脏六腑):战斗、外交、占星、政策、成就等系统,它们是游戏的具体规则实现。引擎只负责"叫"它们做事,具体怎么做,由它们自己决定。
- Store & State Layer (神经中枢) :基于
Zustand和Immer的全局单例。它管理前端UI的所有状态,并负责与后端同步。前端组件只关心这个Store,不需要知道数据从哪里来。 - Real-time Notification (感官系统) :通过 Server-Sent Events (SSE) 实现。当后端有重要事件(如回合结束、成就解锁),会立刻"推送"给前端,前端通过
use-game-notificationsHook 接收并更新界面,实现毫秒级响应。
第三章:技术栈详解
我选择的每一项技术,都是为了解决一个特定问题。
| 分类 | 技术栈 | 角色与理由 |
|---|---|---|
| 前端框架 | Next.js 13 (App Router) + React 18 | 提供服务端渲染 (SSR),让首屏加载更快,利于SEO;App Router的文件夹结构让路由管理更清晰。 |
| 编程语言 | TypeScript | 类型安全是项目的生命线,TS确保了我们写的每一行代码都在预期之内。 |
| UI与样式 | TailwindCSS + shadcn/ui | 快速开发且风格统一。shadcn/ui 提供了一系列无样式、可访问的基础组件,我们可以自由定制,不背"样式债"。 |
| 状态管理 | Zustand + SWR | Zustand 轻量且强大,适合管理复杂的游戏全局状态;SWR 专门用于数据获取,自动处理缓存和重试。 |
| 后端逻辑 | Next.js API Routes | 利用 Next.js 的API路由作为Serverless函数,无需单独部署后端服务,项目结构统一。 |
| 数据库 | Prisma ORM + PostgreSQL | Prisma 提供了类型安全的数据库访问,其模型定义即为"单一事实来源"。PostgreSQL 可靠、功能强大,生产环境首选。开发环境用轻量的 SQLite。 |
| AI 集成 | Z-AI SDK | 统一封装了对大语言模型的调用,方便后续切换或升级模型。 |
| 部署与运维 | Docker + Caddy + GitHub Actions | Docker 保证环境一致性;Caddy 自动处理 HTTPS 证书;CI/CD 自动化构建和部署,减少人工失误。 |
| 监控与日志 | Prometheus + Grafana + Loki | Prometheus 收集指标,Grafana 可视化展示;日志统一收集到 Loki,方便检索和排查问题。 |
第四章:一局游戏的旅程 ------ 核心业务流程
让我们跟随玩家的操作,看数据是如何在整个系统中流动的:
Cron SSE推送 数据库 LLM 游戏引擎 API路由 前端UI 玩家 Cron SSE推送 数据库 LLM 游戏引擎 API路由 前端UI 玩家 #mermaid-svg-qYvRiyHYxcIlQL1w{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qYvRiyHYxcIlQL1w .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qYvRiyHYxcIlQL1w .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qYvRiyHYxcIlQL1w .error-icon{fill:#552222;}#mermaid-svg-qYvRiyHYxcIlQL1w .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qYvRiyHYxcIlQL1w .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qYvRiyHYxcIlQL1w .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qYvRiyHYxcIlQL1w .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qYvRiyHYxcIlQL1w .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qYvRiyHYxcIlQL1w .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qYvRiyHYxcIlQL1w .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qYvRiyHYxcIlQL1w .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qYvRiyHYxcIlQL1w .marker.cross{stroke:#333333;}#mermaid-svg-qYvRiyHYxcIlQL1w svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qYvRiyHYxcIlQL1w p{margin:0;}#mermaid-svg-qYvRiyHYxcIlQL1w .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qYvRiyHYxcIlQL1w text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-qYvRiyHYxcIlQL1w .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-qYvRiyHYxcIlQL1w .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-qYvRiyHYxcIlQL1w .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-qYvRiyHYxcIlQL1w .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-qYvRiyHYxcIlQL1w #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-qYvRiyHYxcIlQL1w .sequenceNumber{fill:white;}#mermaid-svg-qYvRiyHYxcIlQL1w #sequencenumber{fill:#333;}#mermaid-svg-qYvRiyHYxcIlQL1w #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-qYvRiyHYxcIlQL1w .messageText{fill:#333;stroke:none;}#mermaid-svg-qYvRiyHYxcIlQL1w .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qYvRiyHYxcIlQL1w .labelText,#mermaid-svg-qYvRiyHYxcIlQL1w .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-qYvRiyHYxcIlQL1w .loopText,#mermaid-svg-qYvRiyHYxcIlQL1w .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-qYvRiyHYxcIlQL1w .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-qYvRiyHYxcIlQL1w .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-qYvRiyHYxcIlQL1w .noteText,#mermaid-svg-qYvRiyHYxcIlQL1w .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-qYvRiyHYxcIlQL1w .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qYvRiyHYxcIlQL1w .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qYvRiyHYxcIlQL1w .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qYvRiyHYxcIlQL1w .actorPopupMenu{position:absolute;}#mermaid-svg-qYvRiyHYxcIlQL1w .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-qYvRiyHYxcIlQL1w .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qYvRiyHYxcIlQL1w .actor-man circle,#mermaid-svg-qYvRiyHYxcIlQL1w line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-qYvRiyHYxcIlQL1w :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 引擎核心处理阶段 alt成就解锁 loop回合循环 1. 登录/开始新游戏POST /api/game/start调用 startGame()创建会话、角色、世界状态返回成功返回初始数据200 OK + 游戏数据更新 Zustand Store,渲染界面2. 在底部控制栏做出决策 (例如"出征")POST /api/game/action {action: "attack"}调用 processAction()读取最新游戏状态构建LLM上下文 (buildContextMessage)调用LLM,传入Prompt返回叙事JSON解析JSON,应用世界变化 (applyWorldChanges)调用战斗/外交等子系统处理特定逻辑3. 持久化所有变更 (回合、日志、属性)4. 检查游戏结束/成就解锁写入成就记录推送 achievementUnlocked 事件5. 推送 turnResult 事件6. SSE 推送游戏结果useGameNotifications Hook 接收更新 Store 和 UI7. 展示新的剧情与变化定时触发 (季节事件)生成系统事件剧情推送系统事件
流程解读:
- 开始新局:后端正向您描述的那样,初始化一个干净的游戏世界。
- 玩家行动 :每一次点击"下一回合"或做出选择,都会触发一个
POST请求到/api/game/action。 - 引擎调度 :后端收到请求后,
processAction函数开始工作。它不是 硬编码所有规则,而是:- 读取最新状态。
- 将玩家选择、世界状态、角色属性等"喂"给LLM。
- LLM像一个"故事大王",根据上下文生成符合逻辑的叙事文本和结构化的数据变化(比如:民心+5,粮草-100,触发一场遭遇战)。
- 引擎解析这些数据,并调用对应的子系统(如
resolveCombat)来完成计算,最后更新数据库。
- 实时反馈 :整个计算过程结束后,后端通过SSE将增量更新(而不是整个页面)推送给前端。前端无缝更新UI,玩家立即看到故事发展和数值变化。
- 后台时间:Cron定时任务模拟了"时间流逝",即使在玩家不操作时,也会触发季节更替、NPC行动等事件,让世界"活"起来。
第五章:面向未来的设计 ------ 可扩展性路线图
一个优秀的架构,必须能优雅地拥抱变化。我们已经为未来的可能性埋下了种子。
-
插件化剧情 :您提到在
src/app/api/game/<module>/下添加新路由。这正是目前的设计。未来,我们可以实现一个"剧情加载器",扫描指定文件夹下的脚本文件(JSON或JS),动态注册新的API端点和引擎处理函数,实现真正的"热插拔"。 -
多语言支持 :利用
next-i18next等成熟的i18n方案。我们的策略是:- 所有UI文案抽离到
locales/目录。 - LLM Prompt 进行改造 :不再使用自然语言描述语言,而是显式地在Prompt中加入
[LANG: zh-CN]或[LANG: en]标记,指导AI输出对应语言的叙事。
- 所有UI文案抽离到
-
社区化与AI助手:
- 社区功能 :新增
Friendship和Leaderboard表非常简单。难点在于联盟战争等跨玩家PVP逻辑,这需要设计更复杂的并发控制 和事件锁机制,防止数据冲突。 - AI助手 :可以独立为一个微服务。当前端请求"AI建议"时,API网关将该玩家的脱敏状态发给AI助手服务,由它通过另一个LLM实例分析战局,返回策略建议。这解耦了主游戏引擎和辅助功能,互不影响。
- 社区功能 :新增
结语
这份文档是您探索天命三国代码世界的起点。它描绘了我们的蓝图、选择与思考。后续章节的细化文档(如数据库结构、API规范、UI组件库指南)将为您提供更实操性的指引。
请记住我们的核心原则:类型安全、逻辑清晰、系统可观测、架构可扩展。祝您开发愉快,与我们一起开创这个天命交织的三国世界!