From d79bed0253caab0d1eaf151812877dd705fa76d3 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Sat, 5 Sep 2026 21:28:52 +0800 Subject: [PATCH] docs: record remote configuration source and browser evidence --- docs/protocol/01-传输层与架构.md | 12 ++++-- .../plans/2026-09-05-remote-config-memory.md | 2 +- ...6-09-05-remote-config-memory-acceptance.md | 37 +++++++++++++++++++ .../2026-09-05-remote-config-memory-design.md | 10 +++-- 4 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 docs/superpowers/reports/2026-09-05-remote-config-memory-acceptance.md diff --git a/docs/protocol/01-传输层与架构.md b/docs/protocol/01-传输层与架构.md index 5cb8595..5846f2a 100644 --- a/docs/protocol/01-传输层与架构.md +++ b/docs/protocol/01-传输层与架构.md @@ -10,10 +10,10 @@ ``` ┌─────────────┐ 配置请求(HTTP) ┌──────────────────┐ -│ 新前端 │ ─────────────────► │ 配置服务器 │ 返回 update_json (含 urlserver) +│ 新前端 │ ─────────────────► │ 配置服务器 │ 返回 update_json (身份层级配置) │ (Cocos) │ ◄───────────────── │ (gameserver 地址) │ └─────────────┘ └──────────────────┘ - │ ws://ip:port (拿到 urlserver 后) + │ ws://ip:port (解析服务器配置后) ▼ ┌─────────────────────────────────────────────────────┐ │ 大厅服务器(agent) ←切换→ 房间服务器(room) │ @@ -47,7 +47,10 @@ WebSocket 在 `min_tcp(config)`(`00_minhttp.js:256`)内创建: var ws = new WebSocket("ws://" + config.ipport); // 00_minhttp.js:258,例: ws://127.0.0.1:5414 ``` - `config.ipport` 来自 `GameData.Server`(`12_Logic.js:856 / 900 / 941` 等连接函数把 `server` 赋给 `config.ipport`)。 -- `GameData.Server` 的来源(netType==0 分支):配置服务器回包 `ServerUrl_Succ` 内 `GameData.Server=_msg.data.urlserver`(`12_Logic.js:549`)。 +- `GameData.Server` 的来源(netType==0 分支):启动实际调用 `get_config`(`12_Logic.js:521 / 1330`),以 POST 空 body 读取配置,URL 为 `gameserver + "?" + min_timestamp()`;`getConfig_Succ` 对响应文本 JSON.parse 后保存到内存 `GameData.serverConfig.data`。这里的 `data` 是客户端包装,远程 JSON 根对象本身不带这一层。 + - `get_paravalue` 按全局 → agent → game → channel → market 查找;每层第一个身份宽松相等的条目生效,只有 truthy 参数覆盖继承值,缺少层级即返回继承值,对象整体覆盖。 + - `getConfig_Succ` 选择 truthy `game_server_tcp`,否则 `player_server_tcp`;`serverType != 0` 时在配置解析之后覆盖为本地地址。debug/release 均读取配置,`isDebugger` 不控制读取开关。 + - 旧 `ServerUrl_Succ`(`12_Logic.js:533`)中的 `data.urlserver` 属于未启用的旧请求路径,不能作为当前启动配置契约。整份远程配置不写磁盘或 localStorage。 - ⚠️ 注意:`Logic.setGameServer()`(`12_Logic.js:1270`)只设置 `Game_Config.Debugger.gameserver`(配置文件 URL),**不**设置 `GameData.Server`;两者是两步、不可混淆。 - 切换房间/大厅服时,`GameData.Server` 被替换为 `_msg.data.roomserver` / `_msg.data.agentserver`(`09_Net.js:509 / 526`)。 - `GameData.Server` 可为**单个字符串,也可为数组**(多候选服务器)。为数组时按 `GameData.serverIndex` 轮询,详见第 7 节。 @@ -176,7 +179,8 @@ if (route === "platform" || route === "agent" || route === "room") { ``` 1. 启动 AppStart → HTTP 请求配置服务器(gameserver=Game_Config.Debugger.gameserver) - → 回包 ServerUrl_Succ → GameData.Server = _msg.data.urlserver (12_Logic.js:549) + → get_config POST 空 body → getConfig_Succ JSON.parse → 内存 GameData.serverConfig.data + → get_paravalue 按身份层级读取 → game_server_tcp 或 player_server_tcp → 显式本地覆盖 2. (netType==0 时) AppStart 启 GameData.TcpTimer = setInterval(..., 3*GameData.timer) = 30s 首连看门狗 (12_Logic.js:485-496):超时则 GameUI.OpenTips("网络状况不好...") 并 Net.ws_tcp.close() 首连成功(onopen)或拿到配置后会 clearTimeout 它。 diff --git a/docs/superpowers/plans/2026-09-05-remote-config-memory.md b/docs/superpowers/plans/2026-09-05-remote-config-memory.md index 9c6d9e2..b3ae160 100644 --- a/docs/superpowers/plans/2026-09-05-remote-config-memory.md +++ b/docs/superpowers/plans/2026-09-05-remote-config-memory.md @@ -33,7 +33,7 @@ - [ ] Write failing tests first. Use the actual legacy get_paravalue extracted from 12_Logic.js in a node:vm sandbox as differential oracle; fixtures contain synthetic identities only. Assert values across all five scopes, false/0/empty values, object replacement, missing levels and first duplicate. Example: `assert.deepEqual(view.getValue('hall_config'), legacy(wrapper, 'hall_config'))`. - [ ] Add startup behavior assertions: for debug local fake fetcher increment calls then return `{player_server_tcp:'remote.example:3088'}`; expect calls=1, source=remote, local servers, raw config retained. release with profile=local must still fetch and use remote candidates. Failed fetch or parse must leave local-host socket calls at zero. No persistence operation is added. - [ ] Run exact focused tests from cocoscreator_projects: `node --import tsx --test --test-concurrency=1 framework-tests/config/remote-config.test.ts framework-tests/config/runtime-config.test.ts framework-tests/config/profiles.test.ts framework-tests/config/local-startup.test.ts`. Record expected assertion failures before production edits. If tsx ENOMEM occurs, repeat identical command with require_escalated. -- [ ] Implement the smallest integrated change. Loop scopes using first identity equality, update inherited value only if truthy, stop when scope is absent. Validate the JSON root and present hierarchy shape; normalize only the chosen server candidates. Runtime always awaits fetch, builds one memory view, parses candidates, then applies profile local servers. Preserve host cancellation guards and stop semantics. +- [ ] Implement the smallest integrated change. Loop scopes using first identity equality, update inherited value only if truthy, stop when scope is absent. Validate the JSON root and present hierarchy shape; normalize only the chosen server candidates. Runtime always awaits fetch and builds one memory view, then selects explicit local servers or calls parseUrlServers(view) for remote profiles. Local does not require unused remote TCP fields. Preserve host cancellation guards and stop semantics. Test real-source-shaped `{visitor_server_tcp:'visitor.example:3088'}`: debug local succeeds only after fetch/parse; release fails for missing game/player TCP, never selects visitor. - [ ] Update dependent tests/fixtures asserting the superseded direct contract; retain all unrelated assertions and add no runtime fallback. All test RuntimeConfig literals must supply the new view via a central test helper where appropriate. - [ ] Run focused tests then full explicitly enumerated TS suite: `$testPaths = @(rg --files framework-tests -g '*.test.ts' | Sort-Object); node --import tsx --test --test-concurrency=1 @testPaths`. Run `node node_modules/typescript/bin/tsc -p tsconfig.framework.json --noEmit` and `node scripts/check-import-boundaries.mjs`. Discover and explicitly name the two platform architecture tests; do not invoke aggregate generators. - [ ] Self-review, stage exact task files, commit. Report RED/GREEN commands, counts, changed files, and concerns in task-1-report.md. Do not touch root controller docs or existing dirty resources. diff --git a/docs/superpowers/reports/2026-09-05-remote-config-memory-acceptance.md b/docs/superpowers/reports/2026-09-05-remote-config-memory-acceptance.md new file mode 100644 index 0000000..8e856a1 --- /dev/null +++ b/docs/superpowers/reports/2026-09-05-remote-config-memory-acceptance.md @@ -0,0 +1,37 @@ +# 远程配置读取与内存解析验收 + +日期:2026-09-05。分支:codex/local-platform-login。设计与计划提交:62328b6。 + +## 范围 + +debug、release 均读取远程 JSON,按原工程身份层级读取参数,仅保存在内存变量。无远程配置文件落盘、localStorage 快照或失败缓存兜底。Login Layer/MainMenu 和其他 RPC 接线不在本批。 + +## 实际浏览器预检 + +- funplay MCP `fp_c4e73df9a6e175c3`:Creator 打开 `G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus`,场景 dirty=false;已有预览窗口 9 位于 `http://127.0.0.1:7456/?profile=local`。 +- `fp_bc76fa976d3b5404`:在该真实 Chromium 预览 origin 使用 POST 空 body 读取源码 DEFAULT_GAMESERVER(附时间戳)。HTTP 200,JSON 文本字符数 31519、根字段 25,agentlist 为数组,无根 data 包装。未保存响应文件,也未输出配置内容。 +- `fp_aadb469bca47420b`:使用源码 BUILD_IDENTITY 比对该次真实响应,匹配 agentid;其下没有匹配的 gameid,故 channel/market 层未进入。按原 get_paravalue 应继承 agent/全局值,不更换身份。 +- 上述仅验证浏览器能读取配置及响应结构;新代码的完整启动验收在实现后单独记录。 + +## 新代码实际边界 + +实现提交 `053302c`。通过 MCP 逐个刷新五个既有 TS 资源,无序列化资源写入;重新加载已有本地预览窗口。 + +- `fp_dc324592cdfbe09c`:实际 LocalPlatformLogin 启动,loading-first-draw=848.4ms,startup-config=905ms,source=remote,最终唯一目标为 ws://127.0.0.1:3088,诊断数据无 rawConfig;resources-ready=907.4ms,最短展示门=1353.5ms,login 页激活=1354.5ms。ready=true,未显示错误。未点击登录,未验收其他 RPC。 +- `fp_5524451aad3e7ff5`:从 Creator import map 导入实际编译的 runtime-config 与 HttpConfigFetcher,在真实 Chromium 中调用两种模式,底层仍使用真实 fetch。debug/local 请求一次,HTTP 200,source=remote,唯一目标为本地;rawConfig===remoteConfig.raw,getValue 可用,游客服务器字段存在;前后 localStorage 内容未变化。 +- 同一调用中 release 也请求一次、HTTP 200;之后因 game_server_tcp/player_server_tcp 缺失明确报错,未创建生产 WebSocket,localStorage 未变化。说明读取和内存解析入口实际执行,不能称 release 正式启动已通过。 +- `fp_a0676df82b7d83ae`:停止实际本地预览后 ready=false、stopped=true、pendingCount=0。 + +## 当前配置来源限制 + +真实响应经新生产解析器确认:当前身份的 game_server_tcp、player_server_tcp 均为 null,visitor_server_tcp 为字符串;hall_config、logimage 为 null。debug/local 在读取与层级解析成功后按原 serverType 语义使用明确本地地址。release 保持显式缺失错误,未切换身份、未选择真实子游戏、未用游客服务器兜底。 + +因此:本地远程读取→内存解析→本地连接→登录页通过;当前 BUILD 身份对应的正式启动因远程配置缺少地址而未通过。本批不修改远程配置服务或服务器。 + +## 自动验证和工作区 + +- `053302c`:纯 TS 显式枚举 606/606;指定 import-boundaries/presentation-boundaries 架构测试 49/49;框架类型与导入边界检查通过。 +- TDD 包含原函数差分对照、local 必须等待读取、读取失败/停止期间零连接、正式地址缺失、不泄露远程配置到诊断等。保留既有错误路径测试的预期 boom 日志。 +- 原有 50 个未跟踪文件 SHA256 全部不变,两项历史 meta 删除仍保留,共 52 项原有改动未处理。没有新资源文件,没有 worktree,没有合并或推送。 +- 独立审查发现 URL 片段会通过 URL 解析却被 WebSocket 构造器拒绝;`58c974e` 已在配置入口拒绝字面 `#`(包含空片段),保留合法 `%23`。该修复 RED 为 5/6,GREEN 为 6/6;仅修改解析器及对应测试,未重复无关全量测试。 +- 最终复审正在进行,结论补充于审查完成后。 diff --git a/docs/superpowers/specs/2026-09-05-remote-config-memory-design.md b/docs/superpowers/specs/2026-09-05-remote-config-memory-design.md index 2d8e45f..653f832 100644 --- a/docs/superpowers/specs/2026-09-05-remote-config-memory-design.md +++ b/docs/superpowers/specs/2026-09-05-remote-config-memory-design.md @@ -17,15 +17,17 @@ getValue 严格复刻 get_paravalue:全局→匹配 agent→game→channel→market;每层首个宽松相等身份匹配;断层保留上层值;只有 truthy 值覆盖,0/false/空串不覆盖;对象和数组整体覆盖,不自动深合并;缺省返回 null。身份 marketid=0 时保留原函数不搜索该层的语义。非法结构显式 ConfigParseError,不修补。 -WebSocket 候选按原 getConfig_Succ 选 truthy game_server_tcp,否则 player_server_tcp。支持原字符串/数组顺序及重复项;在协议适配入口转为 ws/wss URL。候选缺失或非法显式失败,不退回 data.urlserver。原工程明确的 game→player 选择属于来源契约。 +远程 WebSocket 候选按原 getConfig_Succ 选 truthy game_server_tcp,否则 player_server_tcp。支持原字符串/数组顺序及重复项;在协议适配入口转为 ws/wss URL。实际采用远程服务器时,候选缺失或非法显式失败,不退回 data.urlserver 或 visitor_server_tcp。原工程明确的 game→player 选择属于来源契约。 本批不实现 Logic.setConfigInfo 内的 UI 布局、副作用及子游戏 Game_Modify 回调;其所需原始参数均可从统一 getValue 读取。后续界面接线时再按原工程执行各字段的显示转换和来源默认语义。 ## 启动顺序 -身份解析 → 远程读取 → 根结构及层级解析 → 服务器候选解析 → 显式本地服务器选择 → 发布 RuntimeConfig → 后续 Runtime 启动。 +身份解析 → 远程读取 → 根结构及层级解析 → 显式服务器来源选择 → 校验并规范化最终连接候选 → 发布 RuntimeConfig → 后续 Runtime 启动。 -所有 profile 都是 remote。debug 与 release 共用解析器;isDebugger 保持构建模式语义,不控制是否读取。profile=local 是独立的本地服务器选择:仅 debug 使用,远程解析成功后覆盖连接目标为 PROFILES.local 唯一配置的回环地址。release 仍选 prod,不能因 URL 参数误接本地。 +所有 profile 都是 remote。debug 与 release 共用解析器;isDebugger 保持构建模式语义,不控制是否读取。profile=local 是独立的本地服务器选择:仅 debug 使用,远程 JSON/层级解析成功后采用 PROFILES.local 唯一配置的回环地址,不要求未采用的远程 TCP 字段存在。原 getConfig_Succ 在赋值后以 serverType 覆盖地址,并未预先校验未使用的远程值。release 仍选 prod,不能因 URL 参数误接本地。 + +真实数据核对:当前 BUILD_IDENTITY 匹配 agent,但没有其下匹配 game;继承结果的 game/player TCP 均为 null,仅 visitor TCP 有值。这不授权更换身份或使用游客地址。release 应显式暴露缺失;debug local 在成功读取与层级解析后可用明确的本地地址。该规则是固定的来源选择,禁止捕获读取/解析错误后退回本地。 本地诊断入口继续严格验证 DEBUG/H5/profile=local、身份及回环目标;更新“必须 direct”的旧断言。远程读取/解析失败前不得创建 socket、准备账号或发送登录。停止发生在读取期间时,不得在读取完成后启动连接。 @@ -33,7 +35,7 @@ WebSocket 候选按原 getConfig_Succ 选 truthy game_server_tcp,否则 player 1. 自动测试对照原 get_paravalue 实函数,覆盖五层、断层、重复身份、数字/字符串身份、truthy、对象覆盖及不存在字段。 2. debug prod、debug local、release 均实际调用 fetcher;本地目标在读取解析完成后才生效;无缓存读写。 -3. 非 JSON、非法层级、地址缺失/非法、HTTP 失败均明确失败;本地 host 无 socket/登录副作用。 +3. 非 JSON、非法层级、HTTP 失败均明确失败;本地 host 无 socket/登录副作用。实际采用远程地址的模式在 game/player TCP 缺失或非法时明确失败;local 不消费这些字段。 4. 真实 HTTP 读取使用当前配置源与 BUILD_IDENTITY,输出脱敏结构/匹配情况;不输出原始配置及认证信息,不连接其生产 WS。 5. 经 funplay MCP 验证实际 Creator 浏览器预览远程读取成功、配置在内存、最终目标仍为本地;若浏览器受 CORS 等阻止,记录真实阻塞,不能用 Node 成功代替浏览器通过。 6. 显式枚举纯 TS 测试、指定架构测试、类型和导入边界检查。原有 dirty 资源不变。