Skip to content

Commit fcd0aca

Browse files
committed
To reflect what I talked about in northwood-studios#222
1 parent 2749a86 commit fcd0aca

File tree

2 files changed

+121
-56
lines changed

2 files changed

+121
-56
lines changed

NwPluginAPI/Loader/AssemblyLoader.cs

Lines changed: 97 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,20 @@ public static void Initialize()
7474
FactoryManager.Init();
7575
EventManager.Init();
7676

77+
Log.Info("<---< Loading global dependencies <---<");
78+
// Load dependencies from the Global directory inside "configs".
79+
LoadDependencies(Paths.GlobalPlugins.Dependencies);
80+
81+
Log.Info("<---< Loading server dependencies <---<");
82+
// Load dependencies from the [Server port] directory inside "configs".
83+
LoadDependencies(Paths.LocalPlugins.Dependencies);
84+
7785
Log.Info("<---< Loading global plugins <---<");
7886
// Load plugins from the Global directory inside "configs".
79-
LoadDependencies(Paths.GlobalPlugins.Dependencies);
8087
LoadPlugins(Paths.GlobalPlugins);
8188

8289
Log.Info("<---< Loading server plugins <---<");
8390
// Load plugins from the [Server port] directory inside "configs".
84-
LoadDependencies(Paths.LocalPlugins.Dependencies);
8591
LoadPlugins(Paths.LocalPlugins);
8692

