Files
youle_cocos/docs/guides/配置与调试指南.md
T
joywayerandClaude Opus 5 02dc5d51ca chore(spec): 综合清理 + legacy-layer 迁移 spec/plan/data
主要改动:
- 切到 funplay-cocos-mcp v0.5.1 (用户级配置, 项目级 .mcp.json 删除)
- 仓库文档/CLAUDE.md/.gitignore 等清理过时 cocos-mcp-server 引用
- memory 文件同步: cocos-mcp-setup/path/blocker/spriteframe-uuid/prefab-persist 等加 funplay 实测警告
- memory 新建 funplay-cocos-mcp-pending-verification.md (后已被实测覆盖)
- spec/plan/data:
  - docs/superpowers/specs/2026-09-02-legacy-layer-migration-design.md
  - docs/superpowers/plans/2026-09-02-legacy-layer-migration.md
  - docs/superpowers/data/layer-spirit-summary.json
- YouleNexus: profiles.ts / defaults.ts / PlayerInfoView.prefab / scene 改动

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 07:36:54 +08:00

27 KiB
Raw Blame History

YouleNexus 配置与调试指南

本指南面向在新 Cocos 前端 YouleNexus 上做开发联调的工程师,讲清楚两件事:

  1. 配置——渠道身份(agentid/channelid/gameid/marketid/version)从哪来、远程配置文件怎么读、服务器地址怎么定。
  2. 调试——如何一键切到本地/测试服、临时替换渠道身份、跑自动化测试、做真机端到端联调。

对应实现位于 cocoscreator_projects/YouleNexus/assets/framework/config/,设计依据见 docs/superpowers/specs/2026-06-28-config-channel-design.md。

一句话心智模型:启动时调用 resolveBootstrap(...),它把「构建期默认 + 运行期来源 + 调试 profile + URL 覆盖」合并成最终 identity,再决定 servers(调试服直连 或 远程配置解析),产出 { identity, servers } 喂给 NetClient。


目录

  1. 快速上手(三个最常见场景)
  2. 整体数据流
  3. 渠道身份 ChannelIdentity
  4. 一键切换 debug / release 模式
  5. 调试 Profiles
  6. URL Query 参数清单
  7. 服务器地址是怎么定的
  8. 远程配置文件
  9. 启动编排 resolveBootstrap 与接入 NetClient
  10. 原生联调(window.settings / app_*)
  11. 跑测试与类型检查
  12. 常见调试场景配方
  13. 真实服务器端到端联调
  14. 发布前检查清单
  15. 红线:哪些字符串绝不能改
  16. API 速查表

1. 快速上手(三个最常见场景)

下面假设你在 Cocos 编辑器预览(浏览器/WebView 环境)下调试,地址栏可加 query 参数。

