From e1921badc3da85116ebdb64b051b7c3679698002 Mon Sep 17 00:00:00 2001 From: Kyle Belanger Date: Sun, 18 Feb 2024 01:42:56 -0500 Subject: [PATCH] Command system and input system --- BLHX.Server.Game/Commands/Command.cs | 105 +++++++++++++++++++++++ BLHX.Server.Game/Commands/HelpCommand.cs | 32 +++++++ BLHX.Server.Game/Commands/SaveCommand.cs | 15 ++++ BLHX.Server.Game/InputSystem.cs | 18 ++++ BLHX.Server/Program.cs | 15 ++-- 5 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 BLHX.Server.Game/Commands/Command.cs create mode 100644 BLHX.Server.Game/Commands/HelpCommand.cs create mode 100644 BLHX.Server.Game/Commands/SaveCommand.cs create mode 100644 BLHX.Server.Game/InputSystem.cs diff --git a/BLHX.Server.Game/Commands/Command.cs b/BLHX.Server.Game/Commands/Command.cs new file mode 100644 index 0000000..7409d8a --- /dev/null +++ b/BLHX.Server.Game/Commands/Command.cs @@ -0,0 +1,105 @@ +using BLHX.Server.Common.Utils; +using System.Reflection; + +namespace BLHX.Server.Game.Commands; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] +public class commandHandler : Attribute +{ + public string Name { get; } + public string Description { get; } + public string Example { get; } + + public commandHandler(string commandName, string description, string example) + { + Name = commandName; + Description = description; + Example = example; + } +} + +[AttributeUsage(AttributeTargets.Property)] +public class ArgumentAttribute : Attribute +{ + public string Key { get; } + + public ArgumentAttribute(string key) + { + Key = key; + } +} + +public abstract class Command +{ + readonly Dictionary argsProperties; + + public Command() + { + argsProperties = GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(x => Attribute.IsDefined(x, typeof(ArgumentAttribute))) + .ToDictionary(x => ((ArgumentAttribute)Attribute.GetCustomAttribute(x, typeof(ArgumentAttribute))!).Key, StringComparer.OrdinalIgnoreCase); + } + + public virtual void Execute(Dictionary args) + { + foreach (var arg in args) + { + if (argsProperties.TryGetValue(arg.Key, out var prop)) + prop.SetValue(this, arg.Value); + } + } +} + +public static class CommandHandler +{ + public static readonly List Commands = new List(); + + static readonly Dictionary>> commandFunctions; + + static CommandHandler() + { + commandFunctions = new Dictionary>>(StringComparer.OrdinalIgnoreCase); + + RegisterCommands(Assembly.GetExecutingAssembly()); + } + + public static void RegisterCommands(Assembly assembly) + { + var commandTypes = assembly.GetTypes() + .Where(t => Attribute.IsDefined(t, typeof(commandHandler)) && typeof(Command).IsAssignableFrom(t)); + + foreach (var commandType in commandTypes) + { + var commandAttribute = (commandHandler)Attribute.GetCustomAttribute(commandType, typeof(commandHandler)); + if (commandAttribute != null) + { + var commandInstance = (Command)Activator.CreateInstance(commandType); + commandFunctions[commandAttribute.Name] = commandInstance.Execute; + Commands.Add(commandInstance); + } + } + } + + public static void HandleCommand(string commandLine) + { + var parts = commandLine.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 0) + return; + + if (!commandFunctions.TryGetValue(parts[0], out var command)) + { + Logger.c.Warn($"Unknown command: {parts[0]}"); + return; + } + + var arguments = new Dictionary(StringComparer.OrdinalIgnoreCase); + for (var i = 1; i < parts.Length; i++) + { + var argParts = parts[i].Split('=', 2); + if (argParts.Length == 2) + arguments[argParts[0]] = argParts[1]; + } + + command(arguments); + } +} diff --git a/BLHX.Server.Game/Commands/HelpCommand.cs b/BLHX.Server.Game/Commands/HelpCommand.cs new file mode 100644 index 0000000..a959248 --- /dev/null +++ b/BLHX.Server.Game/Commands/HelpCommand.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Text; + +namespace BLHX.Server.Game.Commands; + +[commandHandler("help", "Print out all commands with their description and example", "help")] +public class HelpCommand : Command +{ + static readonly Dictionary commandAttributes = new Dictionary(); + + public override void Execute(Dictionary args) + { + base.Execute(args); + + StringBuilder sb = new StringBuilder(); + + sb.AppendLine("Available Commands: "); + foreach (var command in CommandHandler.Commands) + { + if (!commandAttributes.TryGetValue(command.GetType(), out var attr)) + { + attr = command.GetType().GetCustomAttribute(typeof(commandHandler)) as commandHandler; + commandAttributes[command.GetType()] = attr; + } + + if (attr != null) + sb.AppendLine($" {attr.Name} - {attr.Description} (Example: {attr.Example})"); + } + + Console.Write(sb.ToString()); + } +} diff --git a/BLHX.Server.Game/Commands/SaveCommand.cs b/BLHX.Server.Game/Commands/SaveCommand.cs new file mode 100644 index 0000000..7864093 --- /dev/null +++ b/BLHX.Server.Game/Commands/SaveCommand.cs @@ -0,0 +1,15 @@ +using BLHX.Server.Common.Utils; + +namespace BLHX.Server.Game.Commands; + +[commandHandler("save", "Save the current state", "save")] +public class SaveCommand : Command +{ + public override void Execute(Dictionary args) + { + base.Execute(args); + + Logger.c.Log("Saving..."); + Logger.c.Log("Saved!"); + } +} diff --git a/BLHX.Server.Game/InputSystem.cs b/BLHX.Server.Game/InputSystem.cs new file mode 100644 index 0000000..a3dc797 --- /dev/null +++ b/BLHX.Server.Game/InputSystem.cs @@ -0,0 +1,18 @@ +using BLHX.Server.Game.Commands; + +namespace BLHX.Server.Game; + +public static class InputSystem +{ + public static void Start() + { + while (true) + { + var command = Console.ReadLine(); + + if (string.IsNullOrEmpty(command)) continue; + + CommandHandler.HandleCommand(command); + } + } +} diff --git a/BLHX.Server/Program.cs b/BLHX.Server/Program.cs index eda4102..d5a5121 100644 --- a/BLHX.Server/Program.cs +++ b/BLHX.Server/Program.cs @@ -1,14 +1,15 @@ using BLHX.Server.Common.Utils; using BLHX.Server.Game; -namespace BLHX.Server +namespace BLHX.Server; + +internal class Program { - internal class Program + static void Main() { - static void Main() - { - Logger.c.Log("Starting..."); - Task.Run(GameServer.Start).Wait(); - } + Logger.c.Log("Starting..."); + + Task.Run(GameServer.Start); + Task.Run(InputSystem.Start).Wait(); } }