Files
reallife-gamemode/ReallifeGamemode.Server.Core/Commands/CommandHandler.cs
2020-03-01 18:45:26 +01:00

209 lines
5.8 KiB
C#

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<string, Command> registeredCommands = new Dictionary<string, Command>();
private readonly ILogger logger = LogManager.GetLogger<CommandHandler>();
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<object>
{
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<object>();
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());
}
}
}