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

98 lines
6.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 14 · 双升DoubleUp游戏 + 单端口一键启动 / 部署
## 做什么
1. **新增双升(两副牌升级 / 拖拉机 / 80 分)游戏**:完整规则引擎 + 牌桌 UI + 联机。
- 纯逻辑在 `packages/engine/src/games/double-up-core.ts`(与 boardgame.io 无关,便于单测)。
- 游戏定义在 `packages/engine/src/games/double-up.ts`boardgame.io `Game` + `ui` 字段)。
- 牌桌 UI 在 `apps/web/src/DoubleUpBoard.tsx`(自定义 Board非通用 `<Board>`)。
- 规则选型见 `docs/double-up/00-rules.md`V1 含大小王(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.ts``Game` 定义 + 阶段机 `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. 跟牌校验(甩牌修复点)
`validateFollow``mixed`(甩牌领出,如 AAK 三张同花)单独分支:手里该类别有牌就只需出 ≥1 张同花色牌,**不强制对子/拖拉机,也不要求张数一致**;断门则任意出。该修复让甩牌后其他家可正常跟单张同花色牌(此前误落到 pair/tractor 逻辑被拒)。
### 3. 单端口 `start.sh`
根目录 `start.sh`(已 `chmod +x`
```sh
./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.ts``PORT` 默认 8000但显式支持 `PORT=5173`;且 `serveStatic``apps/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` prop`App.tsx``double-up``fixedSeats: 4`,创建房间时不显示人数下拉,直接用 4。`onSelect` 回传的 `numPlayers` 也用该固定值。
## 部署(公网暴露,不含 frp 配置)
> frp 的具体配置由用户另行管理;这里只记录服务器侧与客户端侧的非 frp 部分。
**服务端**(生产单端口):
```sh
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 运行时数据,已在 `.gitignore``packages/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.ts`allowedHosts 更新)、`server/index.ts`(注册 DoubleUp`rooms.ts`setName 权威化)、`LobbyRoomList.tsx`fixedSeats
- 测试:双升引擎单测 58 例通过;全引擎套件 174 例通过;`tsc --noEmit`web + engine干净。
## 用户验证路径
```bash
./start.sh # 单端口 :5173
# 浏览器开 http://localhost:5173
# 1. 选「双升」→ 创建房间(固定 4 人)→ 4 个客户端加入
# 2. 房主开始 → 发牌 → 亮主/确认 → 庄家扣底 → 出牌(单/对/拖拉机/甩)
# 3. 整局打完自动结算 + 升级,进入下一局
```
## 关联
- [12-unified-port-and-player-names.md](./12-unified-port-and-player-names.md) — 统一端口基础vite proxy + 静态 serve
- [13-holdem-game.md](./13-holdem-game.md) — 同类「引擎 + Board + 可变人数大厅」范式
- `docs/double-up/00-rules.md` — 双升规则拍板
- `CODEBUDDY.md`「统一端口联网」一节 — 单端口同源设计说明