Compare commits

2 Commits
Author SHA1 Message Date
anth64 d0dcd599cf fix(commandhookmodsystem): short-circuit Before dispatch on cancel
FireBefore continued invoking every registered listener even after one had set Cancel, running callbacks against a command that was already dead. Break out of the loop as soon as Cancel is set.
2026-06-17 19:13:14 +02:00
anth64 a130042206 perf(commanddata): take commandName directly instead of re-parsing FullCommand
Constructor previously rebuilt CommandName from FullCommand via substring + Split, allocating on every command invocation despite the caller already holding a clean commandName. Pass it through directly.
2026-06-17 19:13:14 +02:00
4 changed files with 15 additions and 7 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ public static class ChatCommandApiPatch
return true; return true;
var sender = args.Caller.Player as IServerPlayer; var sender = args.Caller.Player as IServerPlayer;
var data = new CommandData(sender, "/" + commandName); var data = new CommandData(sender, commandName, "/" + commandName);
if (system.FireBefore(commandName, ref data)) if (system.FireBefore(commandName, ref data))
{ {
+3 -3
View File
@@ -19,11 +19,11 @@ public struct CommandData
set => flags = value ? (byte)(flags | FlagCancel) : (byte)(flags & ~FlagCancel); set => flags = value ? (byte)(flags | FlagCancel) : (byte)(flags & ~FlagCancel);
} }
public CommandData(IServerPlayer? sender, string fullCommand) public CommandData(IServerPlayer? sender, string commandName, string fullCommand)
{ {
Sender = sender; Sender = sender;
FullCommand = fullCommand.Length > 0 ? fullCommand.Trim() : string.Empty; CommandName = commandName;
CommandName = FullCommand.Length > 1 ? FullCommand[1..].Split(' ')[0] : string.Empty; FullCommand = fullCommand;
flags = sender != null ? FlagIsPlayerCommand : (byte)0; flags = sender != null ? FlagIsPlayerCommand : (byte)0;
} }
} }
+8 -2
View File
@@ -109,8 +109,8 @@ public class CommandHookModSystem : ModSystem
} }
foreach (var wildcard in wildcards) foreach (var wildcard in wildcards)
foreach (var mods in builder.Values) foreach (var mods in builder.Values)
mods[wildcard.ModId] = wildcard.Registration; mods[wildcard.ModId] = wildcard.Registration;
var pruned = new Dictionary<string, FrozenDictionary<string, CommandRegistration>>( var pruned = new Dictionary<string, FrozenDictionary<string, CommandRegistration>>(
builder.Count builder.Count
@@ -125,8 +125,14 @@ public class CommandHookModSystem : ModSystem
internal bool FireBefore(string commandName, ref CommandData data) internal bool FireBefore(string commandName, ref CommandData data)
{ {
if (registrations.TryGetValue(commandName, out var mods)) if (registrations.TryGetValue(commandName, out var mods))
{
foreach (var (_, reg) in mods) foreach (var (_, reg) in mods)
{
reg.Before?.Invoke(ref data); reg.Before?.Invoke(ref data);
if (data.Cancel)
break;
}
}
return data.Cancel; return data.Cancel;
} }
+3 -1
View File
@@ -3,7 +3,9 @@
"modid": "commandhook", "modid": "commandhook",
"name": "CommandHook", "name": "CommandHook",
"description": "Exposes server command events for other mods to hook into.", "description": "Exposes server command events for other mods to hook into.",
"authors": ["anth64"], "authors": [
"anth64"
],
"version": "0.1.0", "version": "0.1.0",
"side": "Server", "side": "Server",
"dependencies": { "dependencies": {