M3 补全:移除 EchoSample 样板 + getcompareCode 接版本决策 + app_gamename
- 移除 EchoSampleProvider(组装根/导出/文件):M3 全部 Provider 已就位, getTime 由 DeviceProvider 提供;样板回显仅 M1 测试页用,生产无依赖 - getcompareCode:启动期 StartupOrchestrator 把网络 App 版本(VersionDecision.appVersion) 存 KvStore(KV_KEY_NET_APP_VERSION),DeviceProvider 同步读出与本地 config.appversion 比较, 本地>网络→'1' 否则'0'(对齐 Android apputil.code 语义) - app_gamename:VersionXml.parse 解析子 <game name>;ResourceManager.localGameName 暴露; AppDataInjector.buildValues 接 gameName 参数;大厅/子游戏两处注入均填入 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
cc5db07ddd
commit
01e4c0e99e
@@ -10,10 +10,10 @@ export { RemoteConfig, AgentNode, ChannelNode, MarketNode, GameOverrideNode, Gam
|
||||
export { VersionResolver } from './src/main/ets/version/VersionResolver';
|
||||
|
||||
// 资源
|
||||
export { VersionXml } from './src/main/ets/resource/VersionXml';
|
||||
export { VersionXml, VersionXmlInfo } from './src/main/ets/resource/VersionXml';
|
||||
export { ResourceManager, ResourcePaths } from './src/main/ets/resource/ResourceManager';
|
||||
export { AppDataInjector, AppDataValues } from './src/main/ets/resource/AppDataInjector';
|
||||
|
||||
// 启动编排
|
||||
export { StartupOrchestrator, StartupStage, StartupResult, StageCallback }
|
||||
export { StartupOrchestrator, StartupStage, StartupResult, StageCallback, KV_KEY_NET_APP_VERSION }
|
||||
from './src/main/ets/startup/StartupOrchestrator';
|
||||
|
||||
@@ -42,8 +42,8 @@ export interface AppDataValues {
|
||||
export class AppDataInjector {
|
||||
private static readonly log: Logger = Logger.tag('AppDataInjector');
|
||||
|
||||
/** 由本地配置 + 资源版本 + 启动类型构建 app_data 值(大厅 launchtype='0' / 子游戏 '1')。 */
|
||||
static buildValues(config: AppConfig, version: string, launchtype: string): AppDataValues {
|
||||
/** 由本地配置 + 资源版本 + 启动类型 + 游戏名构建 app_data 值(大厅 launchtype='0' / 子游戏 '1')。 */
|
||||
static buildValues(config: AppConfig, version: string, launchtype: string, gameName: string = ''): AppDataValues {
|
||||
return {
|
||||
app_version: version,
|
||||
app_gameconfig: config.gameconfig,
|
||||
@@ -55,8 +55,8 @@ export class AppDataInjector {
|
||||
app_channel: config.channel,
|
||||
app_invitationcode: config.tuiguang,
|
||||
app_Launchtype: launchtype,
|
||||
// TODO(M3):app_gamename 源为 version.xml 的 <game name>,当前 VersionXml 仅解析 version。
|
||||
app_gamename: '',
|
||||
// app_gamename 源为 version.xml 的子 <game name>(ResourceManager.localGameName 提供)
|
||||
app_gamename: gameName,
|
||||
app_getwifisignalLevel: '0',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { common } from '@kit.AbilityKit';
|
||||
import { Downloader, FileSystem, KvStore, ProgressCallback, Unzipper } from 'platform';
|
||||
import { Logger } from 'common';
|
||||
import { AppConfig } from '../config/AppConfig';
|
||||
import { VersionXml } from './VersionXml';
|
||||
import { VersionXml, VersionXmlInfo } from './VersionXml';
|
||||
|
||||
/** 资源路径集合。 */
|
||||
export interface ResourcePaths {
|
||||
@@ -73,18 +73,28 @@ export class ResourceManager {
|
||||
return FileSystem.exists(this.cachedPaths.indexPath);
|
||||
}
|
||||
|
||||
/** 本地资源版本(version.xml 的 version;缺失返回 0)。 */
|
||||
localVersion(): number {
|
||||
/** 解析本地 version.xml(取首个存在的候选;缺失 → 默认空信息)。 */
|
||||
private readVersionXml(): VersionXmlInfo {
|
||||
const candidates: string[] = [
|
||||
`${this.cachedPaths.gameDir}/version.xml`,
|
||||
`${this.cachedPaths.urlpath}/version.xml`,
|
||||
];
|
||||
for (const p of candidates) {
|
||||
if (FileSystem.exists(p)) {
|
||||
return VersionXml.parseVersion(FileSystem.readText(p));
|
||||
return VersionXml.parse(FileSystem.readText(p));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return { version: 0, gameName: '' };
|
||||
}
|
||||
|
||||
/** 本地资源版本(version.xml 的 version;缺失返回 0)。 */
|
||||
localVersion(): number {
|
||||
return this.readVersionXml().version;
|
||||
}
|
||||
|
||||
/** 本地游戏名(version.xml 子 <game name>;缺失返回 ''),写入 app_data 的 app_gamename。 */
|
||||
localGameName(): string {
|
||||
return this.readVersionXml().gameName;
|
||||
}
|
||||
|
||||
/** 持久化路径到 KvStore(供容器/重启读取)。 */
|
||||
|
||||
@@ -2,29 +2,47 @@
|
||||
* version.xml 解析(契约 §5.3,框架 §8.3)。用 @kit.ArkTS xml.XmlPullParser。
|
||||
*
|
||||
* 结构:<game><agent id name/><game id name/><channel id name/><version value name/></game>
|
||||
* 主用途:取 <version value="42"> 与远程 game_version 比较决定是否更新。
|
||||
* `value` 属性为 version 标签独有,故用 attributeValueCallbackFunction 即可稳定抓取。
|
||||
* 用途:取 <version value="42"> 与远程 game_version 比较决定是否更新;取子 <game name="...">
|
||||
* 作 app_data 的 app_gamename。`value` 为 version 标签独有;`name` 多标签共有,故按
|
||||
* 所属标签(当前为子 game)区分抓取。
|
||||
*/
|
||||
import { xml, util } from '@kit.ArkTS';
|
||||
import { Logger } from 'common';
|
||||
|
||||
/** version.xml 解析结果。 */
|
||||
export interface VersionXmlInfo {
|
||||
/** <version value="N"> 整数(缺失/失败 → 0)。 */
|
||||
version: number;
|
||||
/** 子 <game name="..."> 游戏名(缺失 → '')。 */
|
||||
gameName: string;
|
||||
}
|
||||
|
||||
export class VersionXml {
|
||||
private static readonly log: Logger = Logger.tag('VersionXml');
|
||||
|
||||
/** 解析 version 整数(解析失败/缺失 → 0)。 */
|
||||
static parseVersion(xmlText: string): number {
|
||||
/** 解析 version + gameName。属性回调紧随其开始标签,故用 currentTag 把 name 归属到 game 标签。 */
|
||||
static parse(xmlText: string): VersionXmlInfo {
|
||||
const info: VersionXmlInfo = { version: 0, gameName: '' };
|
||||
if (xmlText.length === 0) {
|
||||
return 0;
|
||||
return info;
|
||||
}
|
||||
let value: string = '';
|
||||
let currentTag: string = '';
|
||||
try {
|
||||
const buf: Uint8Array = new util.TextEncoder().encodeInto(xmlText);
|
||||
const parser: xml.XmlPullParser = new xml.XmlPullParser(buf.buffer as object as ArrayBuffer, 'UTF-8');
|
||||
const options: xml.ParseOptions = {
|
||||
ignoreNameSpace: true,
|
||||
tagValueCallbackFunction: (name: string, _val: string): boolean => {
|
||||
currentTag = name;
|
||||
return true;
|
||||
},
|
||||
attributeValueCallbackFunction: (name: string, val: string): boolean => {
|
||||
if (name === 'value') {
|
||||
value = val;
|
||||
} else if (name === 'name' && currentTag === 'game') {
|
||||
// 根 <game> 无 name 属性;只有子 <game id name/> 触发,正是所需游戏名
|
||||
info.gameName = val;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@@ -33,9 +51,15 @@ export class VersionXml {
|
||||
} catch (e) {
|
||||
const err = e as Error;
|
||||
VersionXml.log.w(`parse version.xml failed: ${err.message}`);
|
||||
return 0;
|
||||
return info;
|
||||
}
|
||||
const n: number = parseInt(value, 10);
|
||||
return isNaN(n) ? 0 : n;
|
||||
info.version = isNaN(n) ? 0 : n;
|
||||
return info;
|
||||
}
|
||||
|
||||
/** 解析 version 整数(解析失败/缺失 → 0)。 */
|
||||
static parseVersion(xmlText: string): number {
|
||||
return VersionXml.parse(xmlText).version;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ import { VersionDecision } from '../config/RemoteConfig';
|
||||
import { ResourceManager } from '../resource/ResourceManager';
|
||||
import { AppDataInjector, AppDataValues } from '../resource/AppDataInjector';
|
||||
|
||||
/** KvStore 键:启动期算出的网络 App 版本(供 DeviceProvider getcompareCode 同步比较)。 */
|
||||
export const KV_KEY_NET_APP_VERSION: string = 'net_app_version';
|
||||
|
||||
/** 启动阶段(用于进度展示/可观测)。 */
|
||||
export enum StartupStage {
|
||||
INIT = 'INIT',
|
||||
@@ -122,6 +125,10 @@ export class StartupOrchestrator {
|
||||
StartupOrchestrator.log.w(`blocked by showmessage: ${decision.showmessage}`);
|
||||
return { enter: false, entryUrl: '', message: decision.showmessage };
|
||||
}
|
||||
// 缓存网络 App 版本,供 DeviceProvider getcompareCode 同步比较(离线 decision 缺失则保留上次值)
|
||||
if (decision !== undefined) {
|
||||
kv.putNumber(KV_KEY_NET_APP_VERSION, decision.appVersion);
|
||||
}
|
||||
|
||||
// 5) 版本更新(依赖内置资源已就绪 + 远程决策)
|
||||
const localVer: number = resource.localVersion();
|
||||
@@ -144,7 +151,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');
|
||||
const values: AppDataValues = AppDataInjector.buildValues(local, `${resource.localVersion()}`, '0', resource.localGameName());
|
||||
AppDataInjector.inject(resource.paths().gameDir, values);
|
||||
|
||||
// 7) 进大厅
|
||||
|
||||
Reference in New Issue
Block a user