8793
Log.Info("<---< Load all plugins <---<");
@@ -107,89 +113,120 @@ public static void Initialize()
107113
/// <param name="directory">The paths from which to load plugins and their configs from.</param>
108114
private static void LoadPlugins(PluginDirectory directory)
109115
{
110-
string[] files = Directory.GetFiles(directory.Plugins, "*.dll");
111-
112-
Log.Info($"Loading &2{files.Length}&r plugins...");
113-
int successes = 0;
114-
115-
var loadedAssemblies = AppDomain.CurrentDomain
116-
.GetAssemblies()
117-
.Select(x =>
118-
$"{x.GetName().Name}&r v&6{x.GetName().Version.ToString(3)}");
119-
120-
foreach (string pluginPath in files)
116+
try
121117
{
122-
if (!TryGetAssembly(pluginPath, out Assembly assembly))
123-
continue;
118+
string[] files = Directory.GetFiles(directory.Plugins, "*.dll");
124119

125-
Type[] types = null;
120+
Log.Info($"Loading &2{files.Length}&r plugins...");
121+
int successes = 0;
126122

127-
var missingDependencies = assembly
128-
.GetReferencedAssemblies()
129-
.Select(x =>
130-
$"{x.Name}&r v&6{x.Version.ToString(3)}")
131-
.Where(x => !loadedAssemblies.Contains(x)).ToArray();
123+
var loadedAssemblies = AppDomain.CurrentDomain
124+
.GetAssemblies()
125+
.ToDictionary(x => x.GetName().Name, x => x.GetName().Version);
132126

133-
try
134-
{
135-
if (missingDependencies.Length != 0)
136-
ResolveAssemblyEmbeddedResources(assembly);
137-
types = assembly.GetTypes();
138-
}
139-
catch (Exception e)
127+
var loadedPluginAssemblies = new List<PluginAssemblyInformation>();
128+
var pluginsToInitialize = new List<PluginAssemblyInformation>();
129+
130+
foreach (string pluginPath in files)
140131
{
141-
if (missingDependencies.Length != 0)
142-
{
143-
Log.Error($"Failed loading plugin &2{Path.GetFileNameWithoutExtension(pluginPath)}&r, missing dependencies\n&2{string.Join("\n", missingDependencies.Select(x => $"&r - &2{x}&r"))}\n\n{e}", "Loader");
132+
if (!TryGetAssembly(pluginPath, out Assembly assembly))
144133
continue;
145-
}
146-
147-
Log.Error($"Failed loading plugin &2{Path.GetFileNameWithoutExtension(pluginPath)}&r, {e.ToString()}");
148-
continue;
134+
loadedPluginAssemblies.Add(new PluginAssemblyInformation(pluginPath, assembly));
135+
loadedAssemblies[assembly.GetName().Name] = assembly.GetName().Version;
149136
}
150137

151-
foreach (var entryType in types)
138+
foreach (var pluginInfo in loadedPluginAssemblies)
152139
{
140+
var missingDependencies = pluginInfo.Assembly
141+
.GetReferencedAssemblies()
142+
.Where(x => !loadedAssemblies.ContainsKey(x.Name))
143+
.ToDictionary(x => x.Name, x => x.Version);
144+
var versionMismatch = pluginInfo.Assembly
145+
.GetReferencedAssemblies()
146+
.Where(x => loadedAssemblies.ContainsKey(x.Name) && loadedAssemblies[x.Name] != x.Version)
147+
.ToDictionary(x => x.Name, x => (Expected: x.Version, Actual: loadedAssemblies[x.Name]));
148+
153149
try
154150
{
155-
if (!entryType.IsValidEntrypoint()) continue;
151+
if (missingDependencies.Count != 0)
152+
ResolveAssemblyEmbeddedResources(pluginInfo.Assembly, missingDependencies);
153+
154+
pluginInfo.Types = pluginInfo.Assembly.GetTypes();
155+
pluginsToInitialize.Add(pluginInfo);
156156
}
157-
catch (Exception ex)
157+
catch (Exception e)
158158
{
159-
Log.Error($"Failed checking entrypoint for plugin &2{Path.GetFileNameWithoutExtension(pluginPath)}&r.\n{ex}");
159+
if (missingDependencies.Count != 0)
160+
{
161+
Log.Error($"Failed loading plugin &2{Path.GetFileNameWithoutExtension(pluginInfo.Path)}&r, missing dependencies\n&2{string.Join("\n", missingDependencies.Select(x => "&r - &2" + x.Key + " v" + x.Value.ToString(3) + "&r"))}\n\n{e}", "Loader");
162+
}
163+
else
164+
{
165+
Log.Error("Failed loading plugin &2" + Path.GetFileNameWithoutExtension(pluginInfo.Path) + "&r, " + e, "Loader");
166+
}
167+
160168
continue;
161169
}
162170

163-
if (!Plugins.ContainsKey(assembly)) Plugins.Add(assembly, new Dictionary<Type, PluginHandler>());
171+
if (versionMismatch.Count != 0)
172+
{
173+
Log.Warning($"Dependency version mismatch in plugin &2{Path.GetFileNameWithoutExtension(pluginInfo.Path)}&r\n&2{string.Join("\n", versionMismatch.Select(x => "&r - &2" + x.Key + " v" + x.Value.Actual.ToString(3) + " (expected version by plugin: " + x.Value.Expected.ToString(3) + ")" + "&r"))}", "Loader");
174+
}
175+
}
164176

165-
if (!Plugins[assembly].ContainsKey(entryType))
177+
Log.Info($"Initializing &2{pluginsToInitialize.Count}&r plugins...");
178+
foreach (var pluginInfo in pluginsToInitialize)
179+
{
180+
var pluginPath = pluginInfo.Path;
181+
var assembly = pluginInfo.Assembly;
182+
var types = pluginInfo.Types;
183+
foreach (var entryType in types)
166184
{
167-
object plugin = null;
168185
try
169186
{
170-
plugin = Activator.CreateInstance(entryType);
187+
if (!entryType.IsValidEntrypoint()) continue;
171188
}
172189
catch (Exception ex)
173190
{
174-
Log.Error($"Failed creating instance of plugin &2{Path.GetFileNameWithoutExtension(pluginPath)}&r.\n{ex}", "Loader");
191+
Log.Error($"Failed checking entrypoint for plugin &2{Path.GetFileNameWithoutExtension(pluginPath)}&r.\n{ex}");
175192
continue;
176193
}
177194

178-
PluginToAssembly.Add(plugin, assembly);
195+
if (!Plugins.ContainsKey(assembly)) Plugins.Add(assembly, new Dictionary<Type, PluginHandler>());
179196

180-
Plugins[assembly].Add(entryType, new PluginHandler(directory, plugin, entryType, types)
197+
if (!Plugins[assembly].ContainsKey(entryType))
181198
{
182-
PluginFilePath = pluginPath
183-
});
184-
successes++;
199+
object plugin = null;
200+
try
201+
{
202+
plugin = Activator.CreateInstance(entryType);
203+
}
204+
catch (Exception ex)
205+
{
206+
Log.Error($"Failed creating instance of plugin &2{Path.GetFileNameWithoutExtension(pluginPath)}&r.\n{ex}", "Loader");
207+
continue;
208+
}
209+
210+
PluginToAssembly.Add(plugin, assembly);
211+
212+
Plugins[assembly].Add(entryType, new PluginHandler(directory, plugin, entryType, types)
213+
{
214+
PluginFilePath = pluginPath
215+
});
216+
successes++;
217+
}
185218
}
186219
}
187-
}
188220

