using System; using System.IO; using System.Reflection; using UnityEngine; using XWorld.Framework; namespace XGame.MiniGame { // 从代码 AB(manifest.CodeAb)取出 Core/Client 热更 DLL 字节并加载,反射出 IGameClient 入口工厂 public sealed class MiniGameAssemblyLoader { public Func CreateClient { get; private set; } public string Error { get; private set; } // localDir: MiniGameDownloader.LocalDir;manifest: 解析好的 game.json public bool Load(string localDir, MiniGameManifest manifest) { string abPath = localDir + manifest.CodeAb; if (!File.Exists(abPath)) { Error = $"缺少代码 AB: {abPath}(需要 codeAb 统一 AB 格式,旧 .bytes 包不再支持)"; return false; } AssetBundle ab = null; try { ab = MiniGamePlatform.LoadAssetBundle(abPath); if (ab == null) { Error = "代码 AB 打开失败: " + abPath; return false; } byte[] coreBytes = LoadDllBytes(ab, manifest.CoreDll); byte[] clientBytes = LoadDllBytes(ab, manifest.ClientDll); if (coreBytes == null || clientBytes == null) { Error = $"代码 AB 内缺少 {manifest.CoreDll}/{manifest.ClientDll} 的 .bytes(AB 内资产: {string.Join(", ", ab.GetAllAssetNames())})"; return false; } // Core 先于 Client 加载(Client 依赖 Core) Assembly.Load(coreBytes); Assembly clientAsm = Assembly.Load(clientBytes); Type entry = clientAsm.GetType(manifest.ClientEntryType); if (entry == null) { Error = "找不到入口类型 " + manifest.ClientEntryType; return false; } if (!typeof(IGameClient).IsAssignableFrom(entry)) { Error = manifest.ClientEntryType + " 未实现 IGameClient"; return false; } CreateClient = () => (IGameClient)Activator.CreateInstance(entry); return true; } catch (Exception e) { Error = "加载小游戏程序集失败: " + e; Debug.LogError("[MiniGameAssemblyLoader] " + Error); return false; } finally { // DLL 字节已复制进托管内存,AB 本体可卸;false=不销毁已取出的对象 if (ab != null) ab.Unload(false); } } // "X.dll.bytes" 导入后资产名为 "X.dll"(Unity 剥最后一个扩展名);两种写法都试 private static byte[] LoadDllBytes(AssetBundle ab, string dllName) { TextAsset ta = ab.LoadAsset(dllName) ?? ab.LoadAsset(dllName + ".bytes"); return ta != null ? ta.bytes : null; } } }