18 KiB
启动配置与游客账号来源统一设计
日期:2026-09-05。核实基线:master@286a8b9c165ff2b3eed6af449804490fb2673762。
状态:用户已确认书面设计(2026-09-05),进入实施计划阶段;不表示代码或真实登录验收已完成。
1. 目标与范围
先统一“启动 → 加载 → 登录”所需输入的所有权、解析顺序与生成规则,再接入本地真实服务器。每类数据只有一个权威来源,下游只消费已校验的结果。服务器、远程配置服务、原生 settings/WVJB 均保持现有契约。
本设计覆盖启动环境、构建与渠道身份、服务器配置、游客测试账号、缓存、设备快照及加载端口的组合边界。复用已完成的 PlatformRuntime 和 3A Presenter,不重新执行 3A,也不增加第二个 Store、Router 或启动状态机。
本设计不授权修改 .scene/.prefab/.anim/.meta,不选择真实子游戏,不实现房间、其他平台 RPC、微信授权或短信登录。实际 Cocos 接管和测试宿主的资源清单另行批准;生产 GameEntry 仍为必需。
依据:仓库 AGENTS.md、2026-09-04-framework-subgame-zero-coupling-migration-design.md、2026-09-05-platform-ui-wiring-design.md、协议 01/02/04、原工程源码。已完成的 ../plans/2026-09-05-platform-ui-wiring.md 仅作为完成记录。
下文 assets/ 路径均相对 cocoscreator_projects/YouleNexus/;原工程路径相对 projects/Game_Surface_3/。
2. 已核实的当前入口
MCP 在本会话确认 Creator 3.8.8 打开主 checkout 的 YouleNexus,Login 场景 Canvas/LoginFlow_Node 挂载 LoginFlow。
assets/scripts/LoginFlow.ts:调用 resolveBootstrap,但随后为旧 NetClient 硬编码 A/C/G、mock_openid;还存在失败兜底,使用旧 PlatformSession,跳转 MainMenu。这不是新 Runtime 的联调入口。assets/scripts/LaunchFlow.ts:延时一秒模拟加载后切换 Login,不是真实资源加载器。assets/scripts/SceneStart.ts:模拟玩家响应式数据,不是真实登录。assets/framework/platform/startup.ts:旧 StartupOrchestrator,仍依赖旧 Session/NetClient/RoomRPCBus;不进入新的组合链。assets/framework/config/bootstrap.ts:resolveRuntimeConfig 的兼容外观。现代入口直接消费 RuntimeConfig,不从 BootstrapResult.account 获取账号。assets/framework/platform/runtime.ts:现代启动编排已存在。当前 assets 中尚未发现实际构造 PlatformRuntime 的入口,实例组合见 framework-tests。
因此,集中整理 defaults.ts 本身不能消除运行中的硬编码。实际接管时必须保证只有新的应用根拥有启动流程;旧入口退出活跃路径的资源操作须另行授权。本批不提前删除可能仍被场景引用的脚本。
3. 方案选择
采用“单一应用组合入口 + 按来源分类的配置与适配器”,复用三个现有结果契约:RuntimeConfig、LoginAccountIdentity、LoginDeviceSnapshot。
不采用把所有字段放进全局 GameData/大配置对象的方案:玩家账号与实时设备数据会和构建配置混在一起,难以判断何时更新以及由谁负责。
也不采用给每个 profile 复制完整身份、账号、网络参数的方案:会形成第二份 gameid/version 和测试账号来源,后续修改无法保证一致。
统一指唯一所有权和入口,不要求所有实现塞进同一个文件。协议常量仍由现有 constants/contracts 持有。
4. 启动输入和解析顺序
应用根负责读取宿主输入、选择账号来源、提供引擎端口,构造 Controller 和 Runtime 并管理销毁。应用根不拼协议包、不写 Store、不自行处理登录成功导航。
目标顺序:
- 应用根取得明确的构建运行模式、hostKind、原始查询参数以及本次联调目的,校验启动选择。
- 构造 ViewPort/FrameScheduler/PlatformUiController;加载画面本身已经可用。
- 构造 PlatformRuntime,将 controller 作为 ScenePort;controller.connect(runtime) 后调用 runtime.start()。
- Runtime 显示 loading,并行启动资源加载、最短展示计时和唯一配置解析。
- resolveRuntimeConfig 成功后校验 config.identity.gameid 与必需 GameEntry.gameId 严格一致,再创建唯一 WireClient 并连接。
- resources、config、socket、minimum-display 四项全部就绪,Runtime 请求显示 login。start() Promise 完成不能替代 socket 就绪证据。
- 用户触发游客登录,账号来源读取或生成完整账号;宿主环境来源按原规则处理省市覆盖,形成最终 LoginAccountIdentity,同时准备对应的 LoginDeviceSnapshot。
- controller.login(account) 原样转发。Runtime 通过 getLoginDeviceSnapshot 获取本次快照,调用既有 buildLoginRequest,交由 WireClient 发送。
- 唯一 Router/Session 更新 Store;ScenePort 决定页面,Presenter 投影服务器玩家数据。本批验收到登录成功结果,不自动发起其他平台请求。
账号准备失败时不调用 controller.login;存储写入失败时也不发送首次登录。Controller 不缓存半成品账号。一次操作的省市与设备字段来自同一次宿主采样,不能先后异步采样形成不一致组合。
重连继续沿用 Runtime 已保存的登录信封,不在每次 reconnect 时重新生成游客账号。本设计不修改该已验证时序。
5. 启动环境与服务器来源
5.1 环境
启动选择包含 mode、hostKind 和 profile。本批联调目的明确限制为 debug、h5、local;它是独立开发用途,不改变普通发布入口的配置语义。
运行模式由显式构建/宿主输入提供。新入口不依赖 runtime-mode.ts 在 DEBUG 不存在时返回 debug 的隐式行为,也不让 URL 将本地联调用途切为 release/remote。普通入口已有 URL/native 契约不在此批任意收紧。
本地联调选择须在调用配置 fetcher 前校验;解析结果在创建任何 WebSocket 前再校验。必须来自 direct local profile,servers 与 PROFILES.local 的完整列表一致;该列表只允许 loopback WS 地址。验证引用 profiles.ts,不另写一个备用地址。任何后续连接目标也要服从本批本地范围约束,不能通过服务器切换绕开。
5.2 服务器
唯一环境定义:assets/framework/config/profiles.ts。当前 local 为 ws://127.0.0.1:3088;prod 与 staging 均为远程配置且指向同一个 DEFAULT_GAMESERVER,默认 ACTIVE_PROFILE 为 prod。staging 名称不代表独立测试服务器。
唯一解析入口:assets/framework/config/runtime-config.ts。local debug 跳过远程配置;远程模式使用 profile.gameserver 或来源明确提供的 gameconfig,再交给现有 fetcher,解析 data.urlserver。
gameconfig 的现有编码转换为 - → /、# → :,再构成 http://...txt。原工程 js/00_Surface/12_Logic.js 的 get_config 实际为 POST、空 body、无条件追加 ? 与毫秒时间戳,现代 remote-config-fetcher.ts 已与其一致。技能和总设计中 GET 的旧描述不能作为改回 GET 的依据。
远程配置只接受现有 data.urlserver 契约,不猜其他字段、不失败转本地。当前 profiles.ts 的远程 URL 与原工程 00_SubGame_Config.js 的当前值不同,其注释不足以证明二者相同;本批不联网验证或修改远程地址。
6. 构建与渠道身份
字段名称固定为 agentid、gameid、channelid、marketid、version,不引入 chanelid 等别名。
唯一构建来源保留 assets/framework/config/sources/defaults.ts 的 BUILD_IDENTITY。本文不再复制 token,实施与联调从该文件读取;本会话核实 marketid=4、version=1 为 number,其他身份 token 与用户提供的源码值一致。
现有优先级:
- H5:BUILD_IDENTITY → queryStringSource。URL 可以提供 agentid/channelid/marketid/version,不覆盖 gameid。version 按现有 Number 转换与有限数字校验处理。
- native-settings:构建提供 gameid/version,settings 适配器提供 agentid/channelid/marketid。
- uAgent_3:构建提供 gameid/version,现有 app_agent/app_channel/app_market 适配器提供其余身份。
沿用 resolveIdentity 在来源边界对缺省/空覆盖的现有定义;最终必需字段缺失或非法时报错。不得在原生身份缺失时借用 H5 默认渠道。原生适配器已有的历史缺省语义留在来源层,不复制到 Controller/Runtime。
只解析并冻结一个有效身份,所有登录请求引用它。GameEntry 与 config.identity.gameid 的严格一致校验保留,不能绕过。
本地联调若确需覆盖 version 或渠道,使用现有 H5 query 来源明确提供一次,并记录覆盖来源;不在 profile、LoginFlow、Transport 各加一份值,也不未经验证修改公共 BUILD_IDENTITY。实际版本值须来自服务配置或运行证据,不能采用历史 10000 猜测通过。
7. 游客测试账号来源
7.1 输出与算法
新增专用 local visitor provider,输出现有完整 LoginAccountIdentity:openid、unionid、nickname、avatar、sex、province、city。它只在本批 local debug 用途中可用,不是原生/微信授权失败时的替代路径。
原工程依据:05_Func.js 的 visitorLogin、simulatePlayerInfo、sharelogin;js/gameabc.min.js 的 ifast_random 为 parseInt(Math.random()*b)。迁移后的正整数随机范围按 floor(random()*b) 等价实现,random 输入必须满足 0≤值<1。
- r 为 0..99999 的整数;openid=
testopenid_+r;unionid=ylgame+r;nickname=r+_游客。 - avatar 从 simulatePlayerInfo 原有 10 项头像列表抽取,列表在新的来源模块维护一份,调用方不能另备列表或失败替换 URL。
- sex 为 1 或 2;province=
jiangxi;city=nanchang。 - 时钟、随机函数和存储作为 provider 依赖注入,方便验证;不把测试固定值作为运行默认。
原错误 pInfo.openid.headimgurl 不迁移;从生成结果的 headimgurl 明确转换到 avatar。旧缓存中的 Province 是正确历史存储字段,映射为 province,不应误报为损坏。
原随机空间不保证全服唯一。不能声称“生成即新账号”;服务器可能已存在同标识。若回包涉及房间恢复,本批不删除恢复字段、不自动退房或换号重试,而是明确终止该次验证。
7.2 本地测试缓存
本批采用独立 local 测试缓存,不读取或覆盖 tsgame_visitorinfo、tsgame_wxinfo、tsgame_machineId_ 等旧产品键。缓存命名与读写规则集中在 provider 的 storage 模块。
账号键使用一个固定前缀与 JSON 编码的作用域元组;元组包含 profile、有效 servers、agentid、gameid、channelid、marketid,保留数字/字符串类型差别。version 不进入账号键,因此同一作用域升级版本后仍使用原账号。
值采用一个明确版本的缓存记录,包含 schemaVersion=1 与完整 account;读取只接受当前记录形状。记录中不保存可覆盖 RuntimeConfig 的第二份身份配置。
- 键不存在:生成一次,完整校验并成功持久化后返回。
- 键存在且合法:复用;不重新随机化昵称、头像或性别。
- JSON 损坏、schema 不支持或账号字段非法:报出缓存键与字段路径,保留原值,不清除、不重新生成。
- 存储不可读/不可写:明确失败;不退到内存账号继续登录。
- 不自动过期;仅由显式的本地测试账号重建动作替换。该动作须在无活动登录会话时执行,只操作当前作用域账号键,不清浏览器全部存储。
- 单个应用实例对同一账号初始化合并并发调用;本批不承诺多个浏览器标签页并发创建同一账号的协调能力,联调采用单实例。
本地缓存的新规则不宣称兼容原 cookie 有效期。原 visitorLogin 直接 ReadData,而其他入口使用 getCookie 与共享 validtime;正式旧缓存迁移不在本批执行。将来迁移时必须使用显式导入适配器处理 headimgurl/Province 与旧有效期,不能依靠字段猜测导入。
8. 设备与宿主环境来源
输出复用 LoginDeviceSnapshot,保留既有 getLoginDeviceSnapshot 端口。来源负责如下语义,Runtime 和 Presenter 不填默认值:
- machineid:独立本地测试设备键,按 origin 隔离,与账号 scope 分开,换游客账号不换机器身份。缺键时沿用 Logic.getMachineId 的毫秒时间戳 +
-+ (1000+floor(random()*8999)),随机尾数为 1000..9998;成功保存后使用。非法缓存和写入失败显式报错。 - machineroom:本批无房间的宿主状态明确为
"",对应原 Desk.roomcode 初始值。它不是服务器地址或机房配置;不把空串扩展为生产房间恢复的默认值。 - location:本批 H5 未收到定位的来源状态明确为 null;如有合法定位输入则原样保留。不能为非法对象补 null。
- returnCitySN 不存在:不添加 ip,保留账号来源省市。存在:要求 ip/province/city 满足字段契约,按 Net.Send_login 的行为将省市覆盖到本次登录账号,并提供 ip。不能补 127.0.0.1。
- deviceLogin:本批游客明确 enabled=false,不附 telphone/telphoneAuto;不实现短信登录。
- loginPlayerId:本批明确 enabled=false,不附 playerid。服务器下发的 playerid 是运行状态,不变成客户端配置。
provider 内部保留缓存账号原始地区,returnCitySN 覆盖只形成当前提交的有效账号,不把瞬时地理信息反写游客缓存。原生 settings/WVJB 的名称、方向和 payload 不改变;本批的 H5 空能力不能冒充原生验收。
9. 加载与协议策略
loadResources 必须加载本批声明的真实资源并验证绑定,不能用 setTimeout 或空 Promise 冒充。waitForMinimumDisplay 的时长由启动呈现策略显式提供,是从加载页显示开始计算的最短持续时间;应用根负责传入已耗时间后的剩余等待。缺失时长或非法值报错,不能直接引用旧 LaunchFlow 的一秒模拟值。
资源清单、节点绑定和实际展示时长属于后续 Cocos 接管设计的必需输入;本配置来源批次不新建一个空加载器来宣称四门就绪。
协议 APP/Route、收包超时、登录守护、重连间隔沿用 core/constants.ts 和现有协议 builder 的唯一来源,不复制到 profile,不增加自主心跳,也不改变 Runtime 已有重连行为。
10. 实施边界与文件归属
下一份实施计划只安排可独立验证的来源整理:
- config:沿用 runtime-config.ts、identity.ts、profiles.ts、sources/defaults.ts、query-string.ts、native-settings.ts;本地启动选择与范围校验放在明确的应用/联调边界。
- account/device adapters:新增纯 TypeScript 来源模块和 local 存储策略,输出既有 login contracts;不 import cc,不导出到 Game SDK。
- framework-tests:增加来源、缓存、协议拼包联合测试。未接入引擎期间测试夹具仅留在测试目录。
- 文档:记录来源归属、上述原工程证据及 GET/POST、version 类型的旧描述差异。更新旧说明时按精确范围修正,不改协议行为。
本批不删除 resolveBootstrap/StartupOrchestrator,不修改仍挂载的 LoginFlow。实际新应用根、严格 WebSocket adapter、Cocos View/资源接管需要在后续范围明确后实施,不能把来源模块测试通过称为接管完成。
GameEntry 隔离策略也是后续真实 Runtime 宿主组合的前置条件。本设计不批准此前讨论的测试 GameEntry 落点,不把生产 GameEntry 改为可选,也不要求当前选定真实子游戏。
代码实施创建新隔离 worktree/分支;不得重建已清理的旧工作树。编辑器仍在主 checkout,写入隔离工作树的 TS 不表示编辑器已加载新代码。任何编辑器切换、导入、资源写入均按 AGENTS.md/MCP 和精确授权处理。
11. 验收与真实服务门槛
来源整理验收:
- 本地启动选择不满足 debug/h5/local 时,在任何网络操作前拒绝;local 解析不调用远程 fetcher,服务器地址不在下游重复定义。
- 覆盖顺序、字段类型、gameid 不可被 query 覆盖、原生缺失身份显式失败均有测试。
- 以注入随机序列验证游客七字段和头像映射;缓存缺失生成、重复读取复用、损坏/版本错误/存储失败拒绝、作用域隔离和版本升级复用均有测试。
- 机器标识持久化、账号重建不改变机器身份、无 returnCitySN 省略 ip、有 returnCitySN 覆盖省市、非法输入拒绝均有测试。
- 用真实 buildLoginRequest 联合验证 sources 输出:app/route/rpc 与字段类型一致,version 为 number,手机号/playerid 不被意外加入。无 mock_openid 或身份兜底。
- 每项按 TDD → 规格审查 → 质量审查 → 修复复验推进,实施使用 subagent-driven-development。
- 回归显式枚举 framework-tests/**/*.test.ts,指定 architecture/import-boundaries.test.mjs 与 architecture/presentation-boundaries.test.mjs,并执行 typecheck、导入边界、git diff --check。运行前排查测试资源写入副作用;禁止 scripts/run-framework-tests.mjs、npm test 和含旧资源生成器的 test:framework。
- tsx ENOMEM 按环境问题处理,在允许环境运行相同精确命令,不改业务逻辑。工具路径和版本实施前实测。
真实服务器验收另记:当前已确认本地 3088 监听和服务程序路径,尚无本轮 player_login 成功回包。本地源码会忽略缺 openid/unionid 或 nickname/avatar 同时为空的请求,并比较身份作用域对应 game_version;agent/game 信息还依赖运行配置与数据库。字段齐全不等于该身份被接受,TCP 可达不等于登录成功。
来源整理通过后,在批准的现代 Runtime 宿主中取得真实登录请求、响应和 Store 证据;然后取得 Creator 实际加载与交互证据。非零结果保留原错误,不调服务器、不补字段、不猜版本。房间和其他平台 RPC 留到后续批次。
12. 本次文档交付检查
本次只新增此设计文件,不改运行代码或资源,不发送登录请求。主 checkout 已有 41 个未跟踪 .meta 和 2 个删除 .meta,应按本轮开始时的状态与内容哈希保护,提交只包含本文。
本文经自审后交用户确认,确认后再编写实施计划。来源整理完成、真实登录通过、实际 Cocos UI 验收是三个不同的交付结论,必须分别提供证据。