// // ResourceUnzipper.swift // ylgamehall // // 启动期把 Bundle 内的 gamehall.zip 解压到 Library/Caches/{gamedir}/。 // 详见 docs/H5-Native-Implementation-Design.md §1.1 / §7。 // import Foundation import ZIPFoundation /// 大厅 H5 资源包解压器。actor 隔离保证同一进程内并发调用 `ensureReady()` 不重复解压。 /// /// 解压触发条件:`{Caches}/{gamedir}/{gamestart}/version.xml` 不存在 /// (首次安装 / 系统清空 Caches 后命中)。日常启动直接跳过,耗时为 0。 public actor ResourceUnzipper { public static let shared = ResourceUnzipper() public enum UnzipError: Error { case bundleZipNotFound case unzipFailed(underlying: Error) } private init() {} /// 异步确保大厅 H5 资源在 Caches 内就绪。幂等。 /// /// - 已就绪(version.xml 存在)→ 立即返回 /// - 未就绪 → 创建 `{Caches}/{gamedir}/`,解压 `Bundle/gamehall.zip` 到其中 public func ensureReady() async throws { if FileManager.default.fileExists(atPath: SandboxPaths.lobbyVersionXML.path) { return } guard let zipURL = Bundle.main.url(forResource: "gamehall", withExtension: "zip") else { throw UnzipError.bundleZipNotFound } let destination = SandboxPaths.lobbyRoot let fm = FileManager.default do { try fm.createDirectory(at: destination, withIntermediateDirectories: true) try fm.unzipItem(at: zipURL, to: destination) } catch { throw UnzipError.unzipFailed(underlying: error) } } }