Files
huajishe-tts/docs/dev-log/14-double-up-game.md
e2hang ddcb28472c feat(double-up): 双升游戏 + 单端口 start.sh 部署 + 文档统一更新
- 新增双升(DoubleUp)完整实现:引擎 double-up-core.ts / double-up.ts、牌桌 UI DoubleUpBoard.tsx、单测与 E2E
- 甩牌跟牌修复:validateFollow 新增 mixed 分支,甩牌后其他家可正常跟单张同花色牌
- start.sh:PORT=5173 单端口一键启动(Koa 同时 serve 页面+/api+/socket.io),浏览器直连无需 Vite 代理
- 玩家名 server 权威化(join 时写入 G.playerNames);LobbyRoomList 支持 fixedSeats(双升固定 4 人)
- 文档:新增 dev-log/14、更新 dev-log README 索引与进度、更新根 README 启动/部署说明
- .gitignore 忽略根目录 data/*.db 运行时 SQLite
2026-08-30 02:11:04 +08:00

6.9 KiB
Raw Permalink Blame History

14 · 双升DoubleUp游戏 + 单端口一键启动 / 部署

做什么

  1. 新增双升(两副牌升级 / 拖拉机 / 80 分)游戏:完整规则引擎 + 牌桌 UI + 联机。
    • 纯逻辑在 packages/engine/src/games/double-up-core.ts(与 boardgame.io 无关,便于单测)。
    • 游戏定义在 packages/engine/src/games/double-up.tsboardgame.io Game + ui 字段)。
    • 牌桌 UI 在 apps/web/src/DoubleUpBoard.tsx(自定义 Board非通用 <Board>)。
    • 规则选型见 docs/double-up/00-rules.mdV1 含大小王(108) / V2 2 为常主 / V3 翻第四张兜底 / V4 甩牌罚分 / V6 抠底封顶 64 倍。
    • E2E 实测脚本在 apps/web/e2e/double-up-full-game.spec.ts
  2. 新增 start.sh:单端口(PORT=5173一键启动——Koa 同时 serve 前端页面 + /api REST + /socket.io WS浏览器直连 :5173,无需 Vite 代理。
  3. 玩家名字 server 权威化join 时由 server 把登录用户名写入 G.playerNames[playerID],不再依赖客户端 setName 是否触发。
  4. LobbyRoomList 支持固定人数:双升固定 4 人,创建房间不再显示人数下拉(fixedSeats prop

怎么解决(具体做法)

1. 双升引擎结构

  • double-up-core.ts:所有确定性逻辑(主牌判定 isTrump、牌序 compareCards/trumpWeight、牌型解析 parsePlay、跟牌校验 validateFollow、甩牌全大 isThrowAllBig、赢墩 trickWinnerIndex、抠底倍数 kouchudiMultiplier、计分升级 levelDelta)。无随机、无 Date、无副作用,便于单测与 playerView 多端一致。
  • double-up.tsGame 定义 + 阶段机 waiting → deal → bottom → play → score。关键 move
    • startGame:房主触发,原子 dealAll 一次发满 100 张(每家 25剩 8 张作底;这是修掉联机同步卡死的关键——早期一张张 dealOne 发会产生大量连续 move触发 boardgame.io 客户端 stateID 竞态,其他客户端收不到更新。
    • declareTrump / confirmTrump:发牌阶段亮主/反主(优先级 单级牌<对级牌<对小王<对大王),四家全确认后 resolveTrump 定主花色 + 庄家 + 庄家收 8 底牌。
    • setBottom:庄家扣 8 张隐藏底牌。
    • playCards:单/对/拖拉机/甩牌;首出若 mixed 且同花色副牌则判为甩牌,调用 isThrowAllBig 校验,失败罚分并强制出最小牌。
    • 一墩结算在 applyPlay:四家出齐(按人数计,不按牌数)判赢家,赢家收走全部桌面牌;currentTrick 为扁平每牌列表,trickPlays 为每玩家一手。

2. 跟牌校验(甩牌修复点)

validateFollowmixed(甩牌领出,如 AAK 三张同花)单独分支:手里该类别有牌就只需出 ≥1 张同花色牌,不强制对子/拖拉机,也不要求张数一致;断门则任意出。该修复让甩牌后其他家可正常跟单张同花色牌(此前误落到 pair/tractor 逻辑被拒)。

3. 单端口 start.sh

根目录 start.sh(已 chmod +x

./start.sh

行为:

  • 加载 nvm + pnpm缺依赖自动 pnpm install
  • apps/web/dist 不存在,自动先 pnpm --filter @tts-like/web build(单端口 serve 需要该产物)。
  • 清掉残留的 5173/8000 占用。
  • PORT=5173 pnpm --filter @tts-like/server dev 后台启动tsx watch开发热重载
  • :5173/healthz 就绪后打印访问地址Ctrl-C 连同派生进程一起清理。

为什么映射 5173 即可packages/server/src/index.tsPORT 默认 8000但显式支持 PORT=5173;且 serveStaticapps/web/dist 存在时自动 serve 整个前端。所以 Koa 单一进程在 5173 上同时提供页面 + /api + /socket.io,浏览器同域直连,无需 Vite 代理。这契合 CODEBUDDY.md「统一端口」一节的设计。

4. 玩家名字 server 权威

packages/server/src/rooms.ts 的 join 成功后,dispatchMove(server, games, matchID, playerID, credentials, 'setName', [playerID, username]) 把登录用户名写进 G.playerNames。这样无论客户端是否触发 setName 都能看到真实名字,而非 玩家0/1/2/3。best-effort失败仅影响显示。

5. 固定人数大厅

LobbyRoomList 新增 fixedSeats propApp.tsxdouble-upfixedSeats: 4,创建房间时不显示人数下拉,直接用 4。onSelect 回传的 numPlayers 也用该固定值。

部署(公网暴露,不含 frp 配置)

frp 的具体配置由用户另行管理;这里只记录服务器侧与客户端侧的非 frp 部分。

服务端(生产单端口):

pnpm --filter @tts-like/web build          # 生成 apps/web/dist
PORT=5173 ALLOWED_ORIGINS=https://你的域名 pnpm --filter @tts-like/server start
# 或开发/调试:直接 ./start.sh占 5173
  • 必须设 ALLOWED_ORIGINS 为浏览器将使用的公网 origin留空走默认 LAN 段,公网会被 CORS 拒绝Socket.IO 握手也失败)。
  • TLS 终止在反代Nginx/Caddy或隧道层整链 http 或整链 https 保持一致,避免 wss://ws:// 混用被浏览器拦。
  • 数据库 data/*.db(根目录 SQLite是 server 运行时数据,已在 .gitignorepackages/server/data/*.db 覆盖思路下管理——注意根目录 data/ 当前未忽略,提交时需排除(见 commit 说明)。

客户端(浏览器):零配置。打开 http://<公网地址>:5173(或隧道给的 https 域名)即可。createLobbyClient('') 走同域相对路径,/socket.io 同源。

局域网联机:同 dev 模式,http://<本机内网IP>:5173/ 直接可用origins 默认含 LAN 段)。

成果

  • 新增文件:double-up-core.ts / double-up.ts / DoubleUpBoard.tsx / double-up.test.ts / double-up-sim.test.ts / e2e/double-up-full-game.spec.ts / start.sh / docs/double-up/*
  • 修改:App.tsx(注册双升 + 待机 UI 简化)、vite.config.tsallowedHosts 更新)、server/index.ts(注册 DoubleUprooms.tssetName 权威化)、LobbyRoomList.tsxfixedSeats
  • 测试:双升引擎单测 58 例通过;全引擎套件 174 例通过;tsc --noEmitweb + engine干净。

用户验证路径

./start.sh                              # 单端口 :5173
# 浏览器开 http://localhost:5173
# 1. 选「双升」→ 创建房间(固定 4 人)→ 4 个客户端加入
# 2. 房主开始 → 发牌 → 亮主/确认 → 庄家扣底 → 出牌(单/对/拖拉机/甩)
# 3. 整局打完自动结算 + 升级,进入下一局

关联

  • 12-unified-port-and-player-names.md — 统一端口基础vite proxy + 静态 serve
  • 13-holdem-game.md — 同类「引擎 + Board + 可变人数大厅」范式
  • docs/double-up/00-rules.md — 双升规则拍板
  • CODEBUDDY.md「统一端口联网」一节 — 单端口同源设计说明