"docs: add README"
This commit is contained in:
@@ -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.
|
||||||
|
|
||||||
Reference in New Issue
Block a user