2026-06-25 20:19:15 +02:00
2026-06-25 20:19:15 +02:00
2025-12-02 21:21:13 +01:00
2025-12-02 21:21:13 +01:00
2025-12-02 21:21:13 +01:00
2025-12-02 21:43:46 +01:00

CommandHook

Server-side mod for Vintage Story. Lets other mods hook into chat commands before and after they run, namespaced per mod so multiple mods can watch the same command without colliding.

Usage

Install it like any other mod. It has no effect unless something else depends on it.

For mod development

Add it as a dependency in your modinfo.json:

"dependencies": {
    "game": "1.22.3",
    "commandhook": "1.0.0"
}

CommandHook patches ChatCommandApi.Execute and dispatches to whoever has registered. Your mod doesn't patch anything itself, it just implements ICommandHookListener and calls CommandHookModSystem.Register.

A listener provides:

  • ModId, your mod's id. Used to namespace your registration so it doesn't collide with another mod watching the same command
  • Commands, the command names you want to watch, without the leading slash
  • Registration, a Before delegate, an After delegate, or both

Before runs before the command executes. Set data.Cancel = true inside it to stop the command from running. If you cancel, any remaining Before listeners for that command are skipped, and After never fires for that invocation.

After runs once the command has produced a result. It only fires on the path where the command actually ran.

CommandData is passed by ref the whole way through, no allocation per command.

Example

public class MyListener : ICommandHookListener
{
    // Your mod's id, used to namespace this registration.
    public string ModId => "mymod";

    // Commands you want to watch, no leading slash.
    public IReadOnlyList<string> Commands => new[] { "tp" };

    // Wire up Before, After, or both.
    public CommandRegistration Registration => new(Before, After);

    private void Before(ref CommandData data)
    {
        // data.Sender is null for console invocations.
        if (data.Sender != null && !IsAllowed(data.Sender))
        {
            // Stops the command from running and skips any remaining
            // Before listeners. After never fires for this invocation.
            data.Cancel = true;
        }
    }

    private void After(ref CommandData data, TextCommandResult result)
    {
        // Only runs if nothing cancelled. result is whatever the command
        // actually produced, check result.Status for success/error/deferred.
        if (result.Status != EnumCommandStatus.Success)
            Logger.Warn($"/{data.CommandName} failed: {result.StatusMessage}");
    }
}

Register it once your mod starts, unregister it on dispose:

public override void StartServerSide(ICoreServerAPI api)
{
    // CommandHookModSystem.Instance is null if CommandHook isn't loaded,
    // hence the ?. Register your listener once, here.
    CommandHookModSystem.Instance?.Register(myListener);
}

public override void Dispose()
{
    // Always unregister on dispose, otherwise a stale listener stays in
    // the dispatch table after your mod is gone.
    CommandHookModSystem.Instance?.Unregister(myListener);
}

Calling Register again with the same ModId replaces your existing registration. If your Commands list hasn't changed, it's a no-op.

S
Description
A server-side API for routing and overriding vanilla commands.
Readme MPL-2.0
134 KiB
Languages
C# 99.3%
PowerShell 0.4%
Shell 0.3%