之前新外壳的 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>
64 lines
2.4 KiB
Swift
64 lines
2.4 KiB
Swift
//
|
||
// LocalAudioHandler.swift
|
||
// ylgamehall
|
||
//
|
||
// H5 → Native handler:srcIsloop — 本地音频播放(背景循环 / 按钮单次)
|
||
// 契约:docs/H5-Native-Contract.md §3.1 [3]
|
||
// 原项目:
|
||
// - 大厅 daoqi/msext NewRootVC.m 用 lobby gamedir/gamestart
|
||
// - 子游戏 daoqi/msext gameController.m:359-419 用 self.gamefilepath/self.game_name
|
||
// 两边各自注册,路径根不同。新外壳保留同一 enum 注册器,但调用方注入 assetsRoot。
|
||
//
|
||
// 音频文件实际路径:`{assetsRoot}/assets/wav/{src}`
|
||
// - lobby:assetsRoot = SandboxPaths.lobbyIndex.deletingLastPathComponent()
|
||
// - subGame:assetsRoot = SandboxPaths.subGameIndex(dir,name).deletingLastPathComponent()
|
||
//
|
||
|
||
import Foundation
|
||
|
||
@MainActor
|
||
public enum LocalAudioHandler {
|
||
|
||
/// 注册 srcIsloop handler。
|
||
/// - Parameters:
|
||
/// - bridge: 桥接器
|
||
/// - 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 停同名循环
|
||
// cb: "Response from srcIsloop"
|
||
bridge.register("srcIsloop") { data, callback in
|
||
defer { callback?(.string("Response from srcIsloop")) }
|
||
|
||
guard let obj = data?.asObject,
|
||
let src = obj["src"]?.asString,
|
||
let isloop = obj["isloop"]?.asInt
|
||
else {
|
||
return
|
||
}
|
||
|
||
let url = await assetsRoot()
|
||
.appendingPathComponent("assets")
|
||
.appendingPathComponent("wav")
|
||
.appendingPathComponent(src)
|
||
|
||
switch isloop {
|
||
case 0:
|
||
await AudioPlayer.shared.playOnce(url)
|
||
case 1:
|
||
await AudioPlayer.shared.loopBackground(url, type: src)
|
||
case -1:
|
||
await AudioPlayer.shared.stopBackground(type: src)
|
||
default:
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|