HookDOTS API
Provides an API for mods to hook Unity DOTS systems.
About this mod Author’s original text
HookDOTS API
HookDOTS is a modding library for hooking Unity DOTS games. By itself, it doesn't do anything.
Why use HookDOTS?
Harmony is an excellent tool, but it fails at one important thing: Hooking unmanaged systems in IL2CPP builds.\
HookDOTS was created to solve this problem.
Notable attributes provided (for plugin developers):
- [EcsSystemUpdatePrefix(typeof(EquipItemSystem))]
- [EcsSystemUpdatePostfix(typeof(EquipItemSystem))]
- [WhenCreatedWorldsContainAny("Server", "Client_0")]
Installation
- Install BepInEx
- Extract HookDOTS.API.dll into (VRising folder)/BepInEx/plugins
Usage
For Server Operators
By itself, HookDOTS doesn't do anything. It is required by other plugins and should be kept up to date.
For Plugin Developers
How to use
- Add a reference to the plugin.
dotnet add package HookDOTS.API -v 1.*
- Add the API plugin as a dependency via the BepInDependency attribute on your plugin class.
[BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
[BepInDependency("HookDOTS.API")]
public class ExamplePlugin: BasePlugin
- Register hooks in your plugin's Load method.
public override void Load()
{
//... other initialization code
// Register your plugin's hooks with HookDOTS
_hookDOTS = new HookDOTS.API.HookDOTS(MyPluginInfo.PLUGIN_GUID, Log);
_hookDOTS.RegisterAnnotatedHooks();
}
- Teardown in your plugin's Unload method.
public override bool Unload()
{
//... other unloading code
_hookDOTS.Dispose();
return true;
}
- Set up prefix and postifx hooks for System OnUpdate calls. Both managed and unmanaged systems can be hooked.
public class ExamplePatch
{
// The hook must be public and static.
// If you want SystemState* passed in, an unsafe context is also required.
[EcsSystemUpdatePrefix(typeof(EquipItemSystem))]
unsafe public static bool ExamplePrefix(SystemState* systemState)
{
var world = systemState->World;
ExamplePlugin.LogInstance.LogInfo($"ExamplePrefix executing in world {world.Name}.");
// You can return false, to skip the hooked OnUpdate. Other prefix hooks will still run.
bool shouldSkipTheOriginal = true;
return!shouldSkipTheOriginal; // this will be returned, and is false. Therefore the original will be skipped.
}
[EcsSystemUpdatePrefix(typeof(EquipItemSystem))]
unsafe public static bool ExamplePrefixNoSkip(SystemState* systemState)
{
// Prefer to use the AfterDetours constants instead of true/false.
// return AfterDetours.SkipOriginalMethod;
return AfterDetours.OkToRunOriginalMethod;
}
[EcsSystemUpdatePostfix(typeof(EquipItemSystem))]
unsafe public static void ExamplePostfix(SystemState* systemState)
{
var world = systemState->World;
ExamplePlugin.LogInstance.LogInfo($"ExamplePostfix executing in world {world.Name}.");
// Unlike a prefix hook, a postfix hook cannot return false to skip anything.
// The only valid return type is void.
}
}
onlyWhenSystemRuns
You can set onlyWhenSystemRuns to false, and the hook will be called even if the system doesn't actually run.
[EcsSystemUpdatePrefix(typeof(EquipItemSystem), onlyWhenSystemRuns: false)]
public static void ExamplePrefix2()
{
// (this is commented out, because the log would be spammed every frame)
// ExamplePlugin.LogInstance.LogInfo($"ExamplePrefix2 executing");
}
Throttling
If you're using hooks to dump information and don't want to be flooded every frame, the Throttle attribute can be used.
// You can specify days, hours, minutes, seconds, and milliseconds to define the throttle interval.
// Internally, these are used to create a System.TimeSpan
[Throttle(seconds: 10)]
[EcsSystemUpdatePrefix(typeof(EquipItemSystem), onlyWhenSystemRuns: false)]
public static void ExamplePrefixThrottled()
{
ExamplePlugin.LogInstance.LogInfo($"ExamplePrefixThrottled executing (throttled to once every 10 seconds)");
}
Alternative ways to register hooks
Procedurally, using a HookRegistrar:
var hookDOTS = new HookDOTS.API.HookDOTS(MyPluginInfo.PLUGIN_GUID, Log);
//...
var hook =
Install & use Install: BepInEx plugin
- The archive from playbuff is the mod itself — usually a BepInEx plugin (a .dll or a plugin folder once unzipped).
- Recommended: use a community mod manager (such as r2modman) — pick this game, import the mod, and the manager sets up BepInEx and its dependencies for you.
- Manual route: install the BepInEx loader in the game folder, then drop the unzipped .dll or plugin folder into BepInEx/plugins/.
- Launch the game — the BepInEx console logs every plugin it loads; seeing this mod in the log means it is installed.
Mods with listed dependencies need those installed too, or they will not load. Options are usually changed in-game or in the plugin's config file.
Install these first, or the mod will not work: