Files
youle_app_ios_v2/ylgamehall/Source/Bridge/Handlers/LocalAudioHandler.swift
T
joywayerandClaude Opus 4.7 7be7a2abbc 子游戏 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>
2026-06-23 23:04:57 +08:00

64 lines
2.4 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// LocalAudioHandler.swift
// ylgamehall
//
// H5 → Native handlersrcIsloop — 本地音频播放(背景循环 / 按钮单次)
// 契约: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}`
// - lobbyassetsRoot = SandboxPaths.lobbyIndex.deletingLastPathComponent()
// - subGameassetsRoot = 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
}
}
}
}