Compare commits

3 Commits
2 changed files with 106 additions and 53 deletions
+96 -53
View File
@@ -10,6 +10,9 @@ public class CommandHookModSystem : ModSystem
{
internal static CommandHookModSystem? Instance;
private readonly List<ICommandHookListener> listeners = new();
private readonly List<ICommandHookListener> wildcards = new();
private FrozenDictionary<string, FrozenDictionary<string, CommandRegistration>> registrations =
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 double ExecuteOrder() => 0.0;
public override void StartServerSide(ICoreServerAPI api)
{
Instance = this;
harmony = new Harmony(Mod.Info.ModID);
harmony.PatchAll();
api.Event.ServerRunPhase(EnumServerRunPhase.RunGame, Rebuild);
Mod.Logger.Notification("Loaded");
}
public override void Dispose()
{
harmony?.UnpatchAll(Mod.Info.ModID);
listeners.Clear();
wildcards.Clear();
Instance = null;
}
public void Register(
string modId,
CommandRegistration registration,
params string[] commandNames
)
public void Register(ICommandHookListener listener)
{
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;
}
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(ICommandHookListener listener)
{
for (int i = 0; i < listeners.Count; i++)
{
if (listeners[i].ModId != listener.ModId)
continue;
listeners.RemoveAt(i);
wildcards.RemoveAll(w => w.ModId == listener.ModId);
Rebuild();
return;
}
}
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)
{
target = new Dictionary<string, CommandRegistration>();
builder[commandName] = target;
if (!builder.TryGetValue(cmd, out var mods))
{
mods = new Dictionary<string, CommandRegistration>();
builder[cmd] = mods;
}
mods[listener.ModId] = listener.Registration;
}
target[modId] = 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 void Unregister(string modId, params string[] commandNames)
{
var builder = Thaw();
if (commandNames.Length == 0)
{
foreach (var (_, mods) in builder)
mods.Remove(modId);
}
else
{
foreach (var commandName in commandNames)
if (builder.TryGetValue(commandName, out var mods))
mods.Remove(modId);
}
registrations = Freeze(builder);
}
public bool FireBefore(string commandName, ref CommandData data)
internal bool FireBefore(string commandName, ref CommandData data)
{
if (registrations.TryGetValue(commandName, out var mods))
foreach (var (_, reg) in mods)
@@ -80,38 +131,30 @@ public class CommandHookModSystem : ModSystem
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))
foreach (var (_, reg) in mods)
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>>();
foreach (var (cmd, mods) in registrations)
{
var inner = new Dictionary<string, CommandRegistration>();
foreach (var (id, reg) in mods)
inner[id] = reg;
builder[cmd] = inner;
}
return builder;
bool isWildcard = commands.Count == 1 && commands[0] == "*";
wildcards.RemoveAll(w => w.ModId == listener.ModId);
if (isWildcard)
wildcards.Add(listener);
}
private static FrozenDictionary<string, FrozenDictionary<string, CommandRegistration>> Freeze(
Dictionary<string, Dictionary<string, CommandRegistration>> builder
)
private static bool CommandListEquals(IReadOnlyList<string> a, IReadOnlyList<string> b)
{
var pruned = new Dictionary<string, FrozenDictionary<string, CommandRegistration>>();
if (a.Count != b.Count)
return false;
foreach (var (cmd, mods) in builder)
if (mods.Count > 0)
pruned[cmd] = mods.ToFrozenDictionary();
for (int i = 0; i < a.Count; i++)
if (a[i] != b[i])
return false;
return pruned.ToFrozenDictionary();
return true;
}
}
+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; }
}