子游戏 zip 升级链路完整对齐 msext(持久化目录 + advance + 版本比对)

之前新外壳的 SubGameDownloader 只看 index.html 是否存在,缺这两个关键行为:
- gamefilepath 持久化(msext NSUserDefaults["game_name"] → gamefilepath)
- 远端 game_version > 本地 version.xml 时升级 + 目录末尾追加 "1"
  (msext gameController.m:1414 等价 [NSString stringWithFormat:@"%@1", gamefilepath])

本次完整补齐:

1. 新增 SubGameDirectoryStore:UserDefaults 持久化 directory → actualDir 映射
   - current(forKey:) / advance(forKey:) / saveInitialIfNeeded(forKey:)
   - nonisolated 让 actor / MainActor 都能直接调

2. SubGameDownloader 三分支决策(msext gameController.m:1239-1255 + uplevel: 等价):
   - 目录不存在(首装)→ 下载到 currentDir,不 advance
   - 目录存在 + 远端 > 本地 → advance + 下载到新目录
   - 远端 ≤ 本地 → 复用 currentDir
   API 改为按入参(directoryKey / gameStart / remoteVersion / remoteZipURL)调用,
   返回 Outcome.actualDirectory 暴露实际生效目录名。

3. LocalAudioHandler.register 的 assetsRoot 改 @MainActor closure
   srcIsloop 每次调用时取最新值,让子游戏升级(effectiveGameDir 末尾追加 "1")
   后下一次 srcIsloop 自动用新目录路径。

4. SubGameViewController 引入 effectiveGameDir:
   - init 时同步从 SubGameDirectoryStore 读初始值(首次=request.gameDir)
   - boot pipeline 内调 ensureReady,从 outcome 同步升级后的新目录名
   - loadFileURL / AppDataWriter / srcIsloop closure 全部走 effectiveGameDir
   - resolveBoot 返回 (lobbyResolved, subGameResolved) 完整 ResolvedVersion,
     给 ensureReady 用 gameVersion 做版本比对

5. WebContainerViewController 大厅侧 LocalAudioHandler 改 closure 形式(路径不变)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
joywayer
2026-06-23 23:04:57 +08:00
co-authored by Claude Opus 4.7
parent 18fd782156
commit 7be7a2abbc
5 changed files with 212 additions and 60 deletions
@@ -22,9 +22,13 @@ public enum LocalAudioHandler {
/// 注册 srcIsloop handler。
/// - Parameters:
/// - bridge: 桥接器
/// - assetsRoot: 该容器(lobby / subGame)的 H5 资源根目录;
/// 音频文件实际路径会拼接 `assets/wav/{src}` 在该根下查找。
public static func register(on bridge: any BridgeProtocol, assetsRoot: URL) {
/// - assetsRoot: 闭包,返回该容器(lobby / subGame)的 H5 资源根目录;
/// 每次 srcIsloop 调用时调一次,让子游戏 zip 升级(effectiveGameDir 末尾追加 "1")
/// 后能立刻拿到新目录路径。音频文件实际路径会拼接 `assets/wav/{src}` 在该根下查找。
public static func register(
on bridge: any BridgeProtocol,
assetsRoot: @escaping @MainActor @Sendable () -> URL
) {
// 【3】srcIsloop — 本地音频播放
// 入参 src : string 音频文件名(位于 {assetsRoot}/assets/wav/{src})
// 入参 isloop : int 0 单次 / 1 循环背景 / -1 停同名循环
@@ -39,7 +43,7 @@ public enum LocalAudioHandler {
return
}
let url = assetsRoot
let url = await assetsRoot()
.appendingPathComponent("assets")
.appendingPathComponent("wav")
.appendingPathComponent(src)