189-
if (successes > 0)
190-
CustomNetworkManager.Modded = true;
221+
if (successes > 0)
222+
CustomNetworkManager.Modded = true;
191223

192-
Log.Info($"Loaded &2{successes}&r/&2{files.Length}&r plugins.");
224+
Log.Info($"Loaded &2{successes}&r/&2{files.Length}&r plugins.");
225+
}
226+
catch (Exception e)
227+
{
228+
Log.Error(e.ToString());
229+
}
193230
}
194231

195232
/// <summary>
@@ -217,6 +254,7 @@ private static void LoadDependencies(string path)
217254
Log.Error($"Failed loading dependency &2{Path.GetFileNameWithoutExtension(dependencyPath)}&r.\n{ex}");
218255
continue;
219256
}
257+
220258
successes++;
221259
}
222260

@@ -249,7 +287,8 @@ private static bool TryGetAssembly(string path, out Assembly assembly)
249287
/// Attempts to load Embedded assemblies (compressed) from the target
250288
/// </summary>
251289
/// <param name="target">Assembly to check for embedded assemblies</param>
252-
private static void ResolveAssemblyEmbeddedResources(Assembly target)
290+
/// <param name="missingDependencies"></param>
291+
private static void ResolveAssemblyEmbeddedResources(Assembly target, Dictionary<string, Version> missingDependencies)
253292
{
254293
Log.Debug($"Attempting to load embedded resources for {target.FullName}", Log.DebugMode);
255294

@@ -271,7 +310,8 @@ private static void ResolveAssemblyEmbeddedResources(Assembly target)
271310
}
272311

273312
dataStream.CopyTo(stream);
274-
Assembly.Load(stream.ToArray());
313+
var assemblyName = Assembly.Load(stream.ToArray()).GetName();
314+
missingDependencies.Remove(assemblyName.Name);
275315
Log.Debug($"Loaded {name}", Log.DebugMode);
276316
}
277317
}
@@ -289,7 +329,8 @@ private static void ResolveAssemblyEmbeddedResources(Assembly target)
289329
{
290330
Log.Debug($"Loading {name}", Log.DebugMode);
291331
stream.CopyTo(memStream);
292-
Assembly.Load(memStream.ToArray());
332+
var assemblyName = Assembly.Load(memStream.ToArray()).GetName();
333+
missingDependencies.Remove(assemblyName.Name);
293334
Log.Debug($"Loaded {name}", Log.DebugMode);
294335
}
295336
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace PluginAPI.Loader
2+
{
3+
4+
using System;
5+
using System.Reflection;
6+
7+
internal sealed class PluginAssemblyInformation
8+
{
9+
10+
public PluginAssemblyInformation(string path, Assembly assembly)
11+
{
12+
Path = path;
13+
Assembly = assembly;
14+
}
15+
16+
public Type[] Types = Array.Empty<Type>();
17+
18+
public readonly string Path;
19+
20+
public readonly Assembly Assembly;
21+
22+
}
23+
24+
}

0 commit comments

Comments
 (0)