Mods in Reloaded 1
In Reloaded 1, mods were loaded by performing DLL Injection on the individual mods and calling an exposed DLLExport-ed entry point, Main
.
In order to speed up mod loading, and support additional features such as real time loading, pausing, unloading and reloading of mods, this will change with Reloaded-II.
The Planned Reloaded 2 Way
Reloaded-II will instead have the Launcher inject the mod loader (originally: Reloaded-Loader)
directly into the application, which would then in turn load other mods using the native .NET APIs.
To interact with the loader, the launcher will use a DLLExport'ed API, thus removing all networking code that originally existed in Reloaded 1 (mostly for debugging purposes).
This should make loading mods faster by noticeably reducing the amount of times the OS has to create new threads and trash them for every mod load (CreateRemoteThread)
at the expenses of the cost of Reflection (which should still be faster) in finding the appropriate interface to load the mod from. Unfortunately however, this will not solve the main bottleneck of JIT recompilation as each mod is loaded into its own AppDomain
.
Additional benefits include features such as being able to load AnyCPU
assemblies directly instead of having to decide whether to load the x86
or x64
output of an AnyCPU
build.
Disclaimer
I would like to make it very clear here. The mod loader is an (optional) service. There wouldn't be anything forcing you to have a dependency on the mod loader. The mod loader is a service.
There is nothing stopping you from, for example, DLL Exporting Start()
and running the mod using a plain old DLL injector provided your mod does not rely on any of the mod loader services.
From experience, a majority of code mods will not rely on the services.
Proposed Mod API
This is the interface that every mod will require to implement to be loaded by Reloaded. It provides the mod entry point and serves as a way for the mod loader to interact with the mod.
public interface IMod
{
/* Events */
Action Disposing { get; } // Called for you automatically by the mod loader. Executed before a mod is disposed.
/* Actions */
void Start(IModLoader loader); // Entry point of the mod.
void Suspend(); // Pauses any active mod behaviour. (Undoes changes, deactivates function hooks etc.)
void Resume(); // Resumes any active mod behaviour. (Re-applies undone changes, re-activates hooks etc.)
void Unload(); // Pauses any active mod behaviour (same as suspend), and releases resources (e.g. File Handles, Native Memory)
// for the mod to be ejected (unloaded entirely) from the target program.
/* Capability */
bool CanUnload(); // Returns true if an Unload is supported/possible with this mod. (Manually defined by developer)
bool CanSuspend(); // Returns true if Suspend and Resume are supported with this mod. (Manually defined by developer)
/*
Suspend, Unload and Resume will require manual developer implementation.
In most cases, this will just resolve to deactivating function hooks and undoing any minor memory changes.
*/
}
Proposed Mod Loader API for Mods
This is the interface contained inside Reloaded which will allow for the mod to interact
with the mod loader itself.
public interface IModLoader
{
/* Information */
Version GetLoaderVersion(); // Retrieves the version of the mod loader. (May be useful in the future)
IApplicationConfig GetAppConfig(); // Returns the individual application configuration for the currently running application.
ModGenericTuple<IModConfig>[] GetActiveMods(); // Returns a list of currently active mods.
/* Events */
event Action OnModLoaderInitialized;// Called when all mods have finished loading.
event Action<IMod, IModConfig> ModUnloading; // Called before the loader unloads a mod.
event Action<IMod, IModConfig> ModLoading; // Called before the loader loads a mod.
event Action<IMod, IModConfig> ModLoaded; // Called after the loader loads a mod.
/*
Features below will require more ~~research~~ trial and error.
These features are juicy!
Taking maximum advantage of the nature of C# as a JIT-ed, managed language.
Note: I already know how to do this.
Just don't know if AppDomains will cause additional complications with this yet.
*/
/* Inter-Mod Communication: Plugins */
/*
Plugins will allow you to extend the functionality of other mods by implementing interfaces defined by them.
The way this will work is your mod will implement interface defined by another mod.
That other mod will use the API call below and make an instance of your class then, running your code.
Q: How do I get other mods' interfaces?
A: Mods that are extensible will publish packages containing only the interfaces. Preferably on NuGet.
*/
WeakReference<T>[] MakeInterfaces<T>(); // Visits all loaded mods and instantiates all classes which
// inherit from a given interface, returning all instances.
// Note 1: The mod from which the interface originates from is provided such
// that the interface can be removed when the moD is to be unloaded (see ModUnloading event).
// Note 2: This will probably be slow.
/* Inter-Mod Communication: Controllers */
/*
Sometimes you want one mod to be able to directly interact with another mod, rather
than just running code from it.
For example: Suppose we have a lighting mod that allows you to manipulate the lights in a 3D scene.
Now you, as a developer want to control it from another mod, you like flashing lights, you want to make
it rave!
So how do you do it? In the real life, to control a game you up a joystick and press buttons!
Why not do the same here?
If you are the lighting mod, you give your joystick (interface) to the mod loader.
If you are the rave mod, you ask the mod loader to give you the joystick.
Simple.
*/
void AddOrReplaceController<T>(IMod owner, T instance); // Adds a controller to the mod loader's stored list of controllers.
void RemoveController<T>(); // Removes a submitted controller (to be used when unloading a mod).
WeakReference<T> GetController<T>(); // Gets a controller from the mod loader's stored list of controllers.
/*
Controllers In Depth: Why not "MakeInterfaces"?
The problem is you need to interact with the loaded instance of the other mod.
Using the MakeInterfaces function would just create another instance of
the class to manipulate the lighting mod and thus have no effect.
The concept of the mod loader holding the interface instances is actually called Inversion of Control (IoC).
A great way to learn about IoC is this Stackoverflow post: https://stackoverflow.com/questions/3058/what-is-inversion-of-control
and the NInject (Dependency Injection Framework) wiki: https://github.com/ninject/Ninject/wiki/Dependency-Injection-By-Hand
*/
}
Suggestions down to the methods, method names, interface names etc. are welcome.
Definitions
public class ModGenericTuple<T>
{
public IMod Mod { get; set; }
public T Generic { get; set; }
}