←  返回关于我

项目介绍

独立全栈 / AI 协作开发

鹅了个棋

一款面向桌面浏览器的多人竞速棋盘游戏。项目将 65 格地图、事件与道具系统、离线人机对战和 2 至 4 人真人 / AI 混合联机整合在同一套确定性规则中,并围绕可靠同步、断线恢复与内容版本管理完成全栈实现。

鹅了个棋离线与在线对局配置界面

从本地对局到多人房间

玩家可以直接配置本地棋手开始离线对局,也可以创建私人房间,邀请其他玩家加入。真人和 AI 可以混合入座,房间覆盖准备、开局、回合行动、事件选择、道具取舍、胜负结算及恢复流程。

65 格竞速地图

棋子沿路线推进,并在地标、事件、碰撞和道具作用下改变局势,最终以到达终点决定胜负。

真人 / AI 混合

支持 2 至 4 个座位,本地和联机模式共享规则与 AI 能力,便于单人体验和多人组局。

私人房间

通过房间码加入对局,包含房主权限、座位准备、开局控制和房主转移等完整流程。

断线恢复

刷新或短时掉线后可恢复原座位;超过宽限时间后由 AI 接管,避免整局因单个玩家离线停滞。

事件与道具

行动过程包含事件候选、主动与被动道具、目标选择和取舍确认,增加路线竞速之外的决策空间。

内容管理

地图、事件与皮肤可在管理端编辑、校验、预览、审核、发布和回滚,运行中的房间锁定开局版本。

共享领域能力的 Monorepo

项目由玩家端、游戏服务器、内容服务器和管理后台 4 个应用组成,并抽取规则、协议、AI、内容和渲染等共享包。依赖方向保持单向:领域包不依赖 React、WebSocket 或数据库,应用层在这些稳定边界之上组合产品能力。

纯 TypeScript 规则内核

规则函数接收状态、游戏定义、玩家和命令,输出新状态、领域事件及表现提示,不直接操作 DOM、Canvas、网络或数据库。

运行时协议校验

共享类型之外,协议包以 Zod 校验不可信网络数据,并维护协议版本与 TypeScript 推导类型,减少客户端和服务端漂移。

三类结果各司其职

State 保存权威事实,DomainEvent 描述业务语义,PresentationCue 提供骰面、路径和动画信息,共同兼顾恢复与表现。

确定性随机

权威状态保存 RNG seed 与 cursor。同一游戏定义、初始状态和命令序列能够得到相同结果,便于恢复与稳定测试。

应用层、共享领域包、持久化和客户端表现之间的主要依赖关系。点击图片可查看原图。

技术栈 TypeScript · React · Vite · PixiJS · Three.js · XState · Node.js · WebSocket · Zod · SQLite · PostgreSQL · Docker

用状态机表达允许与禁止

对局包含座次确认、起始道具、等待行动、事件选择、道具取舍和游戏结束等阶段。每个阶段只开放明确的命令;客户端用服务端下发的合法命令生成控件和目标列表,最终合法性仍由服务端再次校验。

领域状态约束命令边界,避免只在前端隐藏按钮而接口仍可被非法调用。点击图片可查看原图。

服务端权威的联机对局

客户端只表达意图,不上传骰子点数、抽卡结果或最终位置。Authority 统一校验玩家身份、Revision、当前行动者与阶段,再调用规则引擎产生随机结果和结算,避免客户端作弊或不同实现造成状态分叉。

  1. React 根据服务端提供的合法命令判断当前操作是否可用,玩家提交带唯一 commandId 的请求。
  2. 服务端按房间串行处理命令,并校验身份、expectedRevision、行动权和阶段。
  3. 规则内核消费权威随机源,处理骰子、修正、移动、碰撞、事件、道具和胜负。
  4. Authority 先持久化新检查点与幂等结果,再向每位玩家广播经过隐私裁剪的更新。
  5. 客户端立即推进权威快照,再按顺序播放骰子、路线、目标提示和棋子移动动画。

命令幂等

相同 commandId 和载荷重试时返回原结果,不重复执行;相同 ID 携带不同载荷会被拒绝。

乐观并发控制

expectedRevision 表示命令基于哪个权威版本,旧命令会被拒绝并触发客户端重新同步。

按房间串行执行

