# 问题 1:改名后牌桌 UI 不刷新 ## 现象 点击“改名”后,**房间列表 pill 上的名字更新了**,但进入牌桌后,座位上的名字(`HoldemBoard`)**没有任何变化**。 ## 根因(已定位) 存在**两个相互独立的名字来源**,改名只更新了其中一个: 1. **Lobby 名字**:`LobbyRoomList.renameMe()` → `lobby.updatePlayer(...)` → boardgame.io `/update` 路由只改 `metadata.players[playerID].name` (见 `boardgame.io/dist/cjs/server.js:2428`:`metadata.players[playerID].name = newName;`)。 房间列表(pill)读的就是这个,所以刷新后变了。 2. **游戏内名字**:牌桌 `HoldemBoard` 显示的是 `G.players[pid].name`,由 `sitDownMove`(`holdem.ts:363`)在入座时写入一次,**之后再也没更新过**。 因此房间列表刷新(来源 1 变了)但牌桌(来源 2 没变)出现不一致。 ## 拟采用方案 让游戏内的名字成为**单一权威来源**,并把改名事件传导进游戏状态: 1. **engine(`holdem.ts`)**:新增 `renameMove({ G, ctx, playerID }, newName?)`: ```ts export function renameMove({ G, ctx, playerID }, rawName?: string) { const me = playerID ?? ctx.currentPlayer; const p = G.players[me]; if (!p || !p.seated) return; const nm = (rawName ?? '').trim().slice(0, 20); if (!nm) return; p.name = nm; pushLog(G, `${nm} 改名`); } ``` 沿用现有 `sitDownMove` 的信任模型(直接用 `playerID`,局域网休闲场景可接受)。 在 `moves` 里注册 `rename: { move: renameMove, client: false }`。 2. **ui(`HoldemBoard.tsx`)**:新增 effect——当 `playerName` prop 变化且该座位已入座时, 自动 `onMove('rename', [playerName])`: ```ts const renamedRef = useRef(null); useEffect(() => { if (playerName && renamedRef.current !== playerName && G.players[me]?.seated) { renamedRef.current = playerName; onMove('rename', [playerName]); } }, [playerName, G.players, me, onMove]); ``` 3. **ui(`App.tsx` 已有链路)**:`LobbyRoomList.renameMe` 成功后调 `onRename(newName)` → 已写入 `App.playerName`(`App.tsx:225` 的 `onRename`)→ 作为 `playerName` prop 透传给 `HoldemBoard`(`App.tsx:609`)→ 触发上面的 effect → 改掉 `G.players[me].name`。 这样改名在“房间列表”和“牌桌”两处都会刷新,且只依赖已有的 prop 链路,无需重构 Client 实例。 ## 验证 - 联机:改名 → 牌桌 `holdem-seat-{pid}` 的 `p.name` 在下一帧变为新名。 - 单测:直接调 `renameMove` 断言 `G.players[me].name` 更新。 - 边界:空名 / 超过 20 字被截断;未入座不生效。