Compare commits

3 Commits
2 changed files with 106 additions and 53 deletions
+93 -50
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,61 +20,109 @@ 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++)
}
public void Unregister(string modId, params string[] commandNames)
{ {
var builder = Thaw(); if (listeners[i].ModId != listener.ModId)
continue;
if (commandNames.Length == 0) if (CommandListEquals(listeners[i].Commands, commands))
return;
listeners[i] = listener;
SyncWildcard(listener, commands);
Rebuild();
return;
}
listeners.Add(listener);
SyncWildcard(listener, commands);
}
public void Unregister(ICommandHookListener listener)
{ {
foreach (var (_, mods) in builder) for (int i = 0; i < listeners.Count; i++)
mods.Remove(modId);
}
else
{ {
foreach (var commandName in commandNames) if (listeners[i].ModId != listener.ModId)
if (builder.TryGetValue(commandName, out var mods)) continue;
mods.Remove(modId);
listeners.RemoveAt(i);
wildcards.RemoveAll(w => w.ModId == listener.ModId);
Rebuild();
return;
}
} }
registrations = Freeze(builder); private void Rebuild()
{
var builder = new Dictionary<string, Dictionary<string, CommandRegistration>>();
foreach (var listener in listeners)
{
var commands = listener.Commands;
if (commands == null || commands.Count == 0)
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;
}
} }
public bool FireBefore(string commandName, ref CommandData data) 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();
}
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)
@@ -80,38 +131,30 @@ public class CommandHookModSystem : ModSystem
return data.Cancel; return data.Cancel;
} }
public void FireAfter(string commandName, ref CommandData data, TextCommandResult result) internal void FireAfter(string commandName, ref CommandData data, TextCommandResult result)
{ {
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.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);
if (isWildcard)
wildcards.Add(listener);
}
foreach (var (cmd, mods) in registrations) private static bool CommandListEquals(IReadOnlyList<string> a, IReadOnlyList<string> b)
{ {
var inner = new Dictionary<string, CommandRegistration>(); if (a.Count != b.Count)
foreach (var (id, reg) in mods) return false;
inner[id] = reg;
builder[cmd] = inner;
}
return builder; for (int i = 0; i < a.Count; i++)
} if (a[i] != b[i])
return false;
private static FrozenDictionary<string, FrozenDictionary<string, CommandRegistration>> Freeze( return true;
Dictionary<string, Dictionary<string, CommandRegistration>> builder
)
{
var pruned = new Dictionary<string, FrozenDictionary<string, CommandRegistration>>();
foreach (var (cmd, mods) in builder)
if (mods.Count > 0)
pruned[cmd] = mods.ToFrozenDictionary();
return pruned.ToFrozenDictionary();
} }
} }
+10
View File
@@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace CommandHook;
public interface ICommandHookListener
{
string ModId { get; }
IReadOnlyList<string> Commands { get; }
CommandRegistration Registration { get; }
}