refactor: rework registration system with listener interface and rebuild pattern

This commit is contained in:
2026-06-11 21:48:10 +02:00
parent 6d4f964e74
commit e26ad1d541
+88 -45
View File
@@ -10,6 +10,9 @@ public class CommandHookModSystem : ModSystem
{ {
internal static CommandHookModSystem? Instance; internal static CommandHookModSystem? Instance;
private readonly List<ICommandHookListener> listeners = new();
private readonly List<ICommandHookListener> wildcards = new();
private FrozenDictionary<string, FrozenDictionary<string, CommandRegistration>> registrations = private FrozenDictionary<string, FrozenDictionary<string, CommandRegistration>> registrations =
FrozenDictionary<string, FrozenDictionary<string, CommandRegistration>>.Empty; FrozenDictionary<string, FrozenDictionary<string, CommandRegistration>>.Empty;
@@ -17,58 +20,106 @@ public class CommandHookModSystem : ModSystem
public override bool ShouldLoad(EnumAppSide forSide) => forSide == EnumAppSide.Server; public override bool ShouldLoad(EnumAppSide forSide) => forSide == EnumAppSide.Server;
public override double ExecuteOrder() => 0.0;
public override void StartServerSide(ICoreServerAPI api) public override void StartServerSide(ICoreServerAPI api)
{ {
Instance = this; Instance = this;
harmony = new Harmony(Mod.Info.ModID); harmony = new Harmony(Mod.Info.ModID);
harmony.PatchAll(); harmony.PatchAll();
api.Event.ServerRunPhase(EnumServerRunPhase.RunGame, Rebuild);
Mod.Logger.Notification("Loaded"); Mod.Logger.Notification("Loaded");
} }
public override void Dispose() public override void Dispose()
{ {
harmony?.UnpatchAll(Mod.Info.ModID); harmony?.UnpatchAll(Mod.Info.ModID);
listeners.Clear();
wildcards.Clear();
Instance = null; Instance = null;
} }
public void Register( public void Register(ICommandHookListener listener)
string modId,
CommandRegistration registration,
params string[] commandNames
)
{ {
var builder = Thaw(); var commands = listener.Commands;
foreach (var commandName in commandNames) if (commands == null || commands.Count == 0)
{ {
if (!builder.TryGetValue(commandName, out var target)) Unregister(listener);
{ return;
target = new Dictionary<string, CommandRegistration>();
builder[commandName] = target;
}
target[modId] = registration;
} }
registrations = Freeze(builder); for (int i = 0; i < listeners.Count; i++)
{
if (listeners[i].ModId != listener.ModId)
continue;
if (CommandListEquals(listeners[i].Commands, commands))
return;
listeners[i] = listener;
SyncWildcard(listener, commands);
Rebuild();
return;
}
listeners.Add(listener);
SyncWildcard(listener, commands);
} }
public void Unregister(string modId, params string[] commandNames) public void Unregister(ICommandHookListener listener)
{ {
var builder = Thaw(); for (int i = 0; i < listeners.Count; i++)
{
if (listeners[i].ModId != listener.ModId)
continue;
if (commandNames.Length == 0) listeners.RemoveAt(i);
{ wildcards.RemoveAll(w => w.ModId == listener.ModId);
foreach (var (_, mods) in builder) Rebuild();
mods.Remove(modId); return;
} }
else }
public void Rebuild()
{
var builder = new Dictionary<string, Dictionary<string, CommandRegistration>>();
foreach (var listener in listeners)
{ {
foreach (var commandName in commandNames) var commands = listener.Commands;
if (builder.TryGetValue(commandName, out var mods)) if (commands == null || commands.Count == 0)
mods.Remove(modId); continue;
bool isWildcard = commands.Count == 1 && commands[0] == "*";
if (isWildcard)
continue;
foreach (var cmd in commands)
{
if (!builder.TryGetValue(cmd, out var mods))
{
mods = new Dictionary<string, CommandRegistration>();
builder[cmd] = mods;
}
mods[listener.ModId] = listener.Registration;
}
} }
registrations = Freeze(builder); foreach (var wildcard in wildcards)
foreach (var mods in builder.Values)
mods[wildcard.ModId] = wildcard.Registration;
var pruned = new Dictionary<string, FrozenDictionary<string, CommandRegistration>>(
builder.Count
);
foreach (var (cmd, mods) in builder)
if (mods.Count > 0)
pruned[cmd] = mods.ToFrozenDictionary();
registrations = pruned.ToFrozenDictionary();
} }
public bool FireBefore(string commandName, ref CommandData data) public bool FireBefore(string commandName, ref CommandData data)
@@ -87,31 +138,23 @@ public class CommandHookModSystem : ModSystem
reg.After?.Invoke(ref data, result); reg.After?.Invoke(ref data, result);
} }
private Dictionary<string, Dictionary<string, CommandRegistration>> Thaw() private void SyncWildcard(ICommandHookListener listener, IReadOnlyList<string> commands)
{ {
var builder = new Dictionary<string, Dictionary<string, CommandRegistration>>(); bool isWildcard = commands.Count == 1 && commands[0] == "*";
wildcards.RemoveAll(w => w.ModId == listener.ModId);
foreach (var (cmd, mods) in registrations) if (isWildcard)
{ wildcards.Add(listener);
var inner = new Dictionary<string, CommandRegistration>();
foreach (var (id, reg) in mods)
inner[id] = reg;
builder[cmd] = inner;
}
return builder;
} }
private static FrozenDictionary<string, FrozenDictionary<string, CommandRegistration>> Freeze( private static bool CommandListEquals(IReadOnlyList<string> a, IReadOnlyList<string> b)
Dictionary<string, Dictionary<string, CommandRegistration>> builder
)
{ {
var pruned = new Dictionary<string, FrozenDictionary<string, CommandRegistration>>(); if (a.Count != b.Count)
return false;
foreach (var (cmd, mods) in builder) for (int i = 0; i < a.Count; i++)
if (mods.Count > 0) if (a[i] != b[i])
pruned[cmd] = mods.ToFrozenDictionary(); return false;
return pruned.ToFrozenDictionary(); return true;
} }
} }