Files
LegacyWeaveLoader/docs/MIXINS.md
2026-03-10 22:37:07 -05:00

4.1 KiB

Mixins

This guide explains how to hook game functions using Weave Loader mixins.

What is a mixin?

A mixin lets you attach your code to an existing game function. You can run code:

  • At the start of a function (head)
  • At the end of a function (tail)

1. Register a mixin class

Create a file named weave.mixins.json in your mod folder:

{
  "package": "ExampleMod.Mixins",
  "mixins": [
    "CreeperExplosionMixin"
  ]
}

Fields (currently supported):

  • package: C# namespace that contains your mixin classes
  • mixins: Class names under that namespace
  • client, server: Optional lists (loaded the same way as mixins)

Fields (parsed but not enforced yet):

  • required
  • injectors.defaultRequire

Place the file next to your mod DLL in build/mods/<YourMod>/.

2. Create a mixin class

Target class

using WeaveLoader.API.Mixins;

[Mixin("Creeper")]
public class CreeperExplosionMixin
{
}

The string in [Mixin("...")] is the class name used in mapping.json.

Inject at the start (head)

using WeaveLoader.API.Mixins;

[Mixin("Creeper")]
public class CreeperExplosionMixin
{
    [Inject("tick", At.Head)]
    private static void OnTick(MixinContext ctx)
    {
        // Code runs before Creeper::tick
    }
}

Inject at the end (tail)

using WeaveLoader.API.Mixins;

[Mixin("Creeper")]
public class CreeperExplosionMixin
{
    [Inject("tick", At.Tail)]
    private static void OnTick(MixinContext ctx)
    {
        // Code runs after Creeper::tick
    }
}

3. Using arguments and return values

Mixin methods must be static and return void. They can take either:

  • No parameters
  • One parameter: MixinContext

MixinContext lets you read or change arguments and return values.

using WeaveLoader.API.Mixins;

[Mixin("Item")]
public class ItemMixin
{
    [Inject("use", At.Head)]
    private static void OnUse(MixinContext ctx)
    {
        var arg0 = ctx.GetArg(0);
        var arg1 = ctx.GetArg(1);
        // use ctx.SetArg(index, value) to modify
    }

    [Inject("use", At.Tail)]
    private static void AfterUse(MixinContext ctx)
    {
        var ret = ctx.GetReturn();
        // use ctx.SetReturn(value) to modify
    }
}

4. Accessing the instance (this)

For non-static methods, the instance pointer is always arg 0:

var self = ctx.GetArg(0);

For static methods, ctx.ThisPtr will be 0.

5. Canceling the original method

You can skip the original game method at head injection:

using WeaveLoader.API.Mixins;
using WeaveLoader.API.Native;

[Mixin("SomeClass")]
public class ExampleCancel
{
    [Inject("doThing", At.Head)]
    private static void OnDoThing(MixinContext ctx)
    {
        ctx.SetReturn(new NativeRet { Type = NativeType.I32, Value = 0 });
        ctx.Cancel();
    }
}

Canceling is only effective at the head. Tail injections run after the original method.

6. Picking the right method name

InjectAttribute takes either:

  • A method name ("tick") which becomes Class::tick
  • A full name ("Creeper::tick") from mapping.json

If you have overloads, use the exact fullName from mapping.json.

7. Confirm it is hooked

When a mixin loads, you should see logs like:

Processing mixin: ExampleMod.Mixins.CreeperExplosionMixin -> Creeper
HookRegistry: hooked Creeper::tick
Mixin hook installed: Creeper::tick (Tail)

If you do not see these logs:

  • Check weave.mixins.json is next to the mod DLL
  • Check the namespace in package
  • Check the class name and method name match the PDB mapping
  • Check the mod loaded at all

8. Common issues

  • Wrong class or method name: PDB names are case-sensitive.
  • Crashes when accessing args: Make sure the method signature matches the game method exactly.
  • Mixins not running: Verify you see the “hook installed” log.

9. Logging

Use Logger.Debug inside mixins while testing:

Logger.Debug("Creeper mixin tick");

Remove or reduce logs for release builds to avoid overhead.

10. Limits and not supported yet

  • Hooks support up to 6 arguments.
  • Overwrite mixins are not supported yet.
  • Invoker mixins are not supported yet.