子游戏跳转链路修复 + H5 音频统一可用
修复后子游戏 SwitchOverGameData 跳转完全可用,行为对齐 daoqi msext:
【zip URL 解析】H5 字段 gamedownloadurl 实为 gameid(msext gameController.m:544
注释"游戏ID")。RemoteConfigClient 新增 lastParsed 缓存 + current() getter;
SubGameViewController.resolveBoot 做两次 VersionResolver.resolve:
- lobby 视角(gameId=bc.gameId)→ ResolvedVersion 传 AppDataWriter 算
app_appversion 审核标志(msext result_state 等价)
- sub-game 视角(gameId=H5 传的 gameid)→ 真实 game_zip 下载 URL
之前 SubGameDownloader 直接把 token 当 URL 用,URLSession 报 -1002
unsupported URL;AppDataWriter resolvedVersion 始终 nil 导致子游戏 app_appversion
永远 0、丢失审核切换能力。
【push 前停大厅背景音】AppCoordinator.showSubGame 节流通过后、push 前调
AudioPlayer.stopAllBackground(msext NewRootVC.m:549-552 等价)。
【H5 音频】
- AVAudioSession.setCategory(.playback) 在 AppDelegate 启动期设置,让原生
+ WKWebView 内 <audio> 都不受静音键影响(msext RootVC.m:1302 等多处等价)
- WKWebViewConfiguration.mediaTypesRequiringUserActionForPlayback = [],子游戏
push 后立即 loadFileURL 自播背景音不再被 WebKit 静默拦截
- LocalAudioHandler.register 增 assetsRoot 参数,srcIsloop 按容器各自的 H5
根目录拼 assets/wav/{src}(msext gameController.m:364 等价,大厅 / 子游戏
音频文件在各自 zip 内不重叠)。修前所有路径都指向大厅根,子游戏读不到自己的
音频文件,AVAudioPlayer 报 OSStatus 2003334207 (wht?
kAudioFileUnsupportedFileTypeError)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
053232ed26
commit
e50dfb75af
@@ -78,7 +78,12 @@ public final class SubGameViewController: UIViewController {
|
||||
OpenSaomaHandler.register(on: bridge)
|
||||
StartLocationHandler.register(on: bridge)
|
||||
|
||||
LocalAudioHandler.register(on: bridge)
|
||||
// assetsRoot = 子游戏 H5 根目录(msext gameController.m:364 等价)
|
||||
LocalAudioHandler.register(
|
||||
on: bridge,
|
||||
assetsRoot: SandboxPaths.subGameIndex(request.gameDir, request.gameStart)
|
||||
.deletingLastPathComponent()
|
||||
)
|
||||
RemoteAudioHandler.register(on: bridge)
|
||||
|
||||
AccreditLoginHandler.register(on: bridge)
|
||||
@@ -163,10 +168,28 @@ public final class SubGameViewController: UIViewController {
|
||||
}
|
||||
|
||||
private func runBootPipelineSteps() async throws {
|
||||
// 0. H5 传过来的 SwitchOverGameData.gamedownloadurl **字段名是 url 但语义是 gameid**
|
||||
// (msext gameController.m:544 注释明确写"游戏ID")。真实 zip 下载地址要按
|
||||
// gameid 去启动期缓存的 RemoteConfig 里查 `game_zip` 字段;msext 走 uplevel:
|
||||
// chulishengji 算法,新外壳直接复用 VersionResolver 同一套实现。
|
||||
//
|
||||
// 需要做两次 resolve:
|
||||
// - lobbyResolved(gameId = bc.gameId)→ 给 AppDataWriter 算 app_appversion 审核标志
|
||||
// 与 msext NewRootVC 算 result 后 initinfo 传给 gameController 等价
|
||||
// - subGameResolved(gameId = H5 传的 gameid)→ 拿真实 game_zip URL
|
||||
// 与 msext gameController.uplevel: 等价
|
||||
let (lobbyResolved, subGameZipURL) = try await resolveBoot()
|
||||
let resolvedRequest = SubGameRequest(
|
||||
gameDir: request.gameDir,
|
||||
gameStart: request.gameStart,
|
||||
downloadURL: subGameZipURL,
|
||||
webData: request.webData
|
||||
)
|
||||
|
||||
// 1. 确保子游戏 zip 已下载解压(缓存命中直接复用,未命中下载 + 解压 + 原子 rename)
|
||||
splash.update(text: "准备子游戏...", progress: nil)
|
||||
_ = try await SubGameDownloader.shared.ensureReady(
|
||||
request: request,
|
||||
request: resolvedRequest,
|
||||
onProgress: { [weak self] p in
|
||||
Task { @MainActor in
|
||||
self?.splash.update(
|
||||
@@ -179,9 +202,11 @@ public final class SubGameViewController: UIViewController {
|
||||
|
||||
// 2. 写 4 个 app_*.js(containerRole = .subGame)。BatteryMonitor / NetworkMonitor
|
||||
// 单例已由大厅启动,此处仅复用其 currentXxx。
|
||||
// resolvedVersion 必须传大厅视角的版本判定结果,让 app_appversion 审核标志
|
||||
// 与大厅一致(msext result_state 传递路径等价)。
|
||||
let writer = AppDataWriter(
|
||||
bundleConfig: .shared,
|
||||
resolvedVersion: nil,
|
||||
resolvedVersion: lobbyResolved,
|
||||
containerRole: .subGame(name: request.gameStart, dir: request.gameDir)
|
||||
)
|
||||
try writer.writeInitial()
|
||||
@@ -197,6 +222,48 @@ public final class SubGameViewController: UIViewController {
|
||||
)
|
||||
}
|
||||
|
||||
/// 子游戏启动期两次 resolve:
|
||||
/// - lobby 视角(gameId = bc.gameId)给 app_appversion 审核标志(msext result_state 等价)
|
||||
/// - sub-game 视角(gameId = H5 传的 gameid)给真实 game_zip 下载 URL(msext gameController.uplevel: 等价)
|
||||
private func resolveBoot() async throws -> (lobbyResolved: ResolvedVersion, gameZipURL: String) {
|
||||
guard let cfg = await RemoteConfigClient.shared.current() else {
|
||||
throw SubGameBootError.remoteConfigUnavailable
|
||||
}
|
||||
let bc = BundleConfig.shared
|
||||
let lobbyResolved = VersionResolver.resolve(
|
||||
config: cfg,
|
||||
agentId: bc.agent,
|
||||
channelId: bc.channel,
|
||||
marketId: bc.market,
|
||||
gameId: bc.gameId
|
||||
)
|
||||
let subGameResolved = VersionResolver.resolve(
|
||||
config: cfg,
|
||||
agentId: bc.agent,
|
||||
channelId: bc.channel,
|
||||
marketId: bc.market,
|
||||
gameId: request.downloadURL // ← H5 字段名 url,实为 gameid
|
||||
)
|
||||
guard let zipURL = subGameResolved.gameZip, !zipURL.isEmpty else {
|
||||
throw SubGameBootError.zipURLNotFound(gameId: request.downloadURL)
|
||||
}
|
||||
return (lobbyResolved, zipURL)
|
||||
}
|
||||
|
||||
enum SubGameBootError: Error, CustomStringConvertible {
|
||||
case remoteConfigUnavailable
|
||||
case zipURLNotFound(gameId: String)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .remoteConfigUnavailable:
|
||||
return "远端配置未就绪,请返回大厅重试"
|
||||
case .zipURLNotFound(let id):
|
||||
return "未找到子游戏 \(id) 的下载地址"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 外部订阅生命周期(参 §2.4.2)
|
||||
//
|
||||
// 子游戏栈顶时挂钩 battery / network / appservice。大厅 push 子游戏后大厅
|
||||
|
||||
Reference in New Issue
Block a user