6.1 KiB
6.1 KiB
Unity 实际验收:重登与失败重试缺陷专项方案
日期:2026-09-21。遵循《当前阶段范围裁决-仅最小登录.md》,正式地址固定 http://s101.local:18080/。
已复现问题
- S1:输入 not-an-id 后停留 LoginStage、Authorized=false;点击重试仍提交旧 ID,只有重试按钮,没有可编辑输入框。证据:uloop-invalid-state.json、uloop-invalid-retry-state.json、uloop-invalid-retry-ui.json。
- S2/S3:101→101→102→101 均能认证进入 FishingStage,但重登后出现三个 Failed to create Sentry 和 TimerManager already exist。证据:uloop-reentry-error-stacks.json。此前“错误为空”仅为早期采样,不能概括重登后的完整阶段。
- 退出旧身份、Cookie 更换及清空、会话版本增加、旧 Hub 断开已由真实 QuitStage 路径验证。此次不重做认证协议。
根因与方案
- PFLoginAct.LoginAsync 仅在首次输入 ID;ReytryDialog 直接重复 userService.Login,无法修正错误输入。仅配置为 {{input}} 时,在重试提交中重新等待 InputID,复用原单次提交门、取消令牌、输入监听清理和进度显隐。固定 ID 和 Agg/真实渠道入口保持原逻辑。
- RootCtx 重建 Container,ActivitySentryManager 静态类型缓存仍在;InitActivitySentryType 认为类型重复直接返回,新容器没有服务注册。以容器实例作为缓存生命周期边界:更换容器时清空类型映射与类型键缓存,同一容器仍拒绝重复注册。发现入口与显式注册入口都先检查当前容器,避免懒加载跳过注册。缓存不含存档,不写玩家数据。
- QuitAct 已取消全部 Timer 任务,但 SubBoostrap 随重登再 Create 时旧常驻 TimerMgr 仍在。取消后调用已有 Timer.Destroy,沿用该方法立即清空静态引用、在 Unity 帧末销毁对象的行为;不改通用计时器机制。
修改边界
生产文件仅 PFLoginAct.cs、Activity/Sentry/ActivitySentryManager.cs、QuitAct.cs。测试与证据位于 CodexOutput/upserver。保留其他未提交差异;不改正式地址、服务器、账号映射、KV、俱乐部扩展、PlayFab SDK、渠道链接或存档。不将停止 PlayMode 计为游戏内退出通过。
完整评审裁决(实施前)
本轮主会话基于真实调用链完成方案评审,不冒充新的独立 agent 评审。
- 范围:三项均直接阻碍失败重试/退出重登的本阶段验收,属于必要修复,允许按上述边界实施。
- 取消与重入:输入等待沿用 LoginLifetime,重试门在等待前设置;输入完成后再次检查取消;旧重试回调不得开启新输入或认证。
- UI:SimpleDialog 调用回调后关闭自身;输入面板是独立对象,可以等待用户重新提交。固定 ID 不弹输入。
- 容器隔离:类型缓存随当前 GContext.container 变化失效;不保存或复制旧容器实例内的服务,不用重复注册覆盖同一容器。
- 退出顺序:原提前注销、await RT 释放、CancelAllRegisteredTimers 顺序保留,随后销毁 TimerMgr;异常/未授权退出不得清存档。
- 验证:增补实际生产流程提取测试(输入重试、等待中注销、重复按钮),对容器替换及重复注册做真实源文件回归,退出测试断言销毁;Unity 编译后重新执行无效 ID→重试失败→101成功及101→102→101。
- 风险边界:活动订阅和更广泛静态单例不在本次主动重构范围;若仍有新异常,单列证据另行裁决。网络断连和真实渠道 SDK 未测则继续待验收。
裁决:方案评审通过,可以实施;本文件先于生产代码修改落盘。运行结果及限定代码 review 另行记录。
补充裁决:退出后 PlayFab 缓存上传保护(实施前)
- 新证据:uloop-fixed-relogin-errors.json。在101同号重登时,UserService.Logout已经ForgetAllCredentials,RootCtx销毁触发PlayFabMgr.OnDestroy→SyncData→UpdateUserData,SDK同步抛出未登录异常。Sentry/Timer旧错误未再出现,但S2暂不能标完整通过。
- 方案:PlayFabMgr.SyncData进入时检查SDK登录状态,未登录直接返回,不轮转/清空待同步缓冲;OnDestroy取消周期任务后保存PlayerPrefs,再走有登录保护的SyncData。UpdateUserDataValue现有按账号LocalData/LastUpdateTime落盘逻辑保持不变。PlayerPrefs.Save写本地已有值,不清空、重置、改账号或新增远端写入。
- 评审:不推迟退出身份失效,不临时恢复旧凭据,不吞掉仍已登录时的上传异常。保护覆盖销毁与周期同步在注销空窗中的调用;保存本地已写入的键。对未登录有待同步数据的路径做队列不变、零远端调用、取消与本地Save回归。
- 开发边界仅新增PlayFabMgr.cs两处保护及CodexOutput测试。属于本轮退出必要兼容,不是启动KV/存档迁移。原有60秒比较策略、网络失败补传、完整存档隔离不在此补丁内,不能宣称保证云端最终一致性。
- 主会话完整差异方案评审结论:通过,允许实施;需要重新编译并复测连续重登。新补丁未经独立agent复审,不沿用旧R1复审冒充。
补充裁决:退出后的首页延迟回调(实施前)
- 最终连续回归在同号重登停下,原因是HomePanel.LastShowPanel在等待0.5秒后,通过新的/已拆卸的GContext.container继续找IFaceUIService;旧首页已销毁。证据uloop-final-relogin101-errors.json,前三类异常均未再出现。
- 仅修改HomePanel.LastShowPanel:进入时拒绝已销毁面板,记录当前容器;等待后校验Unity对象仍存在且容器仍为同一实例,才调用原显示逻辑。不更改首页奖励/活动规则、不新增玩法,不用延长测试等待来规避缺陷。
- 评审:符合退出后迟到回调隔离及首页最小保护范围;容器引用仅保存在短异步方法内;原正常面板与同容器路径保持。生产新增边界仅HomePanel.cs这一方法,测试覆盖正常显示、等待中销毁、等待中容器替换。
- 主会话方案完整评审通过,可以实施。所有本轮运行时补丁统一做最后限定review,不冒充已获独立复审。