没有账号系统,如何实现房间、身份与断线重连
“洛一把”没有用户账号,却仍然需要知道刷新后的玩家是谁、该回到哪个座位,以及掉线前有多少分。
解决办法不是把昵称当身份,而是在加入房间时签发一组临时凭据,让“方便人类识别的名字”和“机器可验证的身份”分开。
房间码只负责定位房间
房间码由六位大写易读字符组成,并排除容易混淆的 0/O/1/I。创建时随机生成,如果已经存在就重新生成。
房间码适合口头分享,但它不是密码。知道房间码只能尝试加入等待中的房间,不能冒充已有玩家。
创建或加入成功后,服务端返回:
playerId:房间内部的随机玩家 ID;resumeToken:高熵恢复令牌;code:房间码。
客户端把这些信息保存在本机。之后建立 WebSocket 或刷新重连时携带恢复令牌,服务端才能把新连接重新绑定到原玩家。
昵称不能承担身份职责
昵称限制为 1—12 个字符,并且房间内不能重复。这个限制是为了显示和交流,不是为了安全。
如果重连只提交昵称,任何知道房间码的人都可以在原玩家掉线后抢占其积分。恢复令牌则把“用户友好的名字”和“机器可验证的身份”分开了。
等待阶段和比赛阶段采用不同离线规则
等待阶段的目标是顺利坐满。普通玩家主动离开,或者断线超过十秒,席位就可以释放,让其他人加入。
比赛开始后房间锁定。掉线玩家的席位、得分和答题记录会保留到整场结束,新玩家不能替换参赛者。否则一次网络波动就可能让陌生人继承原玩家的位置。
房主掉线时先等待十秒;仍未恢复,则把房主权限转给当前在线且加入最早的玩家。比赛进行中不依赖房主推进,但等待房间必须始终有人能够开始游戏。
所有玩家都离线后,房间继续保留三十分钟,给移动网络切换、页面刷新和设备短暂休眠留下恢复窗口。超过保留时间才清理持久状态。
一个 WebSocket 不等于一个玩家
浏览器可能重复连接,也可能旧连接尚未完全关闭,新连接已经建立。因此服务端维护的是“连接到玩家 ID”的映射,而不是把连接对象直接当玩家。
玩家是否在线,取决于是否仍有任何有效连接指向该 ID。断开一条旧连接时,如果同一玩家已经建立新连接,不能误把玩家标记成离线。
客户端连接流程保持简单:
- 根据房间码读取本地身份;
- 使用恢复令牌建立 WebSocket;
- 连接成功后发送
sync; - 收到服务端状态后校准时钟;
- 断开时显示“正在重连”;
- 等待 1.5 秒后重新连接。
页面不自行合并增量事件,而是反复接收服务端投影的房间快照。房间人数最多四人,完整快照体积可控,却能显著降低客户端状态错乱的概率。
状态同步也承担超时恢复
线上运行后,曾经出现过房间停在 0 秒却没有结算的情况。仅仅重连并重新发送旧状态不能解决问题,因为房间仍然认为自己处于游戏中。
后来 sync 被升级成状态机的恢复入口:服务端收到同步请求时,会先使用当前时间执行一次 tick,补做已经到期的提示、结算或下一轮,再广播新状态。
客户端也会在校准时间超过截止时间 500 毫秒后发送一次兜底同步。它不能在本地直接结算,但可以唤醒权威状态机。
这条机制非常实用:重连不应该只恢复网络连接,还应该推动已经落后的服务端状态追上现实时间。
这套房间系统解决了什么
在没有账号和数据库用户表的前提下,系统仍然实现了:
- 房间内唯一玩家;
- 刷新后恢复原席位;
- 断线不丢分;
- 不重复创建玩家;
- 房主自动转移;
- 游戏开始后禁止替补;
- 有期限的房间恢复。
它不适合长期社交关系和跨设备账号同步,但非常适合一次性、低门槛、通过链接邀请朋友加入的小型网页游戏。
项目仓库:LonakoBc/luo-yi-ba
在线试玩:luo-yi-ba.pages.dev
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!



