从单机到多人:可扩展联机架构与公平猜曲
给一个已经能玩的单机项目加入多人联机,最危险的做法不是代码写得慢,而是为了联机把原有玩法全部推倒重写。
“洛一把”原本已经有 React/Vite 前端、静态歌曲题库和本地游戏服务。因此多人版本首先确定了一条边界:单机玩法继续按原来的方式运行,多人玩法作为一条独立路径加入。
首版范围为什么很小
多人首版只支持:
- 创建 2—4 人房间;
- 房主选择目标人数、轮数和曲库;
- 生成六位房间码和邀请链接;
- 玩家输入昵称后加入;
- 坐满后由房主开始;
- 完成所有轮次后显示排名。
大厅、自动匹配、账号、聊天、旁观、踢人和房间重赛都暂时不做。这些功能并非没有价值,但会迅速把一个玩法项目变成社交平台项目。先限制范围,才能集中验证同一道题如何同步、如何公平判分,以及玩家掉线后如何回来。
保留单机服务,新增多人状态
原有的 gameService、歌曲搜索、字段标准化和反馈算法继续保留。多人客户端仍然复用联想搜索和表格展示,但不再拥有最终决定权。
多人系统分成三层:
React 页面 └─ WebSocket / REST 客户端 └─ 通用房间会话 RoomSession └─ 具体玩法处理器 ├─ GuessSongMode ├─ SeniorityMode ├─ SortingMode └─ TriathlonMode通用房间层只处理成员、房主、连接、阶段、持久化和广播;玩法处理器只处理出题、命令、结算,以及不同玩家可以看到哪些状态。
每个处理器遵循相近的职责:
handleCommand:处理经过身份校验的玩家命令;tick:根据服务端时间推进超时和下一轮;addScheduleTimes:告诉房间下一次应该何时唤醒;project:按查看者身份生成可公开状态。
消息中保留 mode 和协议版本。这样客户端可以拒绝不兼容的旧协议,服务端也能在不改变房间外壳的情况下增加玩法。
服务端必须是比赛的唯一裁判
单机猜曲只需要回答“玩家猜对了吗”,多人猜曲还必须回答谁先猜对、倒计时以谁为准,以及对手可以看到多少信息。
多人猜曲采用服务端权威模型:客户端负责输入、动画和倒计时显示,服务端负责选题、时间、提示、判分和阶段推进。
每轮开始时,服务端记录:
startedAt:开始时间;endsAt:截止时间;- 当前答案;
- 已解锁提示等级;
- 每位玩家的猜测与本轮得分。
四人局正确顺序的积分依次为 5、3、2、1,少于四人时只使用前面的分数档。未猜出者得 0 分;全员提前猜出时立即结算,否则等到截止时间。
客户端提交只包含歌曲 ID。服务端从自己的题库查出歌曲,重新计算字段反馈,并以请求进入房间串行队列的顺序确定名次。即使两个请求几乎同时到达,也不会由客户端时间戳或网络往返时间自行决定先后。
截止边界明确为:
receivedAt < endsAt 接受receivedAt >= endsAt 拒绝如果没有这条规则,同一个“剩余 0 秒”的答案就可能在不同机器上得到不同结果。
提示和隐私也属于服务端状态
三分钟内依次解锁:
- 开始 60 秒后公开发布时间与歌姬;
- 开始 120 秒后公开 STAFF;
- 开始 150 秒后公开歌词;
- 开始 180 秒后强制结算。
客户端可以根据服务器校准时间显示倒计时,但不能自行宣布提示已解锁。真正公开哪些字段,由服务端投影决定。玩家修改本地时钟,只能改变自己的动画,无法提前拿到后面的提示。
同一个房间状态会按照查看者生成不同版本。玩家本人可以收到自己的歌曲、字段反馈和方向提示;对手只能收到猜测次数、发生时间、是否正确、公开反馈的颜色状态和得分。
对手状态中不包含歌曲 ID、曲名和完整字段值。前端把它绘制成与真实表格相同的模糊单元格,让玩家能感受到对手正在接近答案,却无法直接抄写。
客户端仍然可以复用单机体验
服务端权威并不意味着前端原有逻辑全部作废。歌曲联想搜索、输入标准化、表格组件和字段颜色都可以继续复用。区别在于:搜索结果只是准备提交的候选,最终反馈必须来自服务端。
这种分工既保留了单机版本成熟的输入体验,也避免客户端自行判分。
从猜曲扩展到更多玩法
多人猜曲稳定后,我们先实现了独立的“谁是老资历”和“歌曲大排序”,再考虑组合成铁人三项。只有每个玩法都能独立完成出题、同步、结算和状态投影,综合赛才只是组合处理器,而不是互相调用的页面逻辑。
歌曲排序就是一个典型例子:玩家把五首歌按发布时间排成时间线,拖拽发生在本地,但完整排列属于私有答案,不能在每次移动时广播给对手。服务端只公开是否提交,提交后再按照十组歌曲对的相对顺序计算正确数量。
相比只检查五个绝对位置,相对顺序计分能给“整体方向正确但有两首互换”的答案合理的部分分数。同一轮还要保证五首歌拥有不同的发布时间,否则先后关系没有定义。
轻量重构带来的收益
这次重构没有把单机玩法改造成联网玩法,而是把题库、搜索和判定能力留在公共服务中,把“谁决定状态”明确交给服务端。
最终得到的不只是几个多人页面,而是一套可以继续容纳新玩法的结构。以后新增填字、抢答或团队模式时,首先要实现的是新的玩法处理器和隐私投影,而不是重新编写房间码、连接、成员和重连系统。
对于已有项目来说,先保护已经工作的部分,再为真正变化的部分建立清晰边界,通常比追求一次性完美的“大一统架构”更可靠。
项目仓库:LonakoBc/luo-yi-ba
在线试玩:luo-yi-ba.pages.dev
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!



