Files
youle_cocos/docs/superpowers/specs/2026-09-05-startup-config-account-design.md
T

192 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 启动配置与游客账号来源统一设计
日期:2026-09-05。核实基线:`master@286a8b9c165ff2b3eed6af449804490fb2673762`。
状态:配置统一方向已讨论;本文为待用户审阅的书面设计,不是实施计划,不表示代码或真实登录验收已完成。
## 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、不自行处理登录成功导航。
目标顺序:
1. 应用根取得明确的构建运行模式、hostKind、原始查询参数以及本次联调目的,校验启动选择。
2. 构造 ViewPort/FrameScheduler/PlatformUiController;加载画面本身已经可用。
3. 构造 PlatformRuntime,将 controller 作为 ScenePort;controller.connect(runtime) 后调用 runtime.start()。
4. Runtime 显示 loading,并行启动资源加载、最短展示计时和唯一配置解析。
5. resolveRuntimeConfig 成功后校验 config.identity.gameid 与必需 GameEntry.gameId 严格一致,再创建唯一 WireClient 并连接。
6. resources、config、socket、minimum-display 四项全部就绪,Runtime 请求显示 login。start() Promise 完成不能替代 socket 就绪证据。
7. 用户触发游客登录,账号来源读取或生成完整账号;宿主环境来源按原规则处理省市覆盖,形成最终 LoginAccountIdentity,同时准备对应的 LoginDeviceSnapshot。
8. controller.login(account) 原样转发。Runtime 通过 getLoginDeviceSnapshot 获取本次快照,调用既有 buildLoginRequest,交由 WireClient 发送。
9. 唯一 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. 验收与真实服务门槛
来源整理验收:
1. 本地启动选择不满足 debug/h5/local 时,在任何网络操作前拒绝;local 解析不调用远程 fetcher,服务器地址不在下游重复定义。
2. 覆盖顺序、字段类型、gameid 不可被 query 覆盖、原生缺失身份显式失败均有测试。
3. 以注入随机序列验证游客七字段和头像映射;缓存缺失生成、重复读取复用、损坏/版本错误/存储失败拒绝、作用域隔离和版本升级复用均有测试。
4. 机器标识持久化、账号重建不改变机器身份、无 returnCitySN 省略 ip、有 returnCitySN 覆盖省市、非法输入拒绝均有测试。
5. 用真实 buildLoginRequest 联合验证 sources 输出:app/route/rpc 与字段类型一致,version 为 number,手机号/playerid 不被意外加入。无 mock_openid 或身份兜底。
6. 每项按 TDD → 规格审查 → 质量审查 → 修复复验推进,实施使用 subagent-driven-development。
7. 回归显式枚举 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。
8. 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 验收是三个不同的交付结论,必须分别提供证据。