我想…… 怎么做
连本地服务器(ws://127.0.0.1:3088) 预览 URL 加 ?profile=local。直接连本地,不抓远程配置。
用测试配置服(staging 的 gameserver) 预览 URL 加 ?profile=staging。
临时换一个渠道身份调一个 bug URL 加 ?agentid=xxx&channelid=yyy&marketid=2,覆盖最高优先级,其余不变。

更多组合见 §12 常见调试场景配方。


2. 整体数据流

                       ┌─────────────────────────────────────────────┐
启动调用                │            resolveBootstrap(opts)            │
resolveBootstrap ─────► │                                             │
                       │  ① resolveActiveProfile(search)             │
                       │      └─ ACTIVE_PROFILE 或 ?profile= 切换     │
                       │                                             │
                       │  ② 合并身份 resolveIdentity([...])           │
                       │      defaults  <  运行时(native|query)       │
                       │              <  profile.identity            │
                       │              <  显式 URL query (最高)        │
                       │                                             │
                       │  ③ 决定服务器:                              │
                       │      profile.server ?  → 直连(跳过远程)      │
                       │      否则 fetcher.fetch(gameserver)         │
                       │            → getParam 分层取参              │
                       │            → resolveServers → ws:// 候选     │
                       └───────────────┬─────────────────────────────┘
                                       │  { identity, servers, rawConfig, profile }
                                       ▼
                         net.setIdentity(identity 相关字段)
                         new NetClient({ servers }) → net.start()

文件分工(每个文件单一职责,纯逻辑与 IO 分离,均可在 Node 下测):

文件 职责
config/identity.ts ChannelIdentity 类型、IdentitySource 接口、resolveIdentity() 纯合并
config/sources/defaults.ts BUILD_IDENTITY 构建期默认(旧 version.js 等价物)
config/sources/query-string.ts queryStringSource():H5 从 URL query 读身份
config/sources/native-settings.ts nativeSettingsSource():原生从 window.settings/app_* 同步读身份
config/profiles.ts DebugProfile / PROFILES / resolveActiveProfile() 调试档位
config/remote-config.ts 远程配置类型、ConfigFetcher 接口、getParam() 分层取参、resolveServers()
config/remote-config-fetcher.ts 生产用 HttpConfigFetcher(全局 fetch 抓取 txt)
config/runtime-mode.ts resolveRuntimeMode() 运行模式(debug/release)解析,MODE_OVERRIDE 手动覆盖
config/bootstrap.ts resolveBootstrap() 启动编排,串起以上全部

3. 渠道身份 ChannelIdentity

最终要发给服务器 player_login 的渠道身份字段。属性名就是协议字段名,逐字不可改(见 §15)。

interface ChannelIdentity {
  agentid: string | number;   // 代理商 ID
  channelid: string | number; // 渠道 ID
  gameid: string;             // 游戏标识(每个子游戏固定)
  marketid: string | number;  // 市场/包渠道 ID
  version: string;            // 版本号(旧 GameData.Version)
  versionCode: number;        // 版本码(旧 GameData.versionCode)
}

各字段从哪来

字段 主来源 说明
gameid 编译期固定(BUILD_IDENTITY,每子游戏不同) 旧工程写死在 version.js。一般不随环境变。
agentid 运行期:H5 走 URL query,原生走 window.settings.getothername('agent') 默认值在 BUILD_IDENTITY,运行期覆盖。
channelid 运行期:H5 走 URL query,原生走 window.settings.getchannelName() 同上。
marketid 运行期:H5 走 URL query,原生走 window.settings.getmarketname()(默认 4) 同上。
version / versionCode BUILD_IDENTITY 默认;H5 可用 ?version= 覆盖 —

合并优先级(低 → 高,后者覆盖前者)

BUILD_IDENTITY(构建期默认)
   < 运行时来源(原生环境=nativeSettings | H5 环境=queryString)
   < profile.identity(当前调试档位的身份覆盖)
   < 显式 URL query(最高,方便临时改单字段)

resolveIdentity(sources) 按数组顺序合并,只覆盖"有效值"字段——undefined/null/空字符串 不覆盖(所以一个来源读不到某字段时,不会把已有值清空);注意 0 是有效值会覆盖(如 marketid=0)。

H5 模式下 queryString 同时担任「运行时来源」和「最高覆盖」两个角色,因此在 URL 里写 ?agentid=... 永远生效且优先级最高。


4. 一键切换 debug / release 模式

运行模式是比 profile 更高一层的总开关,由 config/runtime-mode.ts 提供。

模式 怎么进入 profile URL 覆盖(?profile=/?agentid=…) 身份来源 收发包日志
debug debug/预览构建自动进入;或 MODE_OVERRIDE='debug' ?profile= 可切(默认 prod) 全部生效 原生或 URL(按 isNative) 开(isDebugger=true)
release release 构建自动进入;或 MODE_OVERRIDE='release' 强制 prod 全部忽略 只走原生 window.settings 关

三种切换姿势

  1. 什么都不动:发 release 包自动 release,编辑器预览自动 debug。模式由 Cocos 全局 DEBUG 决定。
  2. release 包里临时调试:把 config/runtime-mode.ts 的 MODE_OVERRIDE 设为 'debug'。
  3. 预览里验证正式行为:把 MODE_OVERRIDE 设为 'release'。

release 之所以能放心锁死 URL:正式包是 Cocos 原生(jsb),渠道身份走 window.settings/app_*,本就不依赖 URL。

接入

import { resolveRuntimeMode } from 'db://assets/framework/config/runtime-mode';

const isDebugBuild = typeof DEBUG !== 'undefined' ? DEBUG : true; // Cocos 全局
const mode = resolveRuntimeMode(isDebugBuild);                    // 'debug' | 'release'
// 传给 resolveBootstrap({ mode, ... }),并把 result.isDebugger 传给 NetClient 的 debug 选项

5. 调试 Profiles

Profiles 取代了旧工程的 Game_Config.Debugger(serverType / gameserver / isDebugger)。一个 profile 打包了「连哪个服 + 用什么渠道身份 + 日志开关」。

interface DebugProfile {
  name: string;
  server?: string;                     // 显式 ws 调试服地址;给定则跳过远程抓取(纯直连)
  gameserver?: string;                 // 远程配置 txt URL(不含防缓存串)
  serverOverride?: string;             // debug:抓远程配置走全链路后,把连接地址覆盖为此(联调本地游戏服)
  identity?: Partial<ChannelIdentity>; // 调试用渠道身份覆盖
  account?: DebugAccount;              // 调试用登录账号(openid 等),仅 debug 模式生效
  isDebugger?: boolean;                // 收发包日志开关
}

// DebugAccount:player_login 中除渠道身份外的字段(openid 必填,其余可选)
// { openid, nickname?, avatar?, sex?, province?, city?, unionid? }
// 联调账号写在这里(profile 是唯一权威来源),不要散在调试脚本里。

内置档位(config/profiles.ts)

profile server(直连) gameserver(远程配置) isDebugger 用途
prod — http://ylyxservice1.0791ts.cn/config/update_json.txt false 正式发布。走远程配置解析服务器。
staging — http://testgame.youlehdyx.com/update_json/ceshi_json.txt true 测试配置服。
local ws://127.0.0.1:3088 — true 本地服务器直连,不抓远程。

如何切换

  • 默认档位:ACTIVE_PROFILE(当前为 'prod'),在 config/profiles.ts 顶部常量。
  • 临时切换:URL 加 ?profile=<name>,例如 ?profile=local、?profile=staging。未知名字会回退到 ACTIVE_PROFILE。
resolveActiveProfile('?profile=local')  // → PROFILES.local
resolveActiveProfile('')                // → PROFILES.prod(ACTIVE_PROFILE)
resolveActiveProfile('?profile=xxx')    // → PROFILES.prod(未知回退)

加一个自己的档位

直接在 PROFILES 里加一项即可,例如连组内某台联调机:

export const PROFILES: Record<string, DebugProfile> = {
  prod:    { name: 'prod',    gameserver: DEFAULT_GAMESERVER, isDebugger: false },
  staging: { name: 'staging', gameserver: STAGING_GAMESERVER, isDebugger: true },
  local:   { name: 'local',   server: 'ws://127.0.0.1:3088',  isDebugger: true },
  // 新增:直连联调机 + 指定测试渠道身份
  lab:     { name: 'lab',     server: 'ws://192.168.1.50:3088', isDebugger: true,
             identity: { agentid: 'test_agent', channelid: 'test_channel' } },
};

之后用 ?profile=lab 即可。


6. URL Query 参数清单

预览/调试时可在地址栏拼接这些参数(H5 环境生效):

参数 作用 示例
profile 切换调试档位 ?profile=local
agentid 覆盖代理商 ID(最高优先级) ?agentid=00bA05...
channelid 覆盖渠道 ID ?channelid=frdt0C...
marketid 覆盖市场 ID ?marketid=2
version 覆盖版本号 ?version=1.2

多个参数用 & 连接:?profile=staging&agentid=xxx&marketid=2。

gameid 不从 URL 取(按子游戏固定在 BUILD_IDENTITY)。


7. 服务器地址是怎么定的

resolveBootstrap 决定 servers(一个 ws:// 地址数组,交给 NetClient 轮询)的顺序:

  1. 当前 profile 有 server → 直接用它,跳过远程配置抓取(local/lab 这类纯直连档位走这条)。
  2. 否则抓 profile 的 gameserver 远程配置文件,用 getParam 取出连接参数,resolveServers 组装候选列表。
    • debug 且 profile 有 serverOverride:仍抓远程配置走完整链路(rawConfig 照常返回),但最终连接地址覆盖为 serverOverride(联调时把游戏服连到本地测试服)。release 一律忽略 serverOverride,用远程解析结果。
  3. 不兜底(CLAUDE.md 第二准则):profile 既无 server 又无 gameserver、远程抓取失败、或解析不出任何地址——一律抛 ConfigFetchError 显式暴露,不猜默认、不降级。重试/重连归 NetClient,不在本层造兜底轮子。

resolveServers 取的连接参数键(逐字一致原工程,按此优先级):

player_server_tcp  →  visitor_server_tcp  →  game_server_tcp

值形如 ip:port,自动加 ws:// 前缀(已带 ws:///wss:// 的不重复加),并去重。

为什么只取 *_tcp 不取 *_http:YouleNexus 是 WebSocket-only(对应旧 netType==0 分支)。旧工程的 *_server_http 属 HTTP 传输模式(netType==1),本期不在范围,故有意省略。


8. 远程配置文件

是什么

一个放在配置服务器上的 .txt 文件(内容是 JSON),URL 即 profile 的 gameserver。抓取时自动追加防缓存时间戳:<gameserver>?<timestamp>。

生产抓取实现 HttpConfigFetcher(config/remote-config-fetcher.ts)用全局 fetch GET 该 URL 并 JSON.parse,解析失败抛 ConfigParseError。

结构与分层取参 getParam

远程配置是分层覆盖结构,越具体的层级优先级越高:

data                          ← 顶层默认
  └─ agentlist[ {agentid}      ← 按 agentid 匹配
       gamelist[ {gameid}      ← 再按 gameid 匹配
         channellist[ {channelid}   ← 再按 channelid
           marketlist[ {marketid} ]  ← 最后按 marketid
       ]]]

getParam(config, key, id) 用当前身份(agentid/gameid/channelid/marketid)逐层下钻,命中的最具体层若带该 key 就更新返回值,匹配链中断则返回当前已找到的最具体值(最终可能是顶层默认或 null)。这与旧工程 get_paravalue 语义逐字一致。

示例:

{
  "data": {
    "player_server_tcp": "1.1.1.1:1000",          // 顶层默认
    "agentlist": [{
      "agentid": "A",
      "player_server_tcp": "2.2.2.2:2000",        // A 代理商覆盖
      "gamelist": [{
        "gameid": "G",
        "channellist": [{
          "channelid": "C",
          "player_server_tcp": "3.3.3.3:3000"     // A/G/C 这条最具体 → 命中它
        }]
      }]
    }]
  }
}

身份 {agentid:'A', gameid:'G', channelid:'C', ...} → getParam(cfg,'player_server_tcp',id) 得 3.3.3.3:3000;若 channelid 换成没配的值,则回退到 2.2.2.2:2000;agentid 也没配则回退顶层 1.1.1.1:1000。

平台/UI 类参数(公告 menunotice、跑马灯 scrollmsg、客服 service_*、登录图 logimage、排行榜 rankList 等)复用同一个 getParam,将在后续 platform 层接入;本期只解析了连接相关的服务器地址。


9. 启动编排 resolveBootstrap 与接入 NetClient

签名

interface BootstrapOptions {
  mode: 'debug' | 'release';  // 运行模式(由 resolveRuntimeMode 决定)
  win: NativeHost;            // 原生注入宿主(≈ window);H5 下传 globalThis/window 即可
  search: string;            // location.search(如 '?profile=local')
  fetcher: ConfigFetcher;    // 远程配置抓取;生产传 new HttpConfigFetcher()
  isNative: boolean;         // 环境判定:原生 WebView=true,H5/浏览器=false(由调用方算)
  cacheBust?: () => string;  // 防缓存串注入(默认 String(Date.now()))
}

interface BootstrapResult {
  identity: ChannelIdentity;
  account?: DebugAccount;          // 调试登录账号(仅 debug 模式有值;release 恒 undefined)
  servers: string[];
  rawConfig: RemoteConfig | null;  // 直连档位为 null
  profile: DebugProfile;
  mode: 'debug' | 'release';      // 透传入参 mode
  isDebugger: boolean;             // 是否开启收发包日志(= mode === 'debug')
}

function resolveBootstrap(opts: BootstrapOptions): Promise<BootstrapResult>;

典型接入(生产 / H5 预览)

import { resolveBootstrap } from 'db://assets/framework/config/bootstrap';
import { resolveRuntimeMode } from 'db://assets/framework/config/runtime-mode';
import { HttpConfigFetcher } from 'db://assets/framework/config/remote-config-fetcher';
import { NetClient } from 'db://assets/framework/net/net-client';
import { CocosWebSocketTransport } from 'db://assets/framework/net/cocos-transport';

async function startup() {
  const href = location.href;
  const isDebugBuild = typeof DEBUG !== 'undefined' ? DEBUG : true;
  const mode = resolveRuntimeMode(isDebugBuild);

  const result = await resolveBootstrap({
    mode,
    win: globalThis as any,
    search: location.search,
    fetcher: new HttpConfigFetcher(),
    isNative: href.indexOf('index.html') > -1,   // 等价旧 Logic.isH5Version() 反向
  });

  const net = new NetClient({
    servers: result.servers,
    transportFactory: () => new CocosWebSocketTransport(),
    debug: result.isDebugger,
  });

  // 渠道身份 + 玩家档案合并成完整 player_login 数据
  // debug:玩家档案来自 result.account(profile 里配的调试账号,单一权威来源)
  // release:result.account 为 undefined,玩家档案由 platform 层/原生提供(后续 Plan)
  net.setIdentity({
    ...result.identity,            // agentid/channelid/gameid/marketid/version...
    ...(result.account ?? {}),     // openid/nickname/avatar/... 调试账号或平台层登录信息
  });

  net.on('login', (data) => { /* 解析 parseLoginResponse(data) */ });
  net.start();
}

NetClient.setIdentity 需要的是完整 player_login 字段(LoginRequestData)。resolveBootstrap 只负责其中的渠道身份部分;玩家档案字段(openid/nickname/avatar/...)由登录/微信信息提供,二者合并后再 setIdentity。

环境判定 isNative

沿用旧工程 Logic.isH5Version() 的判据:URL 含 index.html → 原生 WebView(isNative=true);否则 H5/浏览器(isNative=false)。判定逻辑放在调用方,便于测试注入。


10. 原生联调(window.settings / app_*)

原生模式(isNative=true)下,渠道身份从原生注入对象同步读取,接口名/全局名逐字一致旧工程:

字段 主接口 全局回退
agentid window.settings.getothername('agent') window.app_agent
channelid window.settings.getchannelName() (无)
marketid window.settings.getmarketname() window.app_market

读取做了 try/catch:主接口抛错(原生桥未就绪)则尝试全局回退;都拿不到则该字段不覆盖(保留 BUILD_IDENTITY 默认)。

原生侧需要保证在 H5 启动前注入 window.settings 或上述 app_* 全局。完整的异步 WVJB 桥(分享/视频/语音/电量等)不在本子系统范围,属后续 sdk 层;本期只做身份相关的同步读取。


11. 跑测试与类型检查

配置/渠道子系统是纯逻辑 + IO 分离设计,全部可在 Node 下自动化测试(无需 Cocos 运行时)。

# 工作目录:cocoscreator_projects/
cd cocoscreator_projects

npm run test:framework       # 跑全部框架测试(node:test + tsx)
npm run typecheck:framework  # 类型检查(tsc --noEmit)

要点:

  • 测试文件在 cocoscreator_projects/framework-tests/(Cocos assets/ 之外,不被编辑器编译),实现在 YouleNexus/assets/framework/。
  • test:framework 实际跑的是 scripts/run-framework-tests.mjs,递归收集 framework-tests/**/*.test.ts。原因:本机 Node 20.13 < 20.14,node --test 还不支持 glob,故用脚本枚举。
  • 依赖 tsx、typescript、@types/node(devDependencies)。
  • tsconfig(tsconfig.framework.json)开了 allowImportingTsExtensions,所以框架内 import 必须带 .ts 后缀。

写测试时注意一个 TS 6.x 坑:对「只在闭包内赋值、初值为 null」的变量,经 assert.ok(x) 收窄后会被推成 never。规避法:把变量声明为 any,或在断言处把数据传给纯函数再断言(参考 framework-tests/integration/login-flow.test.ts)。


12. 常见调试场景配方

A. 连本地服务器跑一局

  1. 本地起服务器在 127.0.0.1:3088(或改 local 档位的 server)。
  2. 预览 URL 加 ?profile=local。
  3. 这条直连、不抓远程配置;isDebugger=true 会输出收发包日志。

B. 用测试配置服 + 默认渠道

预览 URL 加 ?profile=staging。走 staging 的 gameserver 解析出服务器地址。

C. 复现某渠道专属 bug

保持当前档位,URL 追加该渠道的身份:

?agentid=<该渠道agentid>&channelid=<该渠道channelid>&marketid=<该渠道marketid>

这些覆盖优先级最高,远程配置会据此分层取到该渠道的服务器/参数。

D. 连组内某台联调机

在 PROFILES 里加一个档位(见 §5 的 lab 例子),用 ?profile=lab。

E. 想绕开远程配置服直连某地址

加一个带 server 的 profile(同 D 的 lab 例子)用 ?profile=lab 直连——这是把"应急地址"写进权威来源(profile),而不是让 resolveBootstrap 兜底。远程配置服真的挂了,就是显式 ConfigFetchError,去修配置服或临时切到直连 profile,不在代码里降级(CLAUDE.md 第二准则)。


13. 真实服务器端到端联调

对应 Plan 2 的 Task 12 Step 4-5(自动化 mock 已全绿,真机联调需要外部输入)。

前置:① 可联调的服务器地址(ws://ip:port 或配置服 gameserver URL);② 一组后端认可的有效渠道身份(agentid/channelid/marketid + 目标子游戏的 gameid)。

步骤:

  1. 用 funplay-cocos-mcp 的 execute_javascript(context=editor)或 execute_editor_script 注入一段临时调试入口(或挂一个调试脚本),按 §9 的接入示例:resolveBootstrap + HttpConfigFetcher + NetClient + CocosWebSocketTransport。
    • 想直连:在 PROFILES 临时加一档带 server 的 profile,或直接 new NetClient({ servers: ['ws://<联调地址>'] })。
    • 想验证远程配置链路:把 gameserver 指向联调配置服,走 resolveBootstrap 完整流程。
  2. net.setIdentity({...result.identity, ...玩家档案}),监听 open/login/message/slow/reconnecting。
  3. net.start() 连真机。
  4. 断言/观察:收到 @toconcon 握手不报错 → 发出 player_login → 收到 player_login 响应且 parseLoginResponse(data).ok === true → console 打印 playerid/资产。
  5. 用 search_project_logs / capture_preview_screenshot 留存联调证据。
  6. 把联调结论(成功/协议偏差)回填到 docs/protocol/ 对应章节的「⚠️待服务器确认」项。

14. 发布前检查清单

  • config/runtime-mode.ts 的 MODE_OVERRIDE 为 null(发布走自动模式,由 release 构建决定)。
  • config/profiles.ts 的 ACTIVE_PROFILE 为 'prod'。
  • prod 档位 isDebugger: false(不输出收发包日志)。
  • 不依赖任何 ?profile=/?agentid= 之类 URL 覆盖(生产 URL 应是干净的)。
  • 目标子游戏的 BUILD_IDENTITY.gameid 正确(每子游戏不同)。
  • gameserver(DEFAULT_GAMESERVER)指向正式配置服。
  • npm run test:framework 全绿、npm run typecheck:framework exit 0。

15. 红线:哪些字符串绝不能改

跨边界到「服务器 / 原生 / 配置服务」的字符串契约必须逐字不变(CLAUDE.md 三条红线)。内部变量/类型/函数名可以现代化命名,但下面这些字面量不能动:

  • 协议字段名:agentid、channelid、gameid、marketid、openid、version、route、rpc 等。
  • 原生接口名/全局名:window.settings.getothername、getchannelName、getmarketname;全局 app_agent、app_market。
  • 远程配置键名:agentlist、gamelist、channellist、marketlist 及其匹配键 agentid/gameid/channelid/marketid;连接参数 player_server_tcp、visitor_server_tcp、game_server_tcp(以及未来用到的 *_http 等)。
  • gameserver URL 防缓存:<url>?<时间戳> 形式。

改这些之前先核对旧工程源码(12_Logic.js 的 get_paravalue/getConfig_Succ、05_Func.js 的 getothername/getchannelName/getmarketname、version.js、00_SubGame_Config.js),不要臆测。


16. API 速查表

符号 来自 签名 / 说明
resolveRuntimeMode config/runtime-mode.ts `(isDebugBuild, override?) => 'debug'
resolveBootstrap config/bootstrap.ts (opts: BootstrapOptions) => Promise<BootstrapResult> 启动编排(入参含 mode,结果含 mode/isDebugger)
BUILD_IDENTITY config/sources/defaults.ts 构建期默认渠道身份常量
resolveIdentity config/identity.ts (sources: IdentitySource[]) => ChannelIdentity 优先级合并
queryStringSource config/sources/query-string.ts (search: string) => IdentitySource H5 URL 身份来源
nativeSettingsSource config/sources/native-settings.ts (win: NativeHost) => IdentitySource 原生身份来源
PROFILES / ACTIVE_PROFILE config/profiles.ts 档位表 / 当前默认档位名
resolveActiveProfile config/profiles.ts (search: string) => DebugProfile,支持 ?profile=
DEFAULT_GAMESERVER config/profiles.ts 正式远程配置 txt URL
getParam config/remote-config.ts (config, key, id) => unknown 分层取参
resolveServers config/remote-config.ts (config, id) => string[] ws 候选地址
ConfigFetcher config/remote-config.ts 抓取接口 { fetch(url): Promise<RemoteConfig> }
HttpConfigFetcher config/remote-config-fetcher.ts 生产用 fetch 实现
ConfigFetchError / ConfigParseError config/remote-config.ts 抓取/解析错误类型

文档对应实现已合入 master;如接口有演进,请同步更新本指南。