/// <summary> /// Find and load all ModBases in the given assembly. /// </summary> /// <param name="meta">The mod metadata, preferably from the mod metadata.yaml file.</param> /// <param name="asm">The mod assembly, preferably relinked.</param> public static void LoadModAssembly(ModMetadata meta, Assembly asm) { if (meta != null) { ModContentManager.Crawl(new AssemblyModContent(asm)); } Type[] types; try { types = asm.GetTypes(); } catch (Exception e) { Logger.Log(LogLevel.Warn, "loader", $"Failed reading assembly: {e}"); e.LogDetailed(); return; } for (int i = 0; i < types.Length; i++) { Type type = types[i]; if (!typeof(ModBase).IsAssignableFrom(type) || type.IsAbstract) { continue; } ModBase mod = (ModBase)type.GetConstructor(ModManager._EmptyTypeArray).Invoke(ModManager._EmptyObjectArray); if (meta != null) { mod.Metadata = meta; } mod.Register(); } }
/// <summary> /// Unregisters an already registered ModBase (mod) dynamically. Invokes Unload. /// </summary> /// <param name="module"></param> public static void Unregister(this ModBase module) { module.Unload(); lock (_Mods) { int index = _Mods.IndexOf(module); _Mods.RemoveAt(index); _ModuleTypes.RemoveAt(index); _ModuleMethods.RemoveAt(index); } Logger.Log(LogLevel.Info, "core", $"Module {module.Metadata} unregistered."); }
public static void Boot(string name, string version, ModBase coreModule) { if (_Booted) { return; } _Booted = true; Name = name; VersionString = version; Logger.Log(LogLevel.Info, "core", $"Booting {name}"); Logger.Log(LogLevel.Info, "core", $"Version: {VersionString}"); if (string.IsNullOrEmpty(PathGame)) { PathGame = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); } if (string.IsNullOrEmpty(PathSettings)) { PathSettings = Path.Combine(PathGame, "ModSettings"); } Directory.CreateDirectory(PathSettings); // Automatically load all modules. if (coreModule != null) { coreModule.Metadata = coreModule.Metadata ?? new ModMetadata { Name = Name, Version = Version }; Register(coreModule); } ModLoader.LoadAuto(); // Also let all mods parse the arguments. Queue <string> args = new Queue <string>(Args); while (args.Count > 0) { string arg = args.Dequeue(); foreach (ModBase mod in Mods) { if (mod.ParseArg(arg, args)) { break; } } } }
/// <summary> /// Register a new ModBase (mod) dynamically. Invokes LoadSettings and Load. /// </summary> /// <param name="module">Mod to register.</param> public static void Register(this ModBase module) { lock (_Mods) { _Mods.Add(module); _ModuleTypes.Add(module.GetType()); _ModuleMethods.Add(new Dictionary <string, MethodInfo>()); _ModuleMethodDelegates.Add(new Dictionary <string, FastReflectionDelegate>()); } module.LoadSettings(); module.Load(); Logger.Log(LogLevel.Info, "core", $"Module {module.Metadata} registered."); // Attempt to load mods after their dependencies have been loaded. // Only load and lock the delayed list if we're not already loading delayed mods. if (Interlocked.CompareExchange(ref ModLoader.DelayedLock, 1, 0) == 0) { lock (ModLoader.Delayed) { for (int i = ModLoader.Delayed.Count - 1; i > -1; i--) { ModLoader.DelayedEntry entry = ModLoader.Delayed[i]; if (!ModLoader.DependenciesLoaded(entry.Meta)) { continue; } ModLoader.LoadMod(entry.Meta); ModLoader.Delayed.RemoveAt(i); entry.Callback?.Invoke(); } } Interlocked.Decrement(ref ModLoader.DelayedLock); } }
/// <summary> /// Invoke a method in all loaded ModBases. /// </summary> /// <param name="methodName">The name of the method.</param> /// <param name="argsTypes">The types of the arguments passed to the methods.</param> /// <param name="args">Any arguments to be passed to the methods.</param> public static void InvokeTyped(string methodName, Type[] argsTypes, params object[] args) { if (args == null) { args = _EmptyObjectArray; if (argsTypes == null) { argsTypes = _EmptyTypeArray; } } else if (argsTypes == null) { argsTypes = Type.GetTypeArray(args); } if (!Debugger.IsAttached) { // Fast codepath: DynamicMethodDelegate // Unfortunately prevents us from stepping into invoked methods. for (int i = 0; i < _Mods.Count; i++) { ModBase module = _Mods[i]; IDictionary <string, FastReflectionDelegate> moduleMethods = _ModuleMethodDelegates[i]; FastReflectionDelegate method; if (moduleMethods.TryGetValue(methodName, out method)) { if (method == null) { continue; } method(module, args); continue; } MethodInfo methodInfo = _ModuleTypes[i].GetMethod(methodName, argsTypes); if (methodInfo != null) { method = methodInfo.GetFastDelegate(); } moduleMethods[methodName] = method; if (method == null) { continue; } method(module, args); } } else { // Slow codepath: MethodInfo.Invoke // Doesn't hinder us from stepping into the invoked methods. for (int i = 0; i < _Mods.Count; i++) { ModBase module = _Mods[i]; IDictionary <string, MethodInfo> moduleMethods = _ModuleMethods[i]; MethodInfo methodInfo; if (moduleMethods.TryGetValue(methodName, out methodInfo)) { if (methodInfo == null) { continue; } methodInfo.Invoke(module, args); continue; } methodInfo = _ModuleTypes[i].GetMethod(methodName, argsTypes); moduleMethods[methodName] = methodInfo; if (methodInfo == null) { continue; } methodInfo.Invoke(module, args); } } }