using System.IO; using System.Reflection; using System.Runtime.Loader; namespace XWorld.Server.Host { // 可卸载上下文:框架契约走默认上下文(类型标识跨界一致),小游戏私有 DLL 载入本上下文。 // DLL 一律字节加载(LoadFromStream):不持有文件句柄,同版本覆盖重发不被文件锁挡住。 // 代价:字节加载的 Assembly.Location 为空——小游戏 DLL 不得依赖它。 internal sealed class GameModuleAlc : AssemblyLoadContext { private readonly string _moduleDir; public GameModuleAlc(string name, string moduleDir) : base(name, isCollectible: true) { _moduleDir = moduleDir; } protected override Assembly Load(AssemblyName assemblyName) { // 设计 §4.2:框架契约类型必须在默认 ALC,不随小游戏卸载。 // 约束:XWorld.Framework.Shared 只能依赖 BCL(System.*);若将来加第三方依赖,需同步扩展下面白名单。 if (assemblyName.Name == "XWorld.Framework.Shared") return null; // BCL / 运行时程序集始终走默认上下文,防止游戏目录里同名文件遮蔽(类型标识错乱) string n = assemblyName.Name; if (n != null && (n.StartsWith("System.", System.StringComparison.Ordinal) || n.StartsWith("Microsoft.", System.StringComparison.Ordinal) || n == "System" || n == "netstandard" || n == "mscorlib")) return null; // 小游戏私有程序集放在版本目录里,载入本可卸载上下文 string candidate = Path.Combine(_moduleDir, assemblyName.Name + ".dll"); if (File.Exists(candidate)) return LoadBytes(candidate); return null; // 其余交默认上下文 } public Assembly LoadEntry(string serverDllPath) => LoadBytes(serverDllPath); private Assembly LoadBytes(string path) { using (var ms = new MemoryStream(File.ReadAllBytes(path))) return LoadFromStream(ms); } } }