Compare commits

7 Commits
7 changed files with 145 additions and 57 deletions
+48
View File
@@ -0,0 +1,48 @@
using System;
using HarmonyLib;
using Vintagestory.API.Common;
using Vintagestory.API.Server;
using Vintagestory.Common;
namespace CommandHook;
[HarmonyPatch(typeof(ChatCommandApi))]
public static class ChatCommandApiPatch
{
[HarmonyPatch(
"Execute",
new[] { typeof(string), typeof(TextCommandCallingArgs), typeof(Action<TextCommandResult>) }
)]
[HarmonyPrefix]
public static bool Prefix(
string commandName,
TextCommandCallingArgs args,
ref Action<TextCommandResult> onCommandComplete
)
{
var system = CommandHookModSystem.Instance;
if (system == null)
return true;
var sender = args.Caller.Player as IServerPlayer;
var data = new CommandData(sender, "/" + commandName);
if (system.FireBefore(commandName, ref data))
{
onCommandComplete?.Invoke(
new TextCommandResult { Status = EnumCommandStatus.Deferred }
);
return false;
}
var original = onCommandComplete;
onCommandComplete = result =>
{
original?.Invoke(result);
var afterData = data;
system.FireAfter(commandName, ref afterData, result);
};
return true;
}
}
+6
View File
@@ -0,0 +1,6 @@
using Vintagestory.API.Common;
namespace CommandHook;
public delegate void CommandBeforeDelegate(ref CommandData data);
public delegate void CommandAfterDelegate(ref CommandData data, TextCommandResult result);
-3
View File
@@ -1,3 +0,0 @@
namespace CommandHook;
public delegate void CommandHookDelegate(ref CommandData data);
+88 -38
View File
@@ -1,67 +1,117 @@
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using HarmonyLib;
using Vintagestory.API.Common; using Vintagestory.API.Common;
using Vintagestory.API.Datastructures;
using Vintagestory.API.Server; using Vintagestory.API.Server;
namespace CommandHook; namespace CommandHook;
public class CommandHookModSystem : ModSystem public class CommandHookModSystem : ModSystem
{ {
private readonly Dictionary<string, CommandRegistration> wildcards = new(); internal static CommandHookModSystem? Instance;
private readonly Dictionary<string, ModRegistration> registrations = new();
private FrozenDictionary<string, FrozenDictionary<string, CommandRegistration>> registrations =
FrozenDictionary<string, FrozenDictionary<string, CommandRegistration>>.Empty;
private Harmony? harmony;
public override bool ShouldLoad(EnumAppSide forSide) => forSide == EnumAppSide.Server; public override bool ShouldLoad(EnumAppSide forSide) => forSide == EnumAppSide.Server;
public override void StartServerSide(ICoreServerAPI api) public override void StartServerSide(ICoreServerAPI api)
{ {
Instance = this;
harmony = new Harmony(Mod.Info.ModID);
harmony.PatchAll();
Mod.Logger.Notification("Loaded"); Mod.Logger.Notification("Loaded");
api.Event.PlayerChat += OnPlayerChat;
} }
public void RegisterWildcard(string modId, CommandRegistration registration) public override void Dispose()
{ {
wildcards[modId] = registration; harmony?.UnpatchAll(Mod.Info.ModID);
Instance = null;
} }
public void Register(string modId, string command, CommandRegistration registration) public void Register(
{ string modId,
if (!registrations.TryGetValue(modId, out var modReg)) CommandRegistration registration,
{ params string[] commandNames
modReg = new ModRegistration(new Dictionary<string, CommandRegistration>());
registrations[modId] = modReg;
}
modReg.Commands[command] = registration;
}
private void OnPlayerChat(
IServerPlayer player,
int channelId,
ref string message,
ref string data,
BoolRef consumed
) )
{ {
if (!message.StartsWith('/')) var builder = Thaw();
return;
var cmdData = new CommandData(player, message); foreach (var commandName in commandNames)
{
if (!builder.TryGetValue(commandName, out var target))
{
target = new Dictionary<string, CommandRegistration>();
builder[commandName] = target;
}
target[modId] = registration;
}
foreach (var (_, reg) in wildcards) registrations = Freeze(builder);
reg.Before?.Invoke(ref cmdData); }
foreach (var (_, modReg) in registrations) public void Unregister(string modId, params string[] commandNames)
if (modReg.Commands.TryGetValue(cmdData.CommandName, out var reg)) {
reg.Before?.Invoke(ref cmdData); var builder = Thaw();
if (cmdData.Cancel) if (commandNames.Length == 0)
consumed.value = true; {
foreach (var (_, mods) in builder)
mods.Remove(modId);
}
else
{
foreach (var commandName in commandNames)
if (builder.TryGetValue(commandName, out var mods))
mods.Remove(modId);
}
foreach (var (_, reg) in wildcards) registrations = Freeze(builder);
reg.After?.Invoke(ref cmdData); }
foreach (var (_, modReg) in registrations) public bool FireBefore(string commandName, ref CommandData data)
if (modReg.Commands.TryGetValue(cmdData.CommandName, out var reg)) {
reg.After?.Invoke(ref cmdData); if (registrations.TryGetValue(commandName, out var mods))
foreach (var (_, reg) in mods)
reg.Before?.Invoke(ref data);
return data.Cancel;
}
public 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()
{
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;
}
private static FrozenDictionary<string, FrozenDictionary<string, CommandRegistration>> Freeze(
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();
} }
} }
+3 -3
View File
@@ -2,10 +2,10 @@ namespace CommandHook;
public struct CommandRegistration public struct CommandRegistration
{ {
public readonly CommandHookDelegate? Before; public readonly CommandBeforeDelegate? Before;
public readonly CommandHookDelegate? After; public readonly CommandAfterDelegate? After;
public CommandRegistration(CommandHookDelegate? before, CommandHookDelegate? after) public CommandRegistration(CommandBeforeDelegate? before, CommandAfterDelegate? after)
{ {
Before = before; Before = before;
After = after; After = after;
-13
View File
@@ -1,13 +0,0 @@
using System.Collections.Generic;
namespace CommandHook;
public struct ModRegistration
{
public readonly Dictionary<string, CommandRegistration> Commands;
public ModRegistration(Dictionary<string, CommandRegistration> commands)
{
Commands = commands;
}
}
Regular → Executable
View File