using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using ReallifeGamemode.Server.Core.API; using ReallifeGamemode.Server.Types; using ReallifeGamemode.Server.Common; using ReallifeGamemode.Server.Log; using Microsoft.Extensions.Logging; namespace ReallifeGamemode.Server.Core.Commands { class CommandHandler { private readonly Dictionary registeredCommands = new Dictionary(); private readonly ILogger logger = LogManager.GetLogger(); private readonly IAPI api; private readonly string[] legacyCommands; private Random random = new Random(); public CommandHandler(IAPI api, string[] registeredCommands) { this.api = api; legacyCommands = registeredCommands; Main.EventHandler.RegisterClientEvent("Command", OnPlayerCommand); } private void OnPlayerCommand(IPlayer player, object[] args) { var command = (args.First() as string)?.ToString()?.ToLower(); logger.LogInformation("Player '{Name}' executed command '{command}'", player.Name, command); if (legacyCommands.Contains(command)) { return; } if (!registeredCommands.ContainsKey(command)) { player.SendMessage($"Der Befehl ~b~/{command}~s~ existiert nicht", ChatPrefix.Error); return; } var executor = registeredCommands[command]; if (!executor.CanExecute(player)) { player.SendMessage($"Du darfst diesen Befehl nicht ausführen", ChatPrefix.Error); return; } var methodInfo = executor.GetType().GetMethod("Handle"); var arguments = new List { player }; try { arguments.AddRange(ComputeParameters(methodInfo, args.Skip(1).ToArray())); var result = methodInfo.Invoke(executor, arguments.ToArray()); } catch (ArgumentException ex) { player.SendMessage($"Der Parameter ~y~'{ex.Message}'~s~ ist ungültig.", ChatPrefix.Error); } catch (SendHelpTextException) { player.SendMessage("/" + command + " " + executor.HelpText, ChatPrefix.Usage); } catch (TargetInvocationException ex) { var errorCode = GetNewErrorCode(); ex.Data.Add("ErrorCode", errorCode); logger.LogError(ex, "Error (Code: {ErrorCode}) while executing command '{Command}' from user '{Name}'", errorCode, command, player.Name); player.SendMessage($"Bei der Ausführung des Befehls gab es einen unbekannten Fehler. ~m~({errorCode})", ChatPrefix.Error); } } public void RegisterCommand(string command, Command commandExecutor) { command = command.ToLower(); logger.LogDebug("Adding command '{command}'", command); if (command.IsNullOrEmpty()) { logger.LogError("Command name is null"); return; } if (commandExecutor == null) { logger.LogError("Command executor for command '{command}' is null", command); return; } var methodInfo = commandExecutor.GetType().GetMethod("Handle"); if (methodInfo == null) { logger.LogError("The executor for command '{command}' does not have a 'Handle' Method", command); return; } registeredCommands[command] = commandExecutor; } private object[] ComputeParameters(MethodInfo methodInfo, object[] arguments) { var computedParameters = new List(); var parameterInfos = methodInfo.GetParameters(); var parameterCount = parameterInfos.Length - 1; var argumentCount = arguments.Length; for (var i = 1; i <= parameterCount; i++) { var currentParameter = parameterInfos[i]; var parameterType = currentParameter.ParameterType; object currentArgument; if (i < argumentCount + 1) { currentArgument = arguments[i - 1]; } else if (argumentCount <= (i - 1) && currentParameter.HasDefaultValue) { currentArgument = currentParameter.DefaultValue; computedParameters.Add(currentArgument); continue; } else { throw new CommandArgumentCountMismatchException(); } if (parameterType == typeof(IPlayer)) { var player = api.GetPlayerFromNameOrId(currentArgument.ToString()); if (player == null) { throw new ArgumentException($"Der Spieler wurde nicht gefunden."); } computedParameters.Add(player); continue; } if (parameterType == typeof(string)) { if (i == parameterCount) { var str = string.Join(' ', arguments.Skip(i - 1)); computedParameters.Add(str); break; } else { computedParameters.Add(currentArgument); } } if (parameterType.IsEnum) { var stringArg = currentArgument.ToString(); if (stringArg.TryParseEnum(parameterType, out var result)) { computedParameters.Add(result); } else { throw new ArgumentException(currentArgument.ToString()); } } else if (parameterType.IsPrimitive) { try { computedParameters.Add(Convert.ChangeType(currentArgument, parameterType)); } catch { throw new ArgumentException(currentArgument.ToString()); } } } return computedParameters.ToArray(); } private string GetNewErrorCode() { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#?$"; return new string(Enumerable.Repeat(chars, 6) .Select(s => s[random.Next(s.Length)]).ToArray()); } } }