队列确定服务端写入顺序,Revision 判断客户端上下文是否过期,两者处理不同层面的并发问题。

命令校验、持久化、玩家投影和客户端表现队列之间的同步流程。点击图片可查看原图。

权威状态不等待动画

网络更新到达后,客户端立即保存最新权威快照,用于 Revision 和下一条命令;画面则通过表现队列逐步追赶。React 管理大厅、HUD、卡牌和弹窗,PixiJS 负责棋盘与棋子,Three.js 在透明画布中呈现 3D 骰子,XState 编排表现阶段。

顺序表现

rolling、routePreview、targetEmphasis、routeFade、moving 等阶段按序推进,避免不同动画相互抢占。

快速恢复

动画异常、页面失焦或超时后可直接同步至最新权威快照,再继续消费后续更新,不拖慢其他玩家。

重复与乱序处理

客户端以 snapshot revision 为顺序依据,只接收更高版本;完整重连快照会清空旧表现队列并重建棋盘。

清晰生命周期

React 只管理 PixiJS 容器的创建与销毁,高频补间、镜头和 ticker 更新留在场景树中执行。

恢复、隐私与持久化

联机正确性不仅是把消息广播出去,还包括进程重启、临时断网、多标签页和多实例切换时如何维持房间身份与单写者约束。

安全恢复凭证

玩家凭证保存在标签页 sessionStorage,服务端只持久化 SHA-256 摘要;重连后发送最新完整投影,而非补播旧动画。

多连接意识

成员记录有效连接数,只有最后一条连接断开才进入宽限期,关闭单个标签页不会误判玩家离线。

玩家隐私投影

服务端按查看者裁剪道具、私有候选、私有事件、合法命令和随机状态,敏感信息不会先发到浏览器再隐藏。

SQLite 恢复

单实例部署使用 SQLite WAL 保存房间、Authority 检查点和幂等缓存,支持进程重启后的状态恢复。

PostgreSQL 所有权

多实例模式使用 owner lease、房间路由和单调递增 fencing token,让数据库拒绝旧 owner 的延迟写入。

先持久化再广播

新检查点保存成功后才对外发布,确保客户端可见状态不会领先于服务器可恢复状态。

可发布、可回滚的游戏内容

管理平台覆盖地图、事件和皮肤的编辑、校验、预览、审核、发布与回滚。发布版本不可变,新房间使用当前版本,进行中的房间继续使用开局时锁定的内容、地图和规则版本,避免运营调整改变存量对局的含义。

共享正式渲染器

后台地图预览复用玩家端棋盘渲染器,让坐标、标记和路线语义与正式运行时保持一致。

认证与 RBAC

前端守卫改善操作体验,内容服务器仍对每个受保护接口校验会话与角色,发布和回滚无法绕过页面直接调用。

规格驱动的 AI 协作

AI 用于仓库检索、影响范围分析、方案比较、局部实现、测试补充和文档同步。我负责需求边界、领域模型、包依赖、服务端信任模型、架构取舍与最终验收,让协作建立在可审查的上下文和明确责任上。

  1. 先用产品和技术规格明确目标、非目标、领域不变量与验收条件。
  2. 每项任务从实际代码、Git 状态和相关测试出发,避免把旧文档当作唯一事实来源。
  3. 通过仓库级约束限制依赖方向、修改范围和验证命令,将工作拆成可验收的小改动。
  4. 修改后执行类型检查、Lint、单元测试和风险相关的端到端测试,并由人工审查关键安全边界。

测试策略与演进方向

测试按责任边界展开,而不是只依赖最终页面回归。规则、协议、房间服务、客户端组件和跨浏览器联机流程分别验证各自不变量,失败时回到规格判断是实现回归还是测试假设已经过期。

规则与协议

覆盖碰撞、终点、道具、固定随机种子、Schema 往返、命令幂等、过期 Revision 和快照恢复。

房间服务

验证房主权限、准备与开局、持久化、进程重启恢复、owner lease 和房间单写者约束。

客户端表现

验证合法操作、事件选择、表现队列、重复或乱序更新,以及动画异常后的权威状态恢复。

端到端联机

使用 Playwright 覆盖双客户端建房、加入、准备、开局和刷新恢复,并关注不同浏览器与延迟链路。

进入鹅了个棋 ↗