diff --git a/managed/src/SwiftlyS2.Core/AttributeParsers/CommandAttributeParser.cs b/managed/src/SwiftlyS2.Core/AttributeParsers/CommandAttributeParser.cs index 6b6fb343a..2d7c7bc56 100644 --- a/managed/src/SwiftlyS2.Core/AttributeParsers/CommandAttributeParser.cs +++ b/managed/src/SwiftlyS2.Core/AttributeParsers/CommandAttributeParser.cs @@ -1,41 +1,42 @@ using System.Reflection; -using SwiftlyS2.Core.Commands; using SwiftlyS2.Shared.Commands; namespace SwiftlyS2.Core.AttributeParsers; -internal static class CommandAttributeParser { - public static void ParseFromObject(this ICommandService self, object instance) { - var type = instance.GetType(); - var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - foreach (var method in methods) +internal static class CommandAttributeParser +{ + public static void ParseFromObject( this ICommandService self, object instance ) { - var commandAttribute = method.GetCustomAttribute(); - var clientCommandHookHandlerAttribute = method.GetCustomAttribute(); - var clientChatHookHandlerAttribute = method.GetCustomAttribute(); - if (commandAttribute != null) - { - var commandAliasAttributes = method.GetCustomAttributes(); - var commandName = commandAttribute.Name; - var commandAlias = commandAliasAttributes.Select(a => a.Alias).ToArray(); - var registerRaw = commandAttribute.RegisterRaw; - var permission = commandAttribute.Permission; - self.RegisterCommand(commandName, method.CreateDelegate(instance), registerRaw, permission); - foreach (var alias in commandAlias) + var type = instance.GetType(); + var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + foreach (var method in methods) { - self.RegisterCommandAlias(commandName, alias, registerRaw); - } - } + var commandAttribute = method.GetCustomAttribute(); + var clientCommandHookHandlerAttribute = method.GetCustomAttribute(); + var clientChatHookHandlerAttribute = method.GetCustomAttribute(); + if (commandAttribute != null) + { + var commandAliasAttributes = method.GetCustomAttributes(); + var commandName = commandAttribute.Name; + var registerRaw = commandAttribute.RegisterRaw; + var permission = commandAttribute.Permission; + + var cmdGuid = self.RegisterCommand(commandName, method.CreateDelegate(instance), registerRaw, permission); + foreach (var aliasAttr in commandAliasAttributes) + { + self.RegisterCommandAlias(commandName, aliasAttr.Alias, aliasAttr.RegisterRaw); + } + } - if (clientCommandHookHandlerAttribute != null) - { - self.HookClientCommand(method.CreateDelegate(instance)); - } + if (clientCommandHookHandlerAttribute != null) + { + _ = self.HookClientCommand(method.CreateDelegate(instance)); + } - if (clientChatHookHandlerAttribute != null) - { - self.HookClientChat(method.CreateDelegate(instance)); - } + if (clientChatHookHandlerAttribute != null) + { + _ = self.HookClientChat(method.CreateDelegate(instance)); + } + } } - } } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Core/Modules/Commands/CommandCallback.cs b/managed/src/SwiftlyS2.Core/Modules/Commands/CommandCallback.cs index c0680c35f..8246494dd 100644 --- a/managed/src/SwiftlyS2.Core/Modules/Commands/CommandCallback.cs +++ b/managed/src/SwiftlyS2.Core/Modules/Commands/CommandCallback.cs @@ -1,13 +1,11 @@ using System.Runtime.InteropServices; -using SwiftlyS2.Core.Natives; -using SwiftlyS2.Core.Services; using SwiftlyS2.Shared.Misc; +using SwiftlyS2.Core.Natives; +using SwiftlyS2.Shared.Players; +using SwiftlyS2.Shared.Profiler; using SwiftlyS2.Shared.Commands; using SwiftlyS2.Shared.Permissions; -using SwiftlyS2.Shared.Players; using Microsoft.Extensions.Logging; -using SwiftlyS2.Shared.Profiler; -using SwiftlyS2.Shared; namespace SwiftlyS2.Core.Commands; @@ -19,183 +17,161 @@ namespace SwiftlyS2.Core.Commands; internal abstract class CommandCallbackBase : IDisposable { + public Guid Guid { get; protected init; } + public IContextedProfilerService Profiler { get; } + public ILoggerFactory LoggerFactory { get; } - public Guid Guid { get; protected init; } - - public IContextedProfilerService Profiler { get; } - - public ILoggerFactory LoggerFactory { get; } - - protected CommandCallbackBase( ILoggerFactory loggerFactory, IContextedProfilerService profiler ) - { - LoggerFactory = loggerFactory; - Profiler = profiler; - } - - public abstract void Dispose(); + protected CommandCallbackBase( ILoggerFactory loggerFactory, IContextedProfilerService profiler ) + { + LoggerFactory = loggerFactory; + Profiler = profiler; + } + public abstract void Dispose(); } internal class CommandCallback : CommandCallbackBase { + public string CommandName { get; protected init; } - public string CommandName { get; protected init; } - - private ICommandService.CommandListener _handler; - private CommandCallbackDelegate _unmanagedCallback; - - private nint _unmanagedCallbackPtr; - private ulong _nativeListenerId; - private string _permissions; + private readonly ICommandService.CommandListener commandHandle; + private readonly CommandCallbackDelegate commandCallback; - private ILogger _logger; - private readonly IPlayerManagerService _playerManagerService; - private readonly IPermissionManager _permissionManager; + private readonly nint commandCallbackPtr; + private readonly string commandPermissions; + private readonly ulong nativeListenerId; + private readonly ILogger logger; - public CommandCallback( string commandName, bool registerRaw, ICommandService.CommandListener handler, string permission, IPlayerManagerService playerManagerService, IPermissionManager permissionManager, ILoggerFactory loggerFactory, IContextedProfilerService profiler ) - : base(loggerFactory, profiler) - { - _logger = LoggerFactory.CreateLogger(); - _playerManagerService = playerManagerService; - _permissionManager = permissionManager; - Guid = Guid.NewGuid(); + public CommandCallback( string commandName, bool registerRaw, ICommandService.CommandListener handler, string permission, IPlayerManagerService playerManagerService, IPermissionManager permissionManager, ILoggerFactory loggerFactory, IContextedProfilerService profiler ) : base(loggerFactory, profiler) + { + this.logger = LoggerFactory.CreateLogger(); - CommandName = commandName; - _permissions = permission; - _handler = handler; + Guid = Guid.NewGuid(); - _unmanagedCallback = ( playerId, argsPtr, commandNamePtr, prefixPtr, slient ) => - { - try - { - var category = "CommandCallback::" + CommandName; - Profiler.StartRecording(category); - var argsString = Marshal.PtrToStringUTF8(argsPtr)!; - var commandNameString = Marshal.PtrToStringUTF8(commandNamePtr)!; - var prefixString = Marshal.PtrToStringUTF8(prefixPtr)!; - - var args = argsString.Split('\x01'); - var context = new CommandContext(playerId, args, commandNameString, prefixString, slient == 1); - if (!context.IsSentByPlayer || string.IsNullOrWhiteSpace(_permissions) || _permissionManager.PlayerHasPermission(_playerManagerService.GetPlayer(playerId).SteamID, _permissions)) - { - _handler(context); - } - else + CommandName = commandName; + commandPermissions = permission; + commandHandle = handler; + commandCallback = ( playerId, argsPtr, commandNamePtr, prefixPtr, slient ) => { - context.Reply("You do not have permission to use this command."); - } - Profiler.StopRecording(category); - } - catch (Exception e) - { - if (!GlobalExceptionHandler.Handle(e)) return; - _logger.LogError(e, "Failed to handle command {0}.", commandName); - } - }; - - _unmanagedCallbackPtr = Marshal.GetFunctionPointerForDelegate(_unmanagedCallback); - - _nativeListenerId = NativeCommands.RegisterCommand(commandName, _unmanagedCallbackPtr, registerRaw); - } - - public override void Dispose() - { - NativeCommands.UnregisterCommand(_nativeListenerId); - } + try + { + var category = "CommandCallback::" + CommandName; + Profiler.StartRecording(category); + var argsString = Marshal.PtrToStringUTF8(argsPtr)!; + var commandNameString = Marshal.PtrToStringUTF8(commandNamePtr)!; + var prefixString = Marshal.PtrToStringUTF8(prefixPtr)!; + + var args = argsString.Split('\x01'); + var context = new CommandContext(playerId, args, commandNameString, prefixString, slient == 1); + if (!context.IsSentByPlayer || string.IsNullOrWhiteSpace(commandPermissions) || permissionManager.PlayerHasPermission(playerManagerService.GetPlayer(playerId).SteamID, commandPermissions)) + { + commandHandle(context); + } + else + { + context.Reply("You do not have permission to use this command."); + } + Profiler.StopRecording(category); + } + catch (Exception e) + { + if (!GlobalExceptionHandler.Handle(e)) return; + logger.LogError(e, "Failed to handle command {CommandName}.", commandName); + } + }; + + commandCallbackPtr = Marshal.GetFunctionPointerForDelegate(commandCallback); + nativeListenerId = NativeCommands.RegisterCommand(commandName, commandCallbackPtr, registerRaw); + } + + public override void Dispose() + { + NativeCommands.UnregisterCommand(nativeListenerId); + } } internal class ClientCommandListenerCallback : CommandCallbackBase { + private readonly ICommandService.ClientCommandHandler commandHandle; + private readonly ClientCommandListenerCallbackDelegate commandCallback; + private readonly nint commandCallbackPtr; + private readonly ulong nativeListenerId; + private readonly ILogger logger; - private ICommandService.ClientCommandHandler _handler; - private ClientCommandListenerCallbackDelegate _unmanagedCallback; - private nint _unmanagedCallbackPtr; - private ulong _nativeListenerId; - private ILogger _logger; - - public ClientCommandListenerCallback( ICommandService.ClientCommandHandler handler, ILoggerFactory loggerFactory, IContextedProfilerService profiler ) - : base(loggerFactory, profiler) - { - _logger = LoggerFactory.CreateLogger(); - Guid = Guid.NewGuid(); - - _handler = handler; + public ClientCommandListenerCallback( ICommandService.ClientCommandHandler handler, ILoggerFactory loggerFactory, IContextedProfilerService profiler ) : base(loggerFactory, profiler) + { + logger = LoggerFactory.CreateLogger(); + Guid = Guid.NewGuid(); - _unmanagedCallback = ( playerId, commandLinePtr ) => + commandHandle = handler; + commandCallback = ( playerId, commandLinePtr ) => + { + try + { + var category = "ClientCommandListenerCallback"; + Profiler.StartRecording(category); + var commandLineString = Marshal.PtrToStringUTF8(commandLinePtr)!; + var result = commandHandle(playerId, commandLineString); + Profiler.StopRecording(category); + return result; + } + catch (Exception e) + { + if (!GlobalExceptionHandler.Handle(e)) return HookResult.Continue; + logger.LogError(e, "Failed to handle client command listener."); + return HookResult.Continue; + } + }; + + commandCallbackPtr = Marshal.GetFunctionPointerForDelegate(commandCallback); + nativeListenerId = NativeCommands.RegisterClientCommandsListener(commandCallbackPtr); + } + + public override void Dispose() { - try - { - var category = "ClientCommandListenerCallback"; - Profiler.StartRecording(category); - var commandLineString = Marshal.PtrToStringUTF8(commandLinePtr)!; - var result = _handler(playerId, commandLineString); - Profiler.StopRecording(category); - return result; - } - catch (Exception e) - { - if (!GlobalExceptionHandler.Handle(e)) return HookResult.Continue; - _logger.LogError(e, "Failed to handle client command listener."); - return HookResult.Continue; - } - }; - - _unmanagedCallbackPtr = Marshal.GetFunctionPointerForDelegate(_unmanagedCallback); - - _nativeListenerId = NativeCommands.RegisterClientCommandsListener(_unmanagedCallbackPtr); - - } - - public override void Dispose() - { - NativeCommands.UnregisterClientCommandsListener(_nativeListenerId); - } + NativeCommands.UnregisterClientCommandsListener(nativeListenerId); + } } internal class ClientChatListenerCallback : CommandCallbackBase { + private readonly ICommandService.ClientChatHandler commandHandle; + private readonly ClientChatListenerCallbackDelegate commandCallback; + private readonly nint commandCallbackPtr; + private readonly ulong nativeListenerId; + private readonly ILogger logger; - private ICommandService.ClientChatHandler _handler; - private ClientChatListenerCallbackDelegate _unmanagedCallback; - private nint _unmanagedCallbackPtr; - private ulong _nativeListenerId; - private ILogger _logger; - - public ClientChatListenerCallback( ICommandService.ClientChatHandler handler, ILoggerFactory loggerFactory, IContextedProfilerService profiler ) - : base(loggerFactory, profiler) - { - _logger = LoggerFactory.CreateLogger(); - Guid = Guid.NewGuid(); - - _handler = handler; + public ClientChatListenerCallback( ICommandService.ClientChatHandler handler, ILoggerFactory loggerFactory, IContextedProfilerService profiler ) : base(loggerFactory, profiler) + { + logger = LoggerFactory.CreateLogger(); + Guid = Guid.NewGuid(); - _unmanagedCallback = ( playerId, textPtr, teamonly ) => + commandHandle = handler; + commandCallback = ( playerId, textPtr, teamonly ) => + { + try + { + var category = "ClientChatListenerCallback"; + Profiler.StartRecording(category); + var textString = Marshal.PtrToStringUTF8(textPtr)!; + var result = commandHandle(playerId, textString, teamonly == 1); + Profiler.StopRecording(category); + return result; + } + catch (Exception e) + { + if (!GlobalExceptionHandler.Handle(e)) return HookResult.Continue; + logger.LogError(e, "Failed to handle client chat listener."); + return HookResult.Continue; + } + }; + + commandCallbackPtr = Marshal.GetFunctionPointerForDelegate(commandCallback); + nativeListenerId = NativeCommands.RegisterClientChatListener(commandCallbackPtr); + } + + public override void Dispose() { - try - { - var category = "ClientChatListenerCallback"; - Profiler.StartRecording(category); - var textString = Marshal.PtrToStringUTF8(textPtr)!; - var result = _handler(playerId, textString, teamonly == 1); - Profiler.StopRecording(category); - return result; - } - catch (Exception e) - { - if (!GlobalExceptionHandler.Handle(e)) return HookResult.Continue; - _logger.LogError(e, "Failed to handle client chat listener."); - return HookResult.Continue; - } - }; - - _unmanagedCallbackPtr = Marshal.GetFunctionPointerForDelegate(_unmanagedCallback); - - _nativeListenerId = NativeCommands.RegisterClientChatListener(_unmanagedCallbackPtr); - - } - - public override void Dispose() - { - NativeCommands.UnregisterClientChatListener(_nativeListenerId); - } + NativeCommands.UnregisterClientChatListener(nativeListenerId); + } } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Core/Modules/Commands/CommandService.cs b/managed/src/SwiftlyS2.Core/Modules/Commands/CommandService.cs index c79518ec7..3d04418ac 100644 --- a/managed/src/SwiftlyS2.Core/Modules/Commands/CommandService.cs +++ b/managed/src/SwiftlyS2.Core/Modules/Commands/CommandService.cs @@ -1,153 +1,158 @@ -using System.Reflection; using Microsoft.Extensions.Logging; using SwiftlyS2.Core.Natives; +using SwiftlyS2.Shared.Players; using SwiftlyS2.Shared.Commands; -using SwiftlyS2.Shared.Plugins; using SwiftlyS2.Shared.Profiler; -using SwiftlyS2.Shared.Players; using SwiftlyS2.Shared.Permissions; namespace SwiftlyS2.Core.Commands; internal class CommandService : ICommandService, IDisposable { + private readonly ILoggerFactory loggerFactory; + private readonly IContextedProfilerService profiler; + private readonly IPlayerManagerService playerManagerService; + private readonly IPermissionManager permissionManager; - private List _callbacks = new(); - private ILogger _Logger { get; init; } - private ILoggerFactory _LoggerFactory { get; init; } - private IContextedProfilerService _Profiler { get; init; } - private IPlayerManagerService _PlayerManagerService { get; init; } - private IPermissionManager _PermissionManager { get; init; } - private List _Aliases = new(); + private readonly List commandCallbacks = []; + private readonly List commandAliases = []; + private readonly Lock commandLock = new(); - private Lock _lock = new(); + public CommandService( ILoggerFactory loggerFactory, IContextedProfilerService profiler, IPlayerManagerService playerManagerService, IPermissionManager permissionManager ) + { + this.loggerFactory = loggerFactory; + this.profiler = profiler; + this.playerManagerService = playerManagerService; + this.permissionManager = permissionManager; - public CommandService( ILogger logger, ILoggerFactory loggerFactory, IContextedProfilerService profiler, IPlayerManagerService playerManagerService, IPermissionManager permissionManager ) - { - _Logger = logger; - _LoggerFactory = loggerFactory; - _Profiler = profiler; - _PlayerManagerService = playerManagerService; - _PermissionManager = permissionManager; - } + lock (commandLock) + { + commandCallbacks.Clear(); + commandAliases.Clear(); + } + } - public Guid RegisterCommand( string commandName, ICommandService.CommandListener handler, bool registerRaw = false, string permission = "" ) - { - var callback = new CommandCallback(commandName, registerRaw, handler, permission, _PlayerManagerService, _PermissionManager, _LoggerFactory, _Profiler); - lock (_lock) + public Guid RegisterCommand( string commandName, ICommandService.CommandListener handler, bool registerRaw = false, string permission = "" ) { - _callbacks.Add(callback); + var callback = new CommandCallback(commandName, registerRaw, handler, permission, playerManagerService, permissionManager, loggerFactory, profiler); + lock (commandLock) + { + commandCallbacks.Add(callback); + } + return callback.Guid; } - return callback.Guid; - } - - public void RegisterCommandAlias( string commandName, string alias, bool registerRaw = false ) - { - lock (_lock) + public void RegisterCommandAlias( string commandName, string alias, bool registerRaw = false ) { - _Aliases.Add(NativeCommands.RegisterAlias(alias, commandName, registerRaw)); + lock (commandLock) + { + var commandId = NativeCommands.RegisterAlias(alias, commandName, registerRaw); + if (commandId != 0) + { + commandAliases.Add(commandId); + } + } } - } - public void UnregisterCommand( Guid guid ) - { - lock (_lock) + public void UnregisterCommand( Guid guid ) { - _callbacks.RemoveAll(callback => - { - if (callback.Guid == guid) + lock (commandLock) { - callback.Dispose(); - return true; + _ = commandCallbacks.RemoveAll(callback => + { + if (callback.Guid == guid) + { + callback.Dispose(); + return true; + } + return false; + }); } - return false; - }); } - } - public void UnregisterCommand( string commandName ) - { - lock (_lock) + public void UnregisterCommand( string commandName ) { - _callbacks.RemoveAll(callback => - { - if (callback is CommandCallback commandCallback && commandCallback.CommandName == commandName) + lock (commandLock) { - commandCallback.Dispose(); - return true; + _ = commandCallbacks.RemoveAll(callback => + { + if (callback is CommandCallback commandCallback && commandCallback.CommandName == commandName) + { + commandCallback.Dispose(); + return true; + } + return false; + }); } - return false; - }); } - } - public Guid HookClientCommand( ICommandService.ClientCommandHandler handler ) - { - var callback = new ClientCommandListenerCallback(handler, _LoggerFactory, _Profiler); - lock (_lock) + public Guid HookClientCommand( ICommandService.ClientCommandHandler handler ) { - _callbacks.Add(callback); + var callback = new ClientCommandListenerCallback(handler, loggerFactory, profiler); + lock (commandLock) + { + commandCallbacks.Add(callback); + } + return callback.Guid; } - return callback.Guid; - } - public void UnhookClientCommand( Guid guid ) - { - lock (_lock) + public void UnhookClientCommand( Guid guid ) { - _callbacks.RemoveAll(callback => - { - if (callback is ClientCommandListenerCallback clientCommandCallback && clientCommandCallback.Guid == guid) + lock (commandLock) { - clientCommandCallback.Dispose(); - return true; + _ = commandCallbacks.RemoveAll(callback => + { + if (callback is ClientCommandListenerCallback clientCommandCallback && clientCommandCallback.Guid == guid) + { + clientCommandCallback.Dispose(); + return true; + } + return false; + }); } - return false; - }); } - } - public Guid HookClientChat( ICommandService.ClientChatHandler handler ) - { - var callback = new ClientChatListenerCallback(handler, _LoggerFactory, _Profiler); - lock (_lock) + public Guid HookClientChat( ICommandService.ClientChatHandler handler ) { - _callbacks.Add(callback); + var callback = new ClientChatListenerCallback(handler, loggerFactory, profiler); + lock (commandLock) + { + commandCallbacks.Add(callback); + } + return callback.Guid; } - return callback.Guid; - } - public void UnhookClientChat( Guid guid ) - { - lock (_lock) + public void UnhookClientChat( Guid guid ) { - _callbacks.RemoveAll(callback => - { - if (callback is ClientChatListenerCallback clientChatListenerCallback && clientChatListenerCallback.Guid == guid) + lock (commandLock) { - clientChatListenerCallback.Dispose(); - return true; + _ = commandCallbacks.RemoveAll(callback => + { + if (callback is ClientChatListenerCallback clientChatListenerCallback && clientChatListenerCallback.Guid == guid) + { + clientChatListenerCallback.Dispose(); + return true; + } + return false; + }); } - return false; - }); } - } - public void Dispose() - { - lock (_lock) + public void Dispose() { - foreach (var callback in _callbacks) - { - callback.Dispose(); - } - _callbacks.Clear(); - foreach (var alias in _Aliases) - { - NativeCommands.UnregisterAlias(alias); - } - _Aliases.Clear(); + lock (commandLock) + { + foreach (var alias in commandAliases) + { + NativeCommands.UnregisterAlias(alias); + } + commandAliases.Clear(); + + foreach (var callback in commandCallbacks) + { + callback.Dispose(); + } + commandCallbacks.Clear(); + } } - } } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Modules/Commands/Attributes/CommandAliasAttribute.cs b/managed/src/SwiftlyS2.Shared/Modules/Commands/Attributes/CommandAliasAttribute.cs index b4aa3dde4..15fda4fc0 100644 --- a/managed/src/SwiftlyS2.Shared/Modules/Commands/Attributes/CommandAliasAttribute.cs +++ b/managed/src/SwiftlyS2.Shared/Modules/Commands/Attributes/CommandAliasAttribute.cs @@ -1,13 +1,15 @@ namespace SwiftlyS2.Shared.Commands; [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] -public class CommandAlias : Attribute { - public string Alias { get; set; } +public class CommandAlias : Attribute +{ + public string Alias { get; set; } - public bool RegisterRaw { get; set; } = false; + public bool RegisterRaw { get; set; } = false; - public CommandAlias(string alias, bool registerRaw = false) { - Alias = alias; - RegisterRaw = registerRaw; - } -} + public CommandAlias( string alias, bool registerRaw = false ) + { + Alias = alias; + RegisterRaw = registerRaw; + } +} \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Modules/Commands/ICommandService.cs b/managed/src/SwiftlyS2.Shared/Modules/Commands/ICommandService.cs index 00b7a6d9f..93d5f28a0 100644 --- a/managed/src/SwiftlyS2.Shared/Modules/Commands/ICommandService.cs +++ b/managed/src/SwiftlyS2.Shared/Modules/Commands/ICommandService.cs @@ -1,88 +1,83 @@ -using SwiftlyS2.Shared.Commands; using SwiftlyS2.Shared.Misc; namespace SwiftlyS2.Shared.Commands; public interface ICommandService { - - /// - /// The listener for the command. - /// - /// The command context. - delegate void CommandListener( ICommandContext context ); - - /// - /// The handler for the client command hook. - /// - /// The player id. - /// The command line. - /// Whether the command should continue to be sent. - delegate HookResult ClientCommandHandler( int playerId, string commandLine ); - - - /// - /// The handler for the client chat hook. - /// - /// The player id. - /// The text. - /// Whether the text is for team only. - /// Whether the text should continue to be sent. - - delegate HookResult ClientChatHandler( int playerId, string text, bool teamonly ); - - /// - /// Registers a command. - /// - /// The command name. - /// The handler callback for the command. - /// If set to false, the command will not starts with a `sw_` prefix. - /// The permission required to use the command. - /// The guid of the command. - Guid RegisterCommand( string commandName, CommandListener handler, bool registerRaw = false, string permission = "" ); - - /// - /// Registers a command alias. - /// - /// The command name. - /// The alias. - /// If set to false, the alias will not starts with a `sw_` prefix. - void RegisterCommandAlias( string commandName, string alias, bool registerRaw = false ); - - /// - /// Unregisters a command. - /// - /// The guid of the command. - void UnregisterCommand( Guid guid ); - - /// - /// Unregisters all command listeners with the specified command name. - /// - /// The command name. - void UnregisterCommand( string commandName ); - - /// - /// Hooks client commands, will be fired when a player sends any command. - /// - /// The handler callback for the client command. - Guid HookClientCommand( ClientCommandHandler handler ); - - /// - /// Unhooks a client command. - /// - /// The guid of the client command. - void UnhookClientCommand( Guid guid ); - - /// - /// Hooks client chat, will be fired when a player sends any chat message. - /// - /// The handler callback for the client chat. - Guid HookClientChat( ClientChatHandler handler ); - - /// - /// Unhooks a client chat. - /// - /// The guid of the client chat. - void UnhookClientChat( Guid guid ); - + /// + /// The listener for the command. + /// + /// The command context. + public delegate void CommandListener( ICommandContext context ); + + /// + /// The handler for the client command hook. + /// + /// The player id. + /// The command line. + /// Whether the command should continue to be sent. + public delegate HookResult ClientCommandHandler( int playerId, string commandLine ); + + /// + /// The handler for the client chat hook. + /// + /// The player id. + /// The text. + /// Whether the text is for team only. + /// Whether the text should continue to be sent. + public delegate HookResult ClientChatHandler( int playerId, string text, bool teamonly ); + + /// + /// Registers a command. + /// + /// The command name. + /// The handler callback for the command. + /// If set to false, the command will not starts with a `sw_` prefix. + /// The permission required to use the command. + /// The guid of the command. + public Guid RegisterCommand( string commandName, CommandListener handler, bool registerRaw = false, string permission = "" ); + + /// + /// Registers a command alias. + /// + /// The command name. + /// The alias. + /// If set to false, the alias will not starts with a `sw_` prefix. + public void RegisterCommandAlias( string commandName, string alias, bool registerRaw = false ); + + /// + /// Unregisters a command. + /// + /// The guid of the command. + public void UnregisterCommand( Guid guid ); + + /// + /// Unregisters all command listeners with the specified command name. + /// + /// The command name. + public void UnregisterCommand( string commandName ); + + /// + /// Hooks client commands, will be fired when a player sends any command. + /// + /// The handler callback for the client command. + public Guid HookClientCommand( ClientCommandHandler handler ); + + /// + /// Unhooks a client command. + /// + /// The guid of the client command. + public void UnhookClientCommand( Guid guid ); + + /// + /// Hooks client chat, will be fired when a player sends any chat message. + /// + /// The handler callback for the client chat. + public Guid HookClientChat( ClientChatHandler handler ); + + /// + /// Unhooks a client chat. + /// + /// The guid of the client chat. + public void UnhookClientChat( Guid guid ); } \ No newline at end of file diff --git a/managed/src/TestPlugin/TestPlugin.cs b/managed/src/TestPlugin/TestPlugin.cs index d39dbae3b..af450e83f 100644 --- a/managed/src/TestPlugin/TestPlugin.cs +++ b/managed/src/TestPlugin/TestPlugin.cs @@ -76,8 +76,14 @@ public TestPlugin( ISwiftlyCore core ) : base(core) [CommandAlias("beh", false)] public void Test2Command( ICommandContext context ) { - Console.WriteLine("hello world"); - Console.WriteLine("\n"); + Console.WriteLine("hello world\n"); + } + + [Command("CommandAliasTest")] + [CommandAlias("cat", true)] + public void CommandAliasTest( ICommandContext context ) + { + context.Reply("CommandAliasTest\n"); } [GameEventHandler(HookMode.Pre)] diff --git a/src/scripting/server/commands.cpp b/src/scripting/server/commands.cpp index 82ac0b9ca..356541f6f 100644 --- a/src/scripting/server/commands.cpp +++ b/src/scripting/server/commands.cpp @@ -24,7 +24,10 @@ int Bridge_Commands_HandleCommandForPlayer(int playerid, const char* command) { auto servercommands = g_ifaceService.FetchInterface(SERVERCOMMANDS_INTERFACE_VERSION); - if (!servercommands) return -1; + if (!servercommands) + { + return -1; + } return servercommands->HandleCommand(playerid, command); } @@ -32,21 +35,28 @@ int Bridge_Commands_HandleCommandForPlayer(int playerid, const char* command) uint64_t Bridge_Commands_RegisterCommand(const char* commandName, void* callback, bool registerRaw) { auto servercommands = g_ifaceService.FetchInterface(SERVERCOMMANDS_INTERFACE_VERSION); - if (!servercommands) return 0; + if (!servercommands) + { + return 0; + } // i hate cpp compilers, i stood here for an hour and a half because it was `-> bool` instead of `-> void` - return servercommands->RegisterCommand(commandName, [callback](int playerid, std::vector args, std::string originalCommandName, std::string selectedPrefix, bool isSilentCommand) -> void { - static std::string imploded_args; - imploded_args = implode(args, "\x01"); - - static std::string original_name; - original_name = originalCommandName; - - static std::string selected_prefix; - selected_prefix = selectedPrefix; - - reinterpret_cast(callback)(playerid, imploded_args.c_str(), original_name.c_str(), selected_prefix.c_str(), isSilentCommand == true ? 1 : 0); - }, registerRaw); + return servercommands->RegisterCommand( + commandName, + [callback](int playerid, std::vector args, std::string originalCommandName, std::string selectedPrefix, bool isSilentCommand) -> void + { + static std::string imploded_args; + imploded_args = implode(args, "\x01"); + + static std::string original_name; + original_name = originalCommandName; + + static std::string selected_prefix; + selected_prefix = selectedPrefix; + + reinterpret_cast(callback)(playerid, imploded_args.c_str(), original_name.c_str(), selected_prefix.c_str(), isSilentCommand == true ? 1 : 0); + }, + registerRaw); } void Bridge_Commands_UnregisterCommand(uint64_t callbackID) @@ -55,10 +65,10 @@ void Bridge_Commands_UnregisterCommand(uint64_t callbackID) servercommands->UnregisterCommand(callbackID); } -void Bridge_Commands_RegisterAlias(const char* alias, const char* command, bool registerRaw) +uint64_t Bridge_Commands_RegisterAlias(const char* alias, const char* command, bool registerRaw) { auto servercommands = g_ifaceService.FetchInterface(SERVERCOMMANDS_INTERFACE_VERSION); - servercommands->RegisterAlias(alias, command, registerRaw); + return servercommands->RegisterAlias(alias, command, registerRaw); } void Bridge_Commands_UnregisterAlias(uint64_t callbackID) @@ -70,9 +80,7 @@ void Bridge_Commands_UnregisterAlias(uint64_t callbackID) uint64_t Bridge_Commands_RegisterClientCommandsListener(void* callback) { auto servercommands = g_ifaceService.FetchInterface(SERVERCOMMANDS_INTERFACE_VERSION); - return servercommands->RegisterClientCommandsListener([callback](int playerid, const std::string& command) -> bool { - return reinterpret_cast(callback)(playerid, command.c_str()); - }); + return servercommands->RegisterClientCommandsListener([callback](int playerid, const std::string& command) -> bool { return reinterpret_cast(callback)(playerid, command.c_str()); }); } void Bridge_Commands_UnregisterClientCommandsListener(uint64_t callbackID) @@ -84,9 +92,7 @@ void Bridge_Commands_UnregisterClientCommandsListener(uint64_t callbackID) uint64_t Bridge_Commands_RegisterClientChatListener(void* callback) { auto servercommands = g_ifaceService.FetchInterface(SERVERCOMMANDS_INTERFACE_VERSION); - return servercommands->RegisterClientChatListener([callback](int playerid, const std::string& text, bool teamonly) -> bool { - return reinterpret_cast(callback)(playerid, text.c_str(), teamonly ? 1 : 0); - }); + return servercommands->RegisterClientChatListener([callback](int playerid, const std::string& text, bool teamonly) -> bool { return reinterpret_cast(callback)(playerid, text.c_str(), teamonly ? 1 : 0); }); } void Bridge_Commands_UnregisterClientChatListener(uint64_t callbackID) diff --git a/src/server/commands/manager.cpp b/src/server/commands/manager.cpp index 29a21354c..4f991a559 100644 --- a/src/server/commands/manager.cpp +++ b/src/server/commands/manager.cpp @@ -24,27 +24,28 @@ #include +#include #include #include std::map conCommandCreated; std::map conCommandMapping; -std::map, std::string, std::string, bool)>> g_mCommandHandlers; +std::map, std::string, std::string, bool)>> commandHandlers; -std::map> g_mClientCommandListeners; -std::map> g_mClientChatListeners; +std::map> clientCommandListeners; +std::map> clientChatListeners; std::set commandPrefixes; std::set silentCommandPrefixes; -void DispatchConCommand(void* _this, ConCommandRef cmd, const CCommandContext& ctx, const CCommand& args); -IVFunctionHook* g_pDispatchConCommandHook = nullptr; +void DispatchConCommand(void* thisPtr, ConCommandRef cmd, const CCommandContext& ctx, const CCommand& args); +IVFunctionHook* dispatchConCommandHook = nullptr; -void ClientCommandHook2(void* _this, CPlayerSlot slot, const CCommand& args); -IVFunctionHook* g_pClientCommandHook2 = nullptr; +void ClientCommandHook2(void* thisPtr, CPlayerSlot slot, const CCommand& args); +IVFunctionHook* clientCommandHook2 = nullptr; -void commandsCallback(const CCommandContext& context, const CCommand& args) +void CommandsCallback(const CCommandContext& context, const CCommand& args) { CCommand tokenizedArgs; tokenizedArgs.Tokenize(args.GetCommandString()); @@ -53,13 +54,19 @@ void commandsCallback(const CCommandContext& context, const CCommand& args) std::transform(commandName.begin(), commandName.end(), commandName.begin(), ::tolower); std::string originalCommandName = commandName; - if (!g_mCommandHandlers.contains(commandName)) commandName = "sw_" + commandName; - if (!g_mCommandHandlers.contains(commandName)) return; + if (!commandHandlers.contains(commandName)) + { + commandName = "sw_" + commandName; + } + if (!commandHandlers.contains(commandName)) + { + return; + } std::vector argsplit = TokenizeCommand(args.GetCommandString()); argsplit.erase(argsplit.begin()); - auto& handler = g_mCommandHandlers[commandName]; + auto& handler = commandHandlers[commandName]; handler(context.GetPlayerSlot().Get(), argsplit, originalCommandName, "sw_", true); } @@ -71,33 +78,33 @@ void CServerCommands::Initialize() void* ccvarVTable; s2binlib_find_vtable("tier0", "CCvar", &ccvarVTable); - g_pDispatchConCommandHook = hooksmanager->CreateVFunctionHook(); - g_pDispatchConCommandHook->SetHookFunction(ccvarVTable, gamedata->GetOffsets()->Fetch("ICvar::DispatchConCommand"), (void*)DispatchConCommand, true); - g_pDispatchConCommandHook->Enable(); + dispatchConCommandHook = hooksmanager->CreateVFunctionHook(); + dispatchConCommandHook->SetHookFunction(ccvarVTable, gamedata->GetOffsets()->Fetch("ICvar::DispatchConCommand"), (void*)DispatchConCommand, true); + dispatchConCommandHook->Enable(); void* gameclientsvtable = nullptr; s2binlib_find_vtable("server", "CSource2GameClients", &gameclientsvtable); - g_pClientCommandHook2 = hooksmanager->CreateVFunctionHook(); - g_pClientCommandHook2->SetHookFunction(gameclientsvtable, gamedata->GetOffsets()->Fetch("IServerGameClients::ClientCommand"), (void*)ClientCommandHook2, true); - g_pClientCommandHook2->Enable(); + clientCommandHook2 = hooksmanager->CreateVFunctionHook(); + clientCommandHook2->SetHookFunction(gameclientsvtable, gamedata->GetOffsets()->Fetch("IServerGameClients::ClientCommand"), (void*)ClientCommandHook2, true); + clientCommandHook2->Enable(); } void CServerCommands::Shutdown() { static auto hooksmanager = g_ifaceService.FetchInterface(HOOKSMANAGER_INTERFACE_VERSION); - if (g_pDispatchConCommandHook) + if (dispatchConCommandHook) { - g_pDispatchConCommandHook->Disable(); - hooksmanager->DestroyVFunctionHook(g_pDispatchConCommandHook); - g_pDispatchConCommandHook = nullptr; + dispatchConCommandHook->Disable(); + hooksmanager->DestroyVFunctionHook(dispatchConCommandHook); + dispatchConCommandHook = nullptr; } - if (g_pClientCommandHook2) + if (clientCommandHook2) { - g_pClientCommandHook2->Disable(); - hooksmanager->DestroyVFunctionHook(g_pClientCommandHook2); - g_pClientCommandHook2 = nullptr; + clientCommandHook2->Disable(); + hooksmanager->DestroyVFunctionHook(clientCommandHook2); + clientCommandHook2 = nullptr; } } @@ -108,28 +115,42 @@ void CServerCommands::Shutdown() int CServerCommands::HandleCommand(int playerid, const std::string& text) { if (text == "" || text.size() == 0) + { return -1; + } static auto playermanager = g_ifaceService.FetchInterface(PLAYERMANAGER_INTERFACE_VERSION); static auto configuration = g_ifaceService.FetchInterface(CONFIGURATION_INTERFACE_VERSION); IPlayer* player = playermanager->GetPlayer(playerid); if (player == nullptr) + { return -1; + } - if (commandPrefixes.size() == 0) commandPrefixes = explodeToSet(std::get(configuration->GetValue("core.CommandPrefixes")), " "); - if (silentCommandPrefixes.size() == 0) silentCommandPrefixes = explodeToSet(std::get(configuration->GetValue("core.CommandSilentPrefixes")), " "); + if (commandPrefixes.size() == 0) + { + commandPrefixes = explodeToSet(std::get(configuration->GetValue("core.CommandPrefixes")), " "); + } + + if (silentCommandPrefixes.size() == 0) + { + silentCommandPrefixes = explodeToSet(std::get(configuration->GetValue("core.CommandSilentPrefixes")), " "); + } bool isCommand = false; bool isSilentCommand = false; std::string selectedPrefix = ""; - if (commandPrefixes.size() > 0) { - for (auto it = commandPrefixes.begin(); it != commandPrefixes.end(); ++it) { + if (commandPrefixes.size() > 0) + { + for (auto it = commandPrefixes.begin(); it != commandPrefixes.end(); ++it) + { std::string prefix = *it; auto strPrefix = text.substr(0, prefix.size()); - if (prefix == strPrefix) { + if (prefix == strPrefix) + { isCommand = true; selectedPrefix = prefix; break; @@ -137,12 +158,15 @@ int CServerCommands::HandleCommand(int playerid, const std::string& text) } } - if (!isCommand && silentCommandPrefixes.size() > 0) { - for (auto it = silentCommandPrefixes.begin(); it != silentCommandPrefixes.end(); ++it) { + if (!isCommand && silentCommandPrefixes.size() > 0) + { + for (auto it = silentCommandPrefixes.begin(); it != silentCommandPrefixes.end(); ++it) + { std::string prefix = *it; auto strPrefix = text.substr(0, prefix.size()); - if (prefix == strPrefix) { + if (prefix == strPrefix) + { isSilentCommand = true; selectedPrefix = prefix; break; @@ -159,36 +183,58 @@ int CServerCommands::HandleCommand(int playerid, const std::string& text) cmdString.erase(cmdString.begin()); if (tokenizedArgs.ArgC() < 1) + { return 0; + } std::string commandName = tokenizedArgs[0]; if (commandName.size() < 1) + { return 0; + } commandName.erase(0, selectedPrefix.size()); std::transform(commandName.begin(), commandName.end(), commandName.begin(), ::tolower); - std::string originalCommandName = commandName; - if (!g_mCommandHandlers.contains(commandName)) commandName = "sw_" + commandName; - if (!g_mCommandHandlers.contains(commandName)) return 0; + if (!commandHandlers.contains(commandName)) + { + commandName = "sw_" + commandName; + } + if (!commandHandlers.contains(commandName)) + { + return 0; + } - g_mCommandHandlers[commandName](playerid, cmdString, originalCommandName, selectedPrefix, isSilentCommand); + commandHandlers[commandName](playerid, cmdString, originalCommandName, selectedPrefix, isSilentCommand); } if (isCommand) + { return 1; + } else if (isSilentCommand) + { return 2; + } else + { return 0; + } } bool CServerCommands::HandleClientCommand(int playerid, const std::string& text) { - for (const auto& [id, listener] : g_mClientCommandListeners) { + for (const auto& [id, listener] : clientCommandListeners) + { auto res = listener(playerid, text); - if (res == 1) return false; - else if (res == 2) break; + if (res == 1) + { + return false; + } + else if (res == 2) + { + break; + } } return true; @@ -196,99 +242,136 @@ bool CServerCommands::HandleClientCommand(int playerid, const std::string& text) bool CServerCommands::HandleClientChat(int playerid, const std::string& text, bool teamonly) { - for (const auto& [id, listener] : g_mClientChatListeners) { + for (const auto& [id, listener] : clientChatListeners) + { auto res = listener(playerid, text, teamonly); - if (res == 1) return false; - else if (res == 2) break; + if (res == 1) + { + return false; + } + else if (res == 2) + { + break; + } } return true; } -uint64_t CServerCommands::RegisterCommand(std::string command_name, std::function, std::string, std::string, bool)> handler, bool registerRaw) +uint64_t CServerCommands::RegisterCommand(std::string commandName, std::function, std::string, std::string, bool)> handler, bool registerRaw) { - std::transform(command_name.begin(), command_name.end(), command_name.begin(), ::tolower); + std::transform(commandName.begin(), commandName.end(), commandName.begin(), ::tolower); if (!registerRaw) { - if (conCommandCreated.contains(command_name)) + if (conCommandCreated.contains(commandName)) + { return 0; - - command_name = "sw_" + command_name; + } + commandName = "sw_" + commandName; } - static uint64_t command_id = 0; - if (!conCommandCreated.contains(command_name)) { - conCommandCreated[command_name] = new ConCommand(command_name.c_str(), commandsCallback, "SwiftlyS2 registered command", (1 << 25) | (1 << 0) | (1 << 24)); - conCommandMapping[++command_id] = command_name; - g_mCommandHandlers[command_name] = handler; + static uint64_t commandId = 0; + if (!conCommandCreated.contains(commandName)) + { + // printf("RegisterCommand -> commandName: %s, handler: %p, registerRaw: %d\n", commandName.c_str(), handler, registerRaw); + conCommandCreated[commandName] = new ConCommand(commandName.c_str(), CommandsCallback, "SwiftlyS2 registered command", (1 << 25) | (1 << 0) | (1 << 24)); + conCommandMapping[++commandId] = commandName; + commandHandlers[commandName] = handler; } - return command_id; + return commandId; } -void CServerCommands::UnregisterCommand(uint64_t command_id) +void CServerCommands::UnregisterCommand(uint64_t commandId) { - auto it = conCommandMapping.find(command_id); - if (it == conCommandMapping.end()) return; + if (commandId == 0) + { + return; + } - std::string command_name = it->second; - auto it2 = conCommandCreated.find(command_name); - if (it2 == conCommandCreated.end()) return; + auto mappingIt = conCommandMapping.find(commandId); + if (mappingIt == conCommandMapping.end()) + { + return; + } - delete it2->second; + const std::string& commandName = mappingIt->second; + auto createdIt = conCommandCreated.find(commandName); + auto handlerIt = commandHandlers.find(commandName); + + ConCommand* conCommand = nullptr; + if (createdIt != conCommandCreated.end()) + { + conCommand = createdIt->second; + } - conCommandCreated.erase(it2); - conCommandMapping.erase(it); + conCommandMapping.erase(mappingIt); + if (handlerIt != commandHandlers.end()) + { + commandHandlers.erase(handlerIt); + } + if (createdIt != conCommandCreated.end()) + { + conCommandCreated.erase(createdIt); + } - auto it3 = g_mCommandHandlers.find(command_name); - if (it3 != g_mCommandHandlers.end()) g_mCommandHandlers.erase(it3); + delete conCommand; } -uint64_t CServerCommands::RegisterAlias(std::string alias_command, std::string command_name, bool registerRaw) +uint64_t CServerCommands::RegisterAlias(std::string aliasCommand, std::string commandName, bool registerRaw) { - if (!g_mCommandHandlers.contains(command_name)) command_name = "sw_" + command_name; - auto& handler = g_mCommandHandlers[command_name]; - return RegisterCommand(alias_command, handler, registerRaw); + std::transform(commandName.begin(), commandName.end(), commandName.begin(), ::tolower); + if (!commandHandlers.contains(commandName)) + { + commandName = "sw_" + commandName; + if (!commandHandlers.contains(commandName)) + { + return 0; + } + } + return RegisterCommand(aliasCommand, commandHandlers[commandName], registerRaw); } -void CServerCommands::UnregisterAlias(uint64_t alias_id) +void CServerCommands::UnregisterAlias(uint64_t aliasId) { - return UnregisterCommand(alias_id); + return UnregisterCommand(aliasId); } uint64_t CServerCommands::RegisterClientCommandsListener(std::function listener) { - static uint64_t listener_id = 0; - g_mClientCommandListeners[++listener_id] = listener; - return listener_id; + static uint64_t listenerId = 0; + clientCommandListeners[++listenerId] = listener; + return listenerId; } -void CServerCommands::UnregisterClientCommandsListener(uint64_t listener_id) +void CServerCommands::UnregisterClientCommandsListener(uint64_t listenerId) { - g_mClientCommandListeners.erase(listener_id); + clientCommandListeners.erase(listenerId); } uint64_t CServerCommands::RegisterClientChatListener(std::function listener) { - static uint64_t listener_id = 0; - g_mClientChatListeners[++listener_id] = listener; - return listener_id; + static uint64_t listenerId = 0; + clientChatListeners[++listenerId] = listener; + return listenerId; } -void CServerCommands::UnregisterClientChatListener(uint64_t listener_id) +void CServerCommands::UnregisterClientChatListener(uint64_t listenerId) { - g_mClientChatListeners.erase(listener_id); + clientChatListeners.erase(listenerId); } -void ClientCommandHook2(void* _this, CPlayerSlot slot, const CCommand& args) +void ClientCommandHook2(void* thisPtr, CPlayerSlot slot, const CCommand& args) { static auto servercommands = g_ifaceService.FetchInterface(SERVERCOMMANDS_INTERFACE_VERSION); - if (!servercommands->HandleClientCommand(slot.Get(), args.GetCommandString())) return; - - return reinterpret_cast(g_pClientCommandHook2->GetOriginal())(_this, slot, args); + if (!servercommands->HandleClientCommand(slot.Get(), args.GetCommandString())) + { + return; + } + return reinterpret_cast(clientCommandHook2->GetOriginal())(thisPtr, slot, args); } -void DispatchConCommand(void* _this, ConCommandRef cmd, const CCommandContext& ctx, const CCommand& args) +void DispatchConCommand(void* thisPtr, ConCommandRef cmd, const CCommandContext& ctx, const CCommand& args) { CPlayerSlot slot = ctx.GetPlayerSlot(); static auto servercommands = g_ifaceService.FetchInterface(SERVERCOMMANDS_INTERFACE_VERSION); @@ -297,38 +380,47 @@ void DispatchConCommand(void* _this, ConCommandRef cmd, const CCommandContext& c if (slot.Get() != -1) { - if (!servercommands->HandleClientCommand(slot.Get(), args.GetCommandString())) return; + if (!servercommands->HandleClientCommand(slot.Get(), args.GetCommandString())) + { + return; + } std::string command = args.Arg(0); if (command == "say" || command == "say_team") { auto player = playermanager->GetPlayer(slot.Get()); - if (!player) return; + if (!player) + { + return; + } void* controller = player->GetController(); bool teamonly = (command == "say_team"); - auto text = args[1]; - if (strlen(text) == 0) return; + if (strlen(text) == 0) + { + return; + } if (controller) { IGameEvent* pEvent = eventmanager->GetGameEventManager()->CreateEvent("player_chat"); - if (pEvent) { pEvent->SetBool("teamonly", teamonly); pEvent->SetInt("userid", slot.Get()); pEvent->SetString("text", text); - eventmanager->GetGameEventManager()->FireEvent(pEvent, true); } } int handleCommandReturn = servercommands->HandleCommand(slot.Get(), text); - if (handleCommandReturn == 2 || !servercommands->HandleClientChat(slot.Get(), text, teamonly)) return; + if (handleCommandReturn == 2 || !servercommands->HandleClientChat(slot.Get(), text, teamonly)) + { + return; + } } } - return reinterpret_cast(g_pDispatchConCommandHook->GetOriginal())(_this, cmd, ctx, args); + return reinterpret_cast(dispatchConCommandHook->GetOriginal())(thisPtr, cmd, ctx, args); } \ No newline at end of file