问题8 登录系统: - node:sqlite 账号存储(scrypt 哈希 + HMAC 签名 token),preset admin 改为 e2hang/evan1115 - /api/auth/register|login|me,me 返回 room 供重联 - 网关拦截 create/join 需登录(401),列表保持开放 踢人逻辑修正(问题2 延伸): - kickMove 真正释放座位(folded=false, name=null)而非锁死 - war/mill 去掉永久封禁语义 管理员越权:管理员 token 可删任意房间、踢任意房间内的人 单房间约束(用户新需求): - 同一用户同时只能在一个房间,create/join 他房返回 409 - 踢人后若房间仅剩踢人者则自动解散并释放其记录(修复 2 人局踢光对手后被困 409) - 陈旧房间记录兜底清理 UI 清理:移除"改名"按钮与"你的名字"输入框;删除房间按钮仅房主/管理员可见 docs: 新增 12-auth-kick-single-room.md 汇总本会话全部需求/根因/修复/边界测试 Co-Authored-By: Claude (CodeBuddy) <noreply@codebuddy.ai>
55 lines
4.2 KiB
Markdown
55 lines
4.2 KiB
Markdown
# 问题 6:结算亮牌 —— 开最大人的牌;All-in 必须翻两边牌,不盖自己的牌
|
||
|
||
## 现象(用户反馈)
|
||
“结算的时候开最大人的牌,如果是 All in 必须翻两边的牌,不要盖住自己的牌。”
|
||
|
||
推断两条诉求:
|
||
1. **摊牌时亮出相关玩家的底牌**(特别是赢得最大池 / 参与对决的玩家)。
|
||
2. **有人 all-in 时,必须两边都翻牌**(即所有 all-in 相关的玩家都要亮牌,不能让别人替你盖着),
|
||
且**不能盖住自己的牌**(自己的底牌在结算后应当可见,而不是被盖回)。
|
||
|
||
## 现状(基于 `holdem.ts` + `HoldemBoard.tsx`)
|
||
- `resolveHand`(showdown)构造 `results.revealedHands`:
|
||
```ts
|
||
revealedHands: Object.fromEntries(
|
||
unfolded.filter(pid => G.hands[pid]).map(pid => [pid, G.hands[pid]])
|
||
)
|
||
```
|
||
即**所有未弃牌玩家**的底牌都会亮(`HoldemBoard` 读 `G.results.revealedHands` 渲染,`HoldemBoard.tsx:122`)。
|
||
→ 已经“开最大人的牌”(摊牌就全体亮),符合诉求 1。
|
||
- **问题在 all-in 提前摊牌**:当多人 all-in 且街道还没发完(例如翻牌后全员 all-in → 直接级联到 showdown),
|
||
此时 `G.hands` 里**只有仍 inHand 的玩家**有牌;弃牌者没有。这是对的(弃牌不亮)。
|
||
- **“不要盖住自己的牌”**:结算后 `resolveHand` 末尾 `G.hands = {}`(`holdem.ts:647`)清空了秘密数据,
|
||
导致结算结果界面里**自己的底牌也消失**(被“盖住”)。用户希望结算后仍能看到自己的牌。
|
||
- **“All-in 必须翻两边的牌”**:当前若一方全下、另一方跟注后继续(没到 showdown),中途弃牌不亮——标准规则
|
||
是“全下对决时双方都亮”。本项目只在 showdown 才亮全部未弃牌者,基本满足;但**被跟注后对方弃牌(fold-win)时不亮牌**
|
||
(`HoldemBoard` fold-win `revealedHands = null`),此时全下者其实已经获胜且应亮牌——现有规则“fold-win 不亮”是常见做法,
|
||
但用户明确要求“all-in 必须翻两边”,需调整为:**只要有玩家 all-in 且手牌结束,就亮出参与对决者的牌**。
|
||
|
||
## 拟采用方案
|
||
1. **结算后保留自己的牌可见**:
|
||
- 不要在 `resolveHand` 里立刻 `G.hands = {}`。改为:把“我自己的底牌”也纳入 `results` 的一个字段
|
||
`results.myHandAtShowdown?: Record<pid, [Card,Card]>` 或在 `revealedHands` 中保留自己。
|
||
- 简化:在 `playerView` 里,若 `results` 存在,把自己的 `G.hands[playerID]` 也塞进 `myHand` 或 `revealedHands[playerID]`,
|
||
供 UI 显示“你这手牌”。
|
||
- 真正的秘密清理仍要做(避免下一手泄露),但可延迟到下一手 `preflop.onBegin` 发牌前再清。
|
||
当前 `startHandSetup` 开头就重置并覆盖 `G.hands`,所以**不清空也不影响下一手保密**,只需在 `waiting` 阶段展示完即可。
|
||
→ 折中:保留 `G.hands` 直到下一手 onBegin 覆盖(最简洁),或显式复制进 `results` 后清空。
|
||
|
||
2. **All-in 对决强制亮牌**:
|
||
- 在 `resolveHand` 之前/之中判定:若手牌结束是因为有人 all-in 且存在“跟注者/对决者”,
|
||
即使最终走 fold-win(对方弃牌),也把**全下者 + 其对手**的底牌亮出。
|
||
- 具体:构造 `revealedHands` 时,除了 `unfolded`(未弃牌者),还纳入
|
||
`allIn 玩家` 与 `对其下注被跟注的玩家`,保证“all-in 两边都翻”。
|
||
- 调整 `results.kind`:fold-win 但含 all-in 对决时,仍填充 `revealedHands`(不完全是“无人跟注”语义,
|
||
可保留 kind 但让 UI 显示亮牌)。或新增 `kind: 'allin-showdown'`。
|
||
|
||
3. **UI(`HoldemBoard`)**:
|
||
- 结算区(已有 `results` 渲染,`HoldemBoard.tsx:243`)展示 `revealedHands` 里所有亮出的牌(含自己的)。
|
||
- 自己的底牌:若 `G.results` 存在且自己在对决中,`myHand` 仍显示,不被盖。
|
||
|
||
## 验证
|
||
- 单测:`resolveHand` 在 all-in + 对方弃牌场景,断言 `revealedHands` 包含全下者及其对手。
|
||
- 单测:showdown 后 `results` 包含自己 pid 的牌(或 `myHand` 在 results 阶段仍非空)。
|
||
- 联机手测:全下对决 → 结算界面双方底牌都翻开,自己的牌不盖。
|