From d335a589b65c14c16cfa3458b626156b8982dcde Mon Sep 17 00:00:00 2001 From: lanterngamescn Date: Sat, 27 Jun 2026 09:31:22 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20app=5Fdata.js=20=E4=B8=89?= =?UTF-8?q?=E5=A4=84=E5=AD=97=E6=AE=B5=E4=B8=8E=20Android=20=E4=B8=8D?= =?UTF-8?q?=E4=B8=80=E8=87=B4=EF=BC=88H5=20=E9=9B=B6=E6=94=B9=E5=8A=A8?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §4.2 配置项接口逻辑审计发现 app_data.js 注入有三处偏离原 Android inith5data(weclomeactivity1/NewwebviewActivity 两处实测一致): 1. app_version:Android 硬编码 `var app_version=1;`(数字常量,"初始化框架 加载方式"标志,非资源版本);原实现误用 res.localVersion()(如 "261" 且成 字符串)。改为固定数字字面量 1。契约文档 §6 此处亦误录为"version.xml 的 version",一并纠正。 2. app_gamename:Android 取 getAllFilename("gamestart")(即 'gamehall');原 实现误用 version.xml 的 (如"友乐游戏")。改为 config.gamestart。 3. app_getwifisignalLevel:Android 硬编码 `=1`(数字);原实现为字符串 '0'。 改为固定数字字面量 1。 buildValues 签名随之简化为 (config, launchtype),去掉不再需要的 version/ gameName 入参;大厅(StartupOrchestrator '0')、子游戏(BridgeGameContainer '1') 两调用点同步更新。buildJs 对 app_version/app_getwifisignalLevel 输出数字字面量 (无引号),其余字符串字段保持引号,逐字对齐 Android。 其余审计结论一致:11 个本地配置键读取一致(servertype/gameserver 契约标 "无代码读取"已正确省略);桥 handler getmarketname→market、getOther→other、 getothername→按名取配置(lookupConfig 全键 + 未知返回'')均与 Android getAllFilename 对齐。真机回归:大厅 V1.261 正常加载渲染。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../设计文档/TSGame_原生与H5接口契约总规范.md | 18 +++++----- .../src/main/ets/resource/AppDataInjector.ets | 35 +++++++++++-------- .../main/ets/startup/StartupOrchestrator.ets | 2 +- .../main/ets/pages/BridgeGameContainer.ets | 2 +- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/docs/设计文档/TSGame_原生与H5接口契约总规范.md b/docs/设计文档/TSGame_原生与H5接口契约总规范.md index 9995fc7..1e1a7d6 100644 --- a/docs/设计文档/TSGame_原生与H5接口契约总规范.md +++ b/docs/设计文档/TSGame_原生与H5接口契约总规范.md @@ -406,22 +406,24 @@ gameconfig 值 = "tsgames.daoqi88.cn-config-update_jsonv2" // 生产端点 ``` 文件内容为一组全局 JS 变量声明(H5 直接引用),字段如下: -| 全局变量 | 来源 | 含义 | +| 全局变量 | 来源 | 含义 / 输出形态 | |---|---|---| -| `app_version` | version.xml 的 version | 资源版本号 | +| `app_version` | **固定数字 `1`** | "初始化框架加载方式"标志(Android 硬编码 `var app_version=1;`,**非**资源版本号);**数字字面量、无引号** | | `app_gameconfig` | `gameconfig` 配置 | 远程配置地址标识 | | `app_gamedir` | `gamedir` 配置 | 资源父目录名 | | `app_gamestart` | `gamestart` 配置 | 游戏目录名(gamehall) | | `app_agent` | `agent` 配置 | 代理商 ID | -| `app_appversion` | `appversion` 配置 | App 版本号 | -| `app_market` | `market` 配置 | 市场 ID | +| `app_appversion` | `appversion` 配置 | App 版本号(字符串) | +| `app_market` | `market` 配置 | 市场 ID(字符串) | | `app_channel` | `channel` 配置 | 渠道 ID | -| `app_invitationcode` | `tuiguang` 配置 | 推广/邀请码 | -| `app_Launchtype` | 固定 `'0'`(大厅) | 启动类型 | -| `app_gamename` | 资源/配置 | 游戏名 | -| `app_getwifisignalLevel` | 运行时 | WiFi 信号等级初值 | +| `app_invitationcode` | `tuiguang` 配置 | 推广/邀请码(空则 `''`) | +| `app_Launchtype` | 固定 `'0'`(大厅)/ `'1'`(子游戏) | 启动类型(字符串) | +| `app_gamename` | **`gamestart` 配置**(getAllFilename("gamestart"),即 `gamehall`) | 游戏名;**注意:源是 gamestart 配置值,非 version.xml 的 ``** | +| `app_getwifisignalLevel` | **固定数字 `1`** | WiFi 信号等级初值(Android 硬编码 `var app_getwifisignalLevel=1;`);**数字字面量、无引号** | > 新平台只需在加载大厅页面前,保证大厅目录下存在等价的 `app_data.js`(同名全局变量、同含义取值)。内部如何生成文件不限。 +> +> ⚠ **纠错(以 Android `inith5data` 实测为准)**:本表早期把 `app_version` 误录为"version.xml 的 version(资源版本号)",`app_gamename` 误录为"资源/配置",`app_getwifisignalLevel` 误录为"运行时"。**实际**:`app_version` 与 `app_getwifisignalLevel` 均为 Android 硬编码常量 `1`(数字字面量,无引号),`app_gamename` 取 `gamestart` 配置值(非 version.xml name)。大厅(`weclomeactivity1`)与子游戏(`NewwebviewActivity`)两处 `inith5data` 字段完全一致,仅 `app_Launchtype` 分别为 `'0'`/`'1'`。 --- diff --git a/domain_resource/src/main/ets/resource/AppDataInjector.ets b/domain_resource/src/main/ets/resource/AppDataInjector.ets index af055b7..3e3cda1 100644 --- a/domain_resource/src/main/ets/resource/AppDataInjector.ets +++ b/domain_resource/src/main/ets/resource/AppDataInjector.ets @@ -11,9 +11,9 @@ import { FileSystem } from 'platform'; import { Logger } from 'common'; import { AppConfig } from '../config/AppConfig'; -/** app_data.js 全局变量取值(§6)。 */ +/** app_data.js 全局变量取值(§6)。逐字对齐 Android weclomeactivity1/NewwebviewActivity.inith5data。 */ export interface AppDataValues { - /** version.xml 的 version(资源版本号)。 */ + /** 固定 '1'(Android 硬编码 `var app_version=1;`,"初始化框架加载方式"标志,**非**资源版本号;输出为数字字面量)。 */ app_version: string; /** gameconfig 配置(远程配置地址标识)。 */ app_gameconfig: string; @@ -31,21 +31,25 @@ export interface AppDataValues { app_channel: string; /** tuiguang 配置(推广/邀请码)。 */ app_invitationcode: string; - /** 固定 '0'(大厅启动类型)。 */ + /** 启动类型:大厅 '0' / 子游戏 '1'。 */ app_Launchtype: string; - /** 游戏名。 */ + /** 游戏名:Android 取 `gamestart` 配置(getAllFilename("gamestart"),即 'gamehall'),**非** version.xml name。 */ app_gamename: string; - /** WiFi 信号等级初值。 */ + /** 固定 '1'(Android 硬编码 `var app_getwifisignalLevel=1;`;输出为数字字面量)。 */ app_getwifisignalLevel: string; } export class AppDataInjector { private static readonly log: Logger = Logger.tag('AppDataInjector'); - /** 由本地配置 + 资源版本 + 启动类型 + 游戏名构建 app_data 值(大厅 launchtype='0' / 子游戏 '1')。 */ - static buildValues(config: AppConfig, version: string, launchtype: string, gameName: string = ''): AppDataValues { + /** + * 由本地配置 + 启动类型构建 app_data 值(大厅 launchtype='0' / 子游戏 '1')。 + * 🔴 逐字对齐 Android:app_version / app_getwifisignalLevel 为硬编码常量 1(非资源版本/非运行时探测); + * app_gamename 取 `gamestart` 配置(非 version.xml name)。其余字段来自本地配置同名项。 + */ + static buildValues(config: AppConfig, launchtype: string): AppDataValues { return { - app_version: version, + app_version: '1', app_gameconfig: config.gameconfig, app_gamedir: config.gamedir, app_gamestart: config.gamestart, @@ -55,16 +59,19 @@ export class AppDataInjector { app_channel: config.channel, app_invitationcode: config.tuiguang, app_Launchtype: launchtype, - // app_gamename 源为 version.xml 的子 (ResourceManager.localGameName 提供) - app_gamename: gameName, - app_getwifisignalLevel: '0', + app_gamename: config.gamestart, + app_getwifisignalLevel: '1', }; } - /** 生成 app_data.js 文本(一组全局 var 声明)。 */ + /** + * 生成 app_data.js 文本(一组全局 var 声明)。 + * app_version / app_getwifisignalLevel 输出为**数字字面量**(无引号,对齐 Android `var app_version=1;`); + * 其余为字符串字面量(JSON.stringify 加引号,等价 Android 的单引号字符串)。 + */ static buildJs(v: AppDataValues): string { const lines: string[] = [ - `var app_version=${JSON.stringify(v.app_version)};`, + `var app_version=${v.app_version};`, `var app_gameconfig=${JSON.stringify(v.app_gameconfig)};`, `var app_gamedir=${JSON.stringify(v.app_gamedir)};`, `var app_gamestart=${JSON.stringify(v.app_gamestart)};`, @@ -75,7 +82,7 @@ export class AppDataInjector { `var app_invitationcode=${JSON.stringify(v.app_invitationcode)};`, `var app_Launchtype=${JSON.stringify(v.app_Launchtype)};`, `var app_gamename=${JSON.stringify(v.app_gamename)};`, - `var app_getwifisignalLevel=${JSON.stringify(v.app_getwifisignalLevel)};`, + `var app_getwifisignalLevel=${v.app_getwifisignalLevel};`, ]; return lines.join('\n') + '\n'; } diff --git a/domain_resource/src/main/ets/startup/StartupOrchestrator.ets b/domain_resource/src/main/ets/startup/StartupOrchestrator.ets index ede5402..d77c78b 100644 --- a/domain_resource/src/main/ets/startup/StartupOrchestrator.ets +++ b/domain_resource/src/main/ets/startup/StartupOrchestrator.ets @@ -155,7 +155,7 @@ export class StartupOrchestrator { // 6) 注入 app_data.js(务必在进大厅之前)。大厅 launchtype='0' this.stage(StartupStage.INJECT_APP_DATA, 92); - const values: AppDataValues = AppDataInjector.buildValues(local, `${resource.localVersion()}`, '0', resource.localGameName()); + const values: AppDataValues = AppDataInjector.buildValues(local, '0'); AppDataInjector.inject(resource.paths().gameDir, values); // 7) 进大厅 diff --git a/entry/src/main/ets/pages/BridgeGameContainer.ets b/entry/src/main/ets/pages/BridgeGameContainer.ets index 2590cc6..8a034d7 100644 --- a/entry/src/main/ets/pages/BridgeGameContainer.ets +++ b/entry/src/main/ets/pages/BridgeGameContainer.ets @@ -183,7 +183,7 @@ export struct BridgeGameContainer { } const subDir: string = `${res.paths().urlpath}/${d.dir}`; // 子游戏目录自带 app_data.js(H5 同步读),launchtype='1' - const values: AppDataValues = AppDataInjector.buildValues(cfg.getLocal(), `${res.localVersion()}`, '1', res.localGameName()); + const values: AppDataValues = AppDataInjector.buildValues(cfg.getLocal(), '1'); AppDataInjector.inject(subDir, values); this.slotL?.deactivate(); this.slotS = new WebSlot('subgame', new webview.WebviewController(), true, this.uploadServer);