Begin script abstraction
This commit is contained in:
24
ReallifeGamemode.Server.Core/Commands/Command.cs
Normal file
24
ReallifeGamemode.Server.Core/Commands/Command.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ReallifeGamemode.Server.Core.API;
|
||||
using ReallifeGamemode.Server.Log;
|
||||
|
||||
namespace ReallifeGamemode.Server.Core.Commands
|
||||
{
|
||||
public abstract class Command
|
||||
{
|
||||
public abstract bool CanExecute(IPlayer player);
|
||||
|
||||
protected abstract IAPI Api { get; }
|
||||
protected ILogger Log { get; set; }
|
||||
public abstract string CommandName { get; }
|
||||
public virtual string HelpText { get; } = null;
|
||||
|
||||
public Command()
|
||||
{
|
||||
Log = LogManager.GetLogger(this.GetType());
|
||||
}
|
||||
}
|
||||
}
|
||||
202
ReallifeGamemode.Server.Core/Commands/CommandHandler.cs
Normal file
202
ReallifeGamemode.Server.Core/Commands/CommandHandler.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
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 Random random = new Random();
|
||||
|
||||
public CommandHandler(IAPI api)
|
||||
{
|
||||
this.api = api;
|
||||
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 (!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());
|
||||
}
|
||||
}
|
||||
}
|
||||
70
ReallifeGamemode.Server.Core/Main.cs
Normal file
70
ReallifeGamemode.Server.Core/Main.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ReallifeGamemode.Database.Models;
|
||||
using ReallifeGamemode.Server.Core.API;
|
||||
using ReallifeGamemode.Server.Core.Commands;
|
||||
using ReallifeGamemode.Server.Log;
|
||||
|
||||
namespace ReallifeGamemode.Server.Core
|
||||
{
|
||||
public class Main
|
||||
{
|
||||
internal static IAPI API { get; private set; }
|
||||
internal static Events.EventHandler EventHandler { get; private set; }
|
||||
|
||||
private static List<Script> loadedScripts = new List<Script>();
|
||||
|
||||
private readonly ILogger logger = LogManager.GetLogger<Main>();
|
||||
|
||||
public Main(IAPI api, Events.EventHandler eventHandler)
|
||||
{
|
||||
logger.LogInformation("Loading scripts...");
|
||||
API = api;
|
||||
EventHandler = eventHandler;
|
||||
|
||||
LoadScript();
|
||||
LoadCommands();
|
||||
}
|
||||
|
||||
private void LoadScript()
|
||||
{
|
||||
var allTypes = Assembly.GetExecutingAssembly().GetTypes();
|
||||
var commandTypes = allTypes.Where(t => t.IsSubclassOf(typeof(Script)) && !t.IsAbstract);
|
||||
|
||||
foreach (var scriptType in commandTypes)
|
||||
{
|
||||
logger.LogDebug("Loading script '{FullName}'", scriptType.FullName);
|
||||
var script = Activator.CreateInstance(scriptType) as Script;
|
||||
loadedScripts.Add(script);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadCommands()
|
||||
{
|
||||
CommandHandler commandHandler = new CommandHandler(API);
|
||||
|
||||
var allTypes = Assembly.GetExecutingAssembly().GetTypes();
|
||||
var commandTypes = allTypes.Where(t => t.IsSubclassOf(typeof(Command)) && !t.IsAbstract);
|
||||
|
||||
foreach (var command in commandTypes)
|
||||
{
|
||||
var cmd = Activator.CreateInstance(command) as Command;
|
||||
commandHandler.RegisterCommand(cmd.CommandName, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
public static DatabaseContext GetDbContext(bool useLoggerFactory = true)
|
||||
{
|
||||
return new DatabaseContext(useLoggerFactory ? LogManager.Factory : null);
|
||||
}
|
||||
|
||||
internal static TScript GetScript<TScript>() where TScript : Script
|
||||
{
|
||||
return loadedScripts.Where(s => s.GetType() == typeof(TScript)).First() as TScript;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ReallifeGamemode.Database\ReallifeGamemode.Database.csproj" />
|
||||
<ProjectReference Include="..\ReallifeGamemode.Server.Common\ReallifeGamemode.Server.Common.csproj" />
|
||||
<ProjectReference Include="..\ReallifeGamemode.Server.Core.API\ReallifeGamemode.Server.Core.API.csproj" />
|
||||
<ProjectReference Include="..\ReallifeGamemode.Server.Core.Events\ReallifeGamemode.Server.Core.Events.csproj" />
|
||||
<ProjectReference Include="..\ReallifeGamemode.Server.Log\ReallifeGamemode.Server.Log.csproj" />
|
||||
<ProjectReference Include="..\ReallifeGamemode.Server.Types\ReallifeGamemode.Server.Types.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
17
ReallifeGamemode.Server.Core/Script.cs
Normal file
17
ReallifeGamemode.Server.Core/Script.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ReallifeGamemode.Server.Core.API;
|
||||
|
||||
namespace ReallifeGamemode.Server.Core
|
||||
{
|
||||
internal abstract class Script
|
||||
{
|
||||
protected IAPI Api { get; } = Main.API;
|
||||
|
||||
public Script()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user