Files
huajishe-tts/docs/dev-log/12-unified-port-and-player-names.md
e2hang c36d2aeae7 feat(server,web): unified port (/api prefix) + player naming system
方案1 统一端口:
- vite proxy: /api 和 /socket.io 转发到内部 :8000(不 rewrite)
- server: mountApiPrefix 把 boardgame.io router 挂到 /api 前缀
- server: 用原生 fs 实现静态文件 serve(SPA fallback),生产单进程
- 客户端 createLobbyClient('') 走同源 /api,CORS 完全消失

方案2 命名系统:
- App header 加"你的名字"输入框,localStorage 持久化
- LobbyRoomList 显示所有玩家名字("Alice · Bob")替代 "2/2"
- 房间内"改名"按钮调 POST /api/games/:name/:id/update

修复:
- LobbyClient 用 fetch 包装替代继承(原 request 是 TS private)
- mountApiPrefix 抽到独立文件(避免 test import index.ts 触发 run(8000))
- vite proxy 不 rewrite(避免和 server static serve 冲突)

Tests: pnpm ts 6/6, engine 34 + server 9, E2E 4/4.
Docs: dev-log 12 + lessons §11 + decisions D27-D29.
2026-08-24 12:17:44 +08:00

5.8 KiB
Raw Permalink Blame History

12 · 统一端口 + 玩家命名系统

做什么

用户要求两个改造(先给方案审核,通过后实施):

  1. 统一端口:不映射两个端口(之前 5173 页面 + 8000 API改成页面和 API 都在同一个端口,如页面 :5173、API :5173/api
  2. 命名系统:战局内可以修改自己的名字(大厅显示所有玩家名字)。

用户还问了一个关键问题:加新游戏(如德州扑克)是不是有 Lua 之类的脚本语言让玩家直接写? 答案记录在本文末尾。

方案(审核通过)

方案 1统一端口

开发环境vite proxy 把 /api/socket.io 转发到内部 :8000

生产环境server 直接 serve 静态文件 + mount /api 路由,单一进程。

浏览器 ──> :5173 (vite / server static)
              ├── /            → index.html (SPA)
              ├── /api/*       → boardgame.io lobby REST
              └── /socket.io/* → WebSocket

方案 2命名系统

  • joinMatch 时传 playerName
  • 大厅显示所有玩家名字(替代 2/2 这种数字)
  • 游戏内改名字调 POST /api/games/:name/:id/update {newName}
  • 名字持久化到 localStorage

遇到什么问题

问题 1koa-send 装包被拒(权限)

修法:用 Node 原生 fs/promisesreadFile + stat 自己实现静态文件 serve约 40 行),不依赖第三方包。

问题 2boardgame.io 的 LobbyClient.request 是 TS private无法继承加前缀

修法:放弃继承,直接写一个 fetch 包装(packages/engine/src/lobby-client.ts),行为兼容 LobbyClient 但请求路径自动加 /api 前缀。

问题 3ctx.URL 是只读 getter临时改 path 时报错

修法:只改 ctx.pathKoa 里这是 getter/setter 可写),不改 ctx.URL

问题 4服务端测试 import index.ts 会触发 server.run(8000) 导致 EADDRINUSE

修法:把 mountApiPrefix 抽到独立文件 packages/server/src/mount-api-prefix.tsindex.ts 和 test 都从那里 import。

问题 5vite proxy rewrite 和 server static serve 冲突

最初 vite proxy 把 /api 剥掉转发到 :8000/games,但 server 端 static serve 拦截了 /games(返回 index.html导致 /api/games 返回 HTML。

修法vite proxy 不 rewrite,保留 /api 转发到 :8000/api/games,由 server 端的 mountApiPrefix 剥掉 /api 转给 boardgame.io router。

问题 6pnpm dev 在错误目录执行

之前 cdpackages/server 后跑 pnpm dev 启动的是 servertsx watch导致 EADDRINUSE。教训:跑命令前明确 cd apps/web

怎么解决(具体做法)

1. vite proxyapps/web/vite.config.ts

server: {
  port: 5173,
  strictPort: true,
  host: '0.0.0.0',
  proxy: {
    '/api': { target: 'http://localhost:8000', changeOrigin: true },  // 不 rewrite
    '/socket.io': { target: 'http://localhost:8000', ws: true, changeOrigin: true },
  },
},

2. server /api 挂载packages/server/src/mount-api-prefix.ts

export function mountApiPrefix(app, router) {
  app.use(async (ctx, next) => {
    if (!ctx.path.startsWith('/api/')) return next();
    const original = ctx.path;
    ctx.path = ctx.path.replace(/^\/api/, '') || '/';
    try {
      await router.routes()(ctx, async () => {});
      await router.allowedMethods()(ctx, async () => {});
    } finally {
      ctx.path = original;
    }
  });
}

3. server 静态文件 servepackages/server/src/index.ts

fs/promises 实现,非 /api/socket.io/healthz 的请求返回 apps/web/dist/index.htmlSPA fallback

4. 客户端 LobbyClientpackages/engine/src/lobby-client.ts

export function createLobbyClient(serverUrl: string, apiPrefix: string = '/api') {
  // fetch 包装,请求路径自动加 /api 前缀
  // createLobbyClient('') → 同源 + /api默认
}

5. 命名系统App.tsx + LobbyRoomList.tsx

  • App header 加"你的名字"输入框localStorage 持久化
  • LobbyRoomList 显示 formatPlayers()"Alice · Bob"
  • 房间内"改名"按钮调 updatePlayer

成果

  • Commit: 见 git log
  • 测试:
    • pnpm ts 6/6
    • pnpm -r test engine 34 + server 9
    • pnpm test:e2e 4/4

用户验证路径

# 本机 + 局域网另一台
# 都开 http://192.168.5.11:5173/(同一个端口!)
# 1. 输入名字 → 创建房间 → 顶部显示"在房间 X 作为 P0"
# 2. 另一台输入名字 → 刷新列表 → 看到 "Alice" → 加入 P1
# 3. 大厅显示 "Alice · Bob"
# 4. 点"改名" → 输入新名字 → 大厅同步

关于加新游戏(用户问的问题)

当前系统没有 Lua/脚本语言层。 所有游戏都用 TypeScript 写(packages/engine/src/games/*.ts),编译进 server 进程,运行期不能替换。

加德州扑克需要(不是"调一下 Lua 文件"

  1. texas-holdem.tsreducer + 5 个 phase + 8 个 move + 隐藏信息,约 400 行 TS
  2. TexasHoldemBoard.tsx(手牌 + 公共牌 + 筹码 + 行动按钮,约 500 行 TSX
  3. 手牌评估库10 种牌型 + kicker约 200 行)
  4. 隐藏信息 plugin每人只看自己手牌
  5. 注册到 server + web
  6. 预估 3-4 周

如果要支持"玩家自己写游戏",需要单独做 脚本化层Lua/JS 沙箱 + game-DSL + 热加载),那是另一个 1-2 个月的产品方向。详见 docs/impl.md Phase 3。

关联