重连成功只是第一步
socket 重新连上只代表可以通信,断线期间世界已经继续变化。这个问题在项目早期经常被当成小功能,等内容量、平台数量和运营节奏上来之后才暴露成本。Godot 客户端要做的不是写一个临时脚本,而是把它当成可验证、可回滚、可观测的系统来设计。团队需要知道数据从哪里来,谁有权修改,失败后玩家看到什么。
重连状态机
状态机要区分 Disconnected、Reconnecting、Authenticated、Resyncing 和 Online。先把输入和输出数据结构定下来,字段要有版本、来源、时间戳和校验结果。不要让每个页面或节点自己拼数据。统一模型能让编辑器工具、运行时逻辑和 QA 脚本读取同一份真相,也方便后续接入平台差异。
断线期间 UI 怎么表现
短暂抖动用轻提示,长时间断线要锁定高风险操作并说明状态:
stateDiagram-v2
[*] --> Online
Online --> Disconnected: socket lost
Disconnected --> Reconnecting: retry policy
Reconnecting --> Authenticated: token accepted
Authenticated --> Resyncing: request world snapshot
Resyncing --> CatchingUp: apply delta and scene state
CatchingUp --> Online: input unlocked
Reconnecting --> OfflineFallback: retry exhausted
图里的每个节点都应该能单独记录耗时和失败原因。流程图不是文档装饰,而是帮助团队确认责任边界。某个阶段失败时,客户端应知道能否重试、能否降级、是否需要玩家确认。
世界快照的范围
重同步要拉场景、实体、玩家状态、背包版本、任务和副本阶段。把规则写成配置或 Resource,而不是散在 UI 和业务脚本里。比如阈值、白名单、黑名单、平台差异、灰度条件,都应该能在不改核心逻辑的情况下调整。配置也要有默认值和校验,缺字段时使用保守策略。
输入队列处理
断线后的战斗输入通常应清理或标记失效,不能重连后突然释放。玩家看到的反馈要具体。只说失败没有意义,应该说明是资源不足、版本不兼容、网络不可用、数据已过期还是需要重新进入页面。Godot 的 UI 很容易弹一个 Toast,但真正关键的是 Toast 背后的错误码要稳定。
场景切换中的断线
Loading 中断线要以服务器场景为准,必要时重新加载。对性能敏感的部分要做预算。不要把所有检查都放到同一帧,也不要在战斗或切场景高峰做大扫描。可以用分帧任务、后台准备和安全点提交。低端设备上,稳定比一次性完成更重要。
差异应用要分阶段
先确认场景,再应用实体、玩家、UI 数据,最后解锁输入。调试工具至少要显示当前状态、最近一次输入、最近一次输出、错误码、耗时和关联资源路径。很多线上问题不是算法错,而是资源、配置、平台状态不一致。把这些信息画出来,排查时间会大幅缩短。
玩家体验细节
位置修正、死亡、结算、队伍解散都要翻译成玩家能理解的反馈。QA 场景要覆盖正常路径、异常路径、低帧率、断网、切后台、语言切换、平台差异和重复操作。只测一次成功是不够的。真正决定系统可靠性的,是失败后能否回到一个干净状态。
测试场景
测试战斗、Loading、奖励、token 过期、快照失败和多次重连。上线后要有轻量指标,但不要上传无关隐私。记录聚合数量、失败阶段、资源版本、平台和耗时分布即可。通过这些数据,团队能知道问题是集中在某个平台、某个资源包,还是某个流程阶段。
落地建议
把重连当完整客户端状态机,不要只写网络 retry。落地时先做最小闭环:数据模型、执行流程、失败反馈、调试面板和一组 QA 用例。等闭环稳定,再扩展更多场景。Godot 给了足够的运行时自由度,工程上要补上的就是边界和纪律。
断线瞬间的本地冻结
断线检测不要太敏感。一次心跳延迟不代表断线,可以先进入 degraded 状态,显示小图标但不锁玩法;连续超时后再进入 Disconnected。进入 Disconnected 时,记录本地玩家状态、场景、输入队列、未完成请求。这个快照不是用来覆盖服务器,而是用于恢复 UI 和解释玩家当时在做什么。
战斗中可以让本地角色短暂播放惯性或防御姿态,但不要继续释放技能。镜头可以保持可动,避免玩家觉得游戏死了。冻结策略要按玩法定,竞技游戏更保守,合作探索可以宽松一点。
重连请求的幂等性
重连过程中可能重复发送认证、快照请求、队伍状态请求。请求要带 session_id 和 reconnect_seq,服务端能识别重复。客户端收到旧 seq 的响应要丢弃,避免慢响应覆盖新状态。网络恢复时最怕乱序,尤其是移动网络从 Wi-Fi 切到蜂窝后,旧连接和新连接可能交错。
Godot 客户端可以在 NetworkSession 层统一管理 seq。业务系统只订阅“当前重连序列”的结果,不直接处理底层包。这样重连多次失败再成功时,旧结果不会把 UI 拉回错误状态。
快照应用的安全点
世界快照最好在安全点应用。比如角色正在播放受击表现,快照说已经死亡,可以立即进入死亡;但如果正在切场景,先完成场景加载再应用实体。UI 数据也要等对应系统 ready。可以有一个 ResyncCoordinator,收集各系统 ready 信号:SceneReady、EntityReady、InventoryReady、QuestReady。全部满足或达到降级阈值后解锁。
某个非关键系统失败时,不一定阻止回到游戏。比如排行榜状态拉取失败,可以稍后刷新;玩家位置和副本阶段失败,则必须阻止。关键路径要写清楚。
断线期间的请求结果
断线前发出的购买、领取、匹配取消,可能在服务器已经成功,但客户端没收到回执。重连后要查询这些 pending request 的最终状态。不要简单重发,特别是购买和领取奖励。每个高风险请求要有 client_request_id,服务端按 id 幂等处理。客户端重连后用 id 查询,得到成功、失败或未知。
未知状态要保守。购买未知时不要显示已到账,也不要让玩家重复购买而无提示。可以显示“正在确认上一笔操作”,直到服务端给出结果或客服路径介入。
观测和玩家沟通
重连失败日志要包括断线原因、网络类型变化、重试次数、认证结果、快照大小、应用耗时、失败系统。玩家提示则要简单:正在重连、正在同步世界、同步失败可重试或返回大厅。不要把错误码直接扔给玩家,但日志里必须有错误码。
工程边界要写在代码之前
断线重连最怕“先能跑再说”。能跑的脚本往往把网络会话、世界快照、输入锁和 UI 提示混在一个节点里,短期看起来省事,后期每个 bug 都要跨 UI、资源、网络和玩法一起查。开工前先写清楚边界:认证、重同步、差异应用和输入解锁分别由谁负责,谁只读数据,谁可以提交状态变化,谁只能播放表现。边界清楚以后,新增需求通常只是加一个策略或 profile,而不是改一串互相调用的节点。
在 Godot 里,这个边界可以通过 Resource、autoload 服务和场景节点组合表达。Resource 保存可调规则,autoload 提供跨场景的状态和队列,场景节点负责当前画面表现。不要把全局状态藏在某个 UI 控件或临时子节点里。只要场景一切换,这类状态就会丢,问题还很难复现。
失败恢复要比成功路径先评审
成功路径通常很顺:玩家点击、系统执行、界面刷新。真正决定质量的是失败路径。战斗断线、Loading 断线、奖励请求未知、token 过期这些情况都不是边角料,而是实际测试和上线后最容易出现的问题。每个失败都要回答三个问题:当前状态是否还能继续,是否需要回滚,玩家需要知道什么。没有答案时,就不要把功能当作完成。
失败恢复还要避免二次伤害。比如恢复时又触发一次旧请求,清理时误删仍在使用的资源,回滚时把玩家新操作覆盖。可以给关键操作加 transaction id 或 version,恢复时只处理当前版本。旧回调、旧异步任务、旧动画事件到达时,如果版本不匹配就丢弃。这个小机制能挡住很多偶现问题。
性能预算不能等卡顿后再补
断线重连通常不是单次成本大,而是高频、叠加或峰值明显。预算要写成数字:每帧最多处理多少对象,每次扫描最多多少毫秒,本地队列最多多少条,缓存最多占多少空间,失败重试间隔如何退避。没有数字时,团队会凭感觉加功能,直到某个场景突然掉帧或磁盘暴涨。
预算也要有降级策略。低端设备、后台恢复、弱网、资源不完整时,系统应该知道哪些表现可以降低,哪些规则必须保持。表现层可以降,权威状态不能乱;调试信息可以少,关键错误不能吞;刷新频率可以降,玩家资产和输入边界不能省。预算不是单纯砍功能,而是把优先级提前讲清楚。
团队协作需要工具,而不是口头约定
断线重连经常跨程序、美术、策划、QA 和运营。只靠口头说“这个资源别这么配”“这个按钮别这样关”很快会失效。更可靠的是做小工具:编辑器检查、运行时调试面板、资源报告、状态导出、固定压测场景。工具不一定复杂,但必须让非程序也能看到问题所在。
例如检查器可以扫出缺字段、错误引用、超过预算的资源、不可回滚的状态;调试面板可以显示当前 profile、版本、队列、耗时、错误码;固定测试场景可以一键复现高峰。工具越早出现,团队越容易在内容制作阶段修问题,而不是等集成测试时集中爆炸。
上线验收清单
上线前至少检查这些项:正常路径是否稳定,失败路径是否可恢复,切场景和切后台是否安全,低帧率或弱网下是否有明确降级,日志是否能定位问题,玩家提示是否具体,配置缺失时是否保守,旧版本数据是否兼容,重复操作是否幂等,调试开关是否不会进入正式表现。这个清单看起来普通,但每一项都对应真实线上事故。
还要留一个回看机制。上线后一周看聚合指标和玩家反馈,确认失败率、耗时、回滚次数或异常状态是否在预期内。没有指标的功能,只能等玩家投诉。断线重连做得好,不是玩家会夸它,而是它在复杂场景里安静地工作,不把风险转嫁给玩家。
重连成功后的短暂保护期
世界重同步完成后,可以给玩家 0.5 到 1 秒的保护期或输入缓冲清理期,具体看玩法。竞技游戏不一定能给无敌,但至少应清掉断线期间的旧技能输入,避免刚回到 Online 就释放过期动作。合作副本可以短暂免疫环境伤害,让玩家从网络异常中恢复视野。这个保护期要由服务器或权威玩法确认,客户端只负责表现和输入状态,不要本地擅自给无敌。
UI 上可以显示“同步完成”并快速淡出,告诉玩家已经恢复控制。反馈短,但能减少困惑。
推荐的最小实现顺序
第一步,把网络状态从简单 connected bool 改成明确枚举:Online、Degraded、Disconnected、Reconnecting、Resyncing。第二步,在 Disconnected 时锁定高风险输入,并清理战斗输入缓冲。第三步,实现重连后请求完整玩家快照,至少同步场景、位置、生命、背包版本和副本阶段。第四步,再增加实体增量、pending request 查询和差异分阶段应用。
不要一开始就追求无缝。先保证玩家不会在错误状态下继续操作,再逐步减少重连等待时间。稳定的重连宁可多等一秒,也不要让玩家短暂回到游戏后又被大幅拉回。
上线后的维护
重连功能上线后,要关注失败后的去向。玩家是成功回到副本,还是大多被送回大厅,或者直接退出游戏?这些路径比单纯重连成功率更能说明体验。重连失败不可避免,但失败后仍然清楚、可继续,才算客户端状态机合格。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。