"docs: add README"

This commit is contained in:
2026-06-21 12:39:34 +02:00
parent 0b030bb2a8
commit 2a7cdb4fad
+86
View File
@@ -0,0 +1,86 @@
# 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.
## How it works
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
```csharp
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:
```csharp
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.