Phase 6 commit B:SubGameDownloader zip 下载 + 解压

- 新增 Source/Resource/SubGameDownloader.swift(actor):URLSession + ZIPFoundation,
  缓存命中直接复用(看 {Caches}/{gameDir}/{gameStart}/index.html 是否存在),
  未命中下载 → staging 解压 → 原子 rename(与 LobbyZipUpgrader 同思路;
  msext gameController.m:1340-1401 等价,以 staging 替代 msext 直接覆盖避免半残留)
- SubGameViewController.runBootPipelineSteps:接入 ensureReady,splash 实时更新下载进度,
  移除 commit A 占位的 SubGameBootError
- DownloadProgressDelegate 与 LobbyZipUpgrader 同款就地复制(两处用不抽公共,
  三处出现时再合并)
- Plan §5.6.7 / §8 进度已勾选

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
joywayer
2026-06-22 21:01:53 +08:00
co-authored by Claude Opus 4.7
parent 7a5644053d
commit a9eaf86c63
3 changed files with 178 additions and 27 deletions
@@ -164,15 +164,19 @@ public final class SubGameViewController: UIViewController {
}
private func runBootPipelineSteps() async throws {
// 1. 确保子游戏 zip 已下载解压(commit B 接入 SubGameDownloader)
// commit A 暂时直接走 FileManager 检查,不存在则抛错让 commit B 接入下载。
let indexURL = SandboxPaths.subGameIndex(request.gameDir, request.gameStart)
guard FileManager.default.fileExists(atPath: indexURL.path) else {
throw SubGameBootError.subGameNotInstalled(
dir: request.gameDir,
downloadURL: request.downloadURL
)
}
// 1. 确保子游戏 zip 已下载解压(缓存命中直接复用,未命中下载 + 解压 + 原子 rename)
splash.update(text: "准备子游戏...", progress: nil)
_ = try await SubGameDownloader.shared.ensureReady(
request: request,
onProgress: { [weak self] p in
Task { @MainActor in
self?.splash.update(
text: String(format: "下载子游戏 %d%%", Int(p * 100)),
progress: p
)
}
}
)
// 2. 写 4 个 app_*.js(containerRole = .subGame)。BatteryMonitor / NetworkMonitor
// 单例已由大厅启动,此处仅复用其 currentXxx。
@@ -187,6 +191,7 @@ public final class SubGameViewController: UIViewController {
// 3. 加载子游戏 H5(allowingReadAccessTo 给 subGameRoot 才能跨目录引用资源)
splash.update(text: "进入子游戏...", progress: nil)
let indexURL = SandboxPaths.subGameIndex(request.gameDir, request.gameStart)
bridgedWebView.webView.loadFileURL(
indexURL,
allowingReadAccessTo: SandboxPaths.subGameRoot(request.gameDir)
@@ -239,23 +244,12 @@ public final class SubGameViewController: UIViewController {
AppLifecycleObserver.shared.onForeground = nil
}
// MARK: - Errors
private enum SubGameBootError: LocalizedError {
case subGameNotInstalled(dir: String, downloadURL: String)
var errorDescription: String? {
switch self {
case .subGameNotInstalled(let dir, _):
return "子游戏未安装(\(dir)),下载流程待 Phase 6.B 接入"
}
}
}
// MARK: - Error alert
private func showFatalAlert(error: Error) {
let alert = UIAlertController(
title: "加载失败",
message: "\(error.localizedDescription)",
message: "\(error)",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "返回大厅", style: .default) { [weak self] _ in