Fix mod assembly loading on Windows.
This commit is contained in:
@@ -21,8 +21,6 @@ namespace OpenRA.FileSystem
|
||||
{
|
||||
public IEnumerable<IReadOnlyPackage> MountedPackages { get { return mountedPackages.Keys; } }
|
||||
readonly Dictionary<IReadOnlyPackage, int> mountedPackages = new Dictionary<IReadOnlyPackage, int>();
|
||||
|
||||
static readonly Dictionary<string, Assembly> AssemblyCache = new Dictionary<string, Assembly>();
|
||||
Cache<string, List<IReadOnlyPackage>> fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
|
||||
|
||||
public IReadWritePackage CreatePackage(string filename, Dictionary<string, byte[]> content)
|
||||
@@ -217,30 +215,5 @@ namespace OpenRA.FileSystem
|
||||
else
|
||||
return mountedPackages.Keys.Any(f => f.Contains(name));
|
||||
}
|
||||
|
||||
public static Assembly ResolveAssembly(object sender, ResolveEventArgs e)
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
if (assembly.FullName == e.Name)
|
||||
return assembly;
|
||||
|
||||
var frags = e.Name.Split(',');
|
||||
var filename = frags[0] + ".dll";
|
||||
|
||||
Assembly a;
|
||||
if (AssemblyCache.TryGetValue(filename, out a))
|
||||
return a;
|
||||
|
||||
if (Game.ModData.ModFiles.Exists(filename))
|
||||
using (var s = Game.ModData.ModFiles.Open(filename))
|
||||
{
|
||||
var buf = s.ReadBytes((int)s.Length);
|
||||
a = Assembly.Load(buf);
|
||||
AssemblyCache.Add(filename, a);
|
||||
return a;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,8 +235,6 @@ namespace OpenRA
|
||||
{
|
||||
Console.WriteLine("Platform is {0}", Platform.CurrentPlatform);
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += FileSystem.FileSystem.ResolveAssembly;
|
||||
|
||||
InitializeSettings(args);
|
||||
|
||||
Log.AddChannel("perf", "perf.log");
|
||||
|
||||
@@ -43,11 +43,7 @@ namespace OpenRA
|
||||
Manifest = new Manifest(mod);
|
||||
ModFiles.LoadFromManifest(Manifest);
|
||||
|
||||
// Allow mods to load types from the core Game assembly, and any additional assemblies they specify.
|
||||
var assemblies = new[] { typeof(Game).Assembly }
|
||||
.Concat(Manifest.Assemblies.Select(path => Assembly.Load(ModFiles.Open(path).ReadAllBytes())))
|
||||
.ToList();
|
||||
ObjectCreator = new ObjectCreator(assemblies);
|
||||
ObjectCreator = new ObjectCreator(Manifest, ModFiles);
|
||||
Manifest.LoadCustomData(ObjectCreator);
|
||||
|
||||
if (useLoadScreen)
|
||||
|
||||
@@ -10,23 +10,74 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class ObjectCreator
|
||||
{
|
||||
// .NET does not support unloading assemblies, so mod libraries will leak across mod changes.
|
||||
// This tracks the assemblies that have been loaded since game start so that we don't load multiple copies
|
||||
static readonly Dictionary<string, Assembly> ResolvedAssemblies = new Dictionary<string, Assembly>();
|
||||
|
||||
readonly Cache<string, Type> typeCache;
|
||||
readonly Cache<Type, ConstructorInfo> ctorCache;
|
||||
readonly Pair<Assembly, string>[] assemblies;
|
||||
|
||||
public ObjectCreator(IEnumerable<Assembly> sourceAssemblies)
|
||||
public ObjectCreator(Assembly a)
|
||||
{
|
||||
typeCache = new Cache<string, Type>(FindType);
|
||||
ctorCache = new Cache<Type, ConstructorInfo>(GetCtor);
|
||||
assemblies = sourceAssemblies.SelectMany(asm => asm.GetNamespaces().Select(ns => Pair.New(asm, ns))).ToArray();
|
||||
assemblies = a.GetNamespaces().Select(ns => Pair.New(a, ns)).ToArray();
|
||||
}
|
||||
|
||||
public ObjectCreator(Manifest manifest, FileSystem.FileSystem modFiles)
|
||||
{
|
||||
typeCache = new Cache<string, Type>(FindType);
|
||||
ctorCache = new Cache<Type, ConstructorInfo>(GetCtor);
|
||||
|
||||
// Allow mods to load types from the core Game assembly, and any additional assemblies they specify.
|
||||
var assemblyList = new List<Assembly>() { typeof(Game).Assembly };
|
||||
foreach (var path in manifest.Assemblies)
|
||||
{
|
||||
var data = modFiles.Open(path).ReadAllBytes();
|
||||
|
||||
// .NET doesn't provide any way of querying the metadata of an assembly without either:
|
||||
// (a) loading duplicate data into the application domain, breaking the world.
|
||||
// (b) crashing if the assembly has already been loaded.
|
||||
// We can't check the internal name of the assembly, so we'll work off the data instead
|
||||
string hash;
|
||||
using (var ms = new MemoryStream(data))
|
||||
using (var csp = SHA1.Create())
|
||||
hash = new string(csp.ComputeHash(data).SelectMany(a => a.ToString("x2")).ToArray());
|
||||
|
||||
Assembly assembly;
|
||||
if (!ResolvedAssemblies.TryGetValue(hash, out assembly))
|
||||
{
|
||||
assembly = Assembly.Load(data);
|
||||
ResolvedAssemblies.Add(hash, assembly);
|
||||
}
|
||||
|
||||
assemblyList.Add(assembly);
|
||||
}
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
|
||||
assemblies = assemblyList.SelectMany(asm => asm.GetNamespaces().Select(ns => Pair.New(asm, ns))).ToArray();
|
||||
AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly;
|
||||
}
|
||||
|
||||
Assembly ResolveAssembly(object sender, ResolveEventArgs e)
|
||||
{
|
||||
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
|
||||
if (a.FullName == e.Name)
|
||||
return a;
|
||||
|
||||
return assemblies.Select(a => a.First).FirstOrDefault(a => a.FullName == e.Name);
|
||||
}
|
||||
|
||||
public static Action<string> MissingTypeAction =
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace OpenRA.Test
|
||||
var yaml = MiniYaml.Merge(sources.Select(s => MiniYaml.FromString(s)));
|
||||
var allUnits = yaml.ToDictionary(node => node.Key, node => node.Value);
|
||||
var unit = allUnits[name];
|
||||
var creator = new ObjectCreator(new[] { typeof(ActorInfoTest).Assembly });
|
||||
var creator = new ObjectCreator(typeof(ActorInfoTest).Assembly);
|
||||
return new ActorInfo(creator, name, unit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,6 @@ namespace OpenRA.Utility
|
||||
return;
|
||||
}
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += FileSystem.FileSystem.ResolveAssembly;
|
||||
|
||||
Log.AddChannel("perf", null);
|
||||
Log.AddChannel("debug", null);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user