Compare commits
7 Commits
virtual-co
...
master
Author | SHA1 | Date | |
---|---|---|---|
cfa47142ac | |||
15b916a52a | |||
8ffedd6ba9 | |||
2a1c40a51c | |||
f1a4ea0a65 | |||
99ba6e1710 | |||
60979355d4 |
@ -5,6 +5,14 @@
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="src\CogwheelLib.csproj" />
|
||||
<Compile Remove="obj\**\*.AssemblyAttributes.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,60 +0,0 @@
|
||||
namespace Cogwheel;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public class DeafultCogwheelConsole : ICogwheelConsole
|
||||
{
|
||||
public string OpeningMessage { get; set; } = "** COGWHEEL CONSOLE **";
|
||||
public bool IsRunning { get; set; }
|
||||
public CommandsManager CommandsManager { get; set; }
|
||||
|
||||
|
||||
public void Initialize(CommandsManager commandsManager)
|
||||
{
|
||||
CommandsManager = commandsManager;
|
||||
CommandsManager.RegisterObject(this);
|
||||
|
||||
Write(OpeningMessage);
|
||||
|
||||
IsRunning = true;
|
||||
while (IsRunning)
|
||||
{
|
||||
Console.Write("> ");
|
||||
string input = Console.ReadLine();
|
||||
CommandsManager.RunCommand(input);
|
||||
}
|
||||
}
|
||||
|
||||
public void Log(string message)
|
||||
{
|
||||
Console.WriteLine($"[COGWHEEL] {message}");
|
||||
}
|
||||
|
||||
public void LogError(string message)
|
||||
{
|
||||
Console.WriteLine($"[COGWHEEL ERROR] {message}");
|
||||
}
|
||||
|
||||
public void LogWarning(string message)
|
||||
{
|
||||
Console.WriteLine($"[COGWHEEL WARNING] {message}");
|
||||
}
|
||||
|
||||
public void Write(string message)
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
|
||||
public void ClearConsole()
|
||||
{
|
||||
Console.Clear();
|
||||
}
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
IsRunning = false;
|
||||
}
|
||||
|
||||
}
|
13
Program.cs
13
Program.cs
@ -1,9 +1,20 @@
|
||||
// Program.cs
|
||||
/*
|
||||
This is an example of how to initialize the Cogwheel system in a console application
|
||||
using the default console implementation and adding the assembly containing the built-in commands.
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
using Cogwheel;
|
||||
|
||||
// Create a new instance of an ICogwheelConsole implementation
|
||||
ICogwheelConsole cogwheelConsole = new DeafultCogwheelConsole();
|
||||
|
||||
// Create a new instance of a CommandsManager
|
||||
CommandsManager commandsManager = new CommandsManager();
|
||||
|
||||
// Register the assembly containing the built-in commands
|
||||
commandsManager.AddAssembly(Assembly.GetCallingAssembly());
|
||||
|
||||
// Initialize the Cogwheel system
|
||||
COGWHEEL.Initialize(commandsManager, cogwheelConsole);
|
||||
|
||||
|
182
src/COGWHEEL.cs
182
src/COGWHEEL.cs
@ -1,26 +1,50 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Cogwheel;
|
||||
|
||||
/// <summary>
|
||||
/// Static class for interacting with the Cogwheel system
|
||||
/// </summary>
|
||||
public static class COGWHEEL
|
||||
{
|
||||
private static CommandsManager _commandsManager;
|
||||
private static ICogwheelConsole _console;
|
||||
|
||||
public static bool UseBuiltInCommands { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Cogwheel system
|
||||
/// </summary>
|
||||
/// <param name="commandsManager"></param>
|
||||
/// <param name="console"></param>
|
||||
public static void Initialize(CommandsManager commandsManager, ICogwheelConsole console)
|
||||
{
|
||||
_console = console;
|
||||
_commandsManager = commandsManager;
|
||||
|
||||
InitializeCustomParsers();
|
||||
|
||||
_commandsManager.Initialize(_console);
|
||||
_console.Initialize(_commandsManager);
|
||||
}
|
||||
|
||||
|
||||
// == Public static methods for the CommandsManager == //
|
||||
/// <summary>
|
||||
/// Runs a command
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
public static void RunCommand(string input)
|
||||
{
|
||||
_commandsManager.RunCommand(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an object with the CommandsManager for non-static commands
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
public static void RegisterObject(object obj)
|
||||
{
|
||||
_commandsManager.RegisterObject(obj);
|
||||
@ -28,43 +52,96 @@ public static class COGWHEEL
|
||||
|
||||
|
||||
// == Public static methods for the Console == //
|
||||
/// <summary>
|
||||
/// Logs a message to the console
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void Log(string message)
|
||||
{
|
||||
_console.Log(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message to the console
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void LogError(string message)
|
||||
{
|
||||
_console.LogError(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message to the console
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
_console.LogWarning(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a message to the console
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void Write(string message)
|
||||
{
|
||||
_console.Write(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the context for the CommandsManager
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
public static void SetContext(object obj)
|
||||
{
|
||||
_commandsManager.SetContext(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the context for the CommandsManager by GUID
|
||||
/// </summary>
|
||||
/// <param name="guid"></param>
|
||||
public static void SetContext(Guid guid)
|
||||
{
|
||||
_commandsManager.SetContext(guid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the context for the CommandsManager by GUID string
|
||||
/// </summary>
|
||||
/// <param name="guidString"></param>
|
||||
public static void SetContext(string guidString)
|
||||
{
|
||||
_commandsManager.SetContext(guidString);
|
||||
}
|
||||
|
||||
// == Built-in commands == //
|
||||
/// <summary>
|
||||
/// Quit the Cogwheel console
|
||||
/// </summary>
|
||||
[Command(Name = "quit", Description = "Quits the Cogwheel console.")]
|
||||
public static void QuitCogwheelConsole()
|
||||
{
|
||||
_console.Exit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the console
|
||||
/// </summary>
|
||||
[Command(Name = "clear", Description = "Clears the console.")]
|
||||
public static void ClearConsole()
|
||||
{
|
||||
_console.ClearConsole();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the description and usage of a given command
|
||||
/// </summary>
|
||||
/// <param name="commandName"></param>
|
||||
[Command(Name = "help", Description = "Gets usage for given command.")]
|
||||
public static void ShowHelp(string commandName)
|
||||
{
|
||||
ICommand command = _commandsManager.GetCommandByName(commandName);
|
||||
ICommand? command = _commandsManager.GetCommandByName(commandName);
|
||||
if (command is null)
|
||||
{
|
||||
_console.LogError($"Command error: '{commandName}' is not a command.");
|
||||
@ -76,6 +153,9 @@ public static class COGWHEEL
|
||||
$"Usage: {_commandsManager.GetCommandUsage(command)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists all available commands
|
||||
/// </summary>
|
||||
[Command(Name = "list", Description = "Lists all available commands.")]
|
||||
public static void ListCommands()
|
||||
{
|
||||
@ -85,21 +165,55 @@ public static class COGWHEEL
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs the current context type and GUID
|
||||
/// </summary>
|
||||
[Command(Name = "what", Description = "Prints the current context.")]
|
||||
public static void ShowCurrentContext()
|
||||
{
|
||||
if (_commandsManager.CurrentContext is null)
|
||||
{
|
||||
Log("No context set.");
|
||||
return;
|
||||
}
|
||||
|
||||
_console.Log($"Current context: {_commandsManager.CurrentContext.GetType()} : {_commandsManager.CurrentContextGuid}");
|
||||
}
|
||||
|
||||
[Command(Name = "test", Description = "Creates a new TestClass object.")]
|
||||
public static void CreateTestObject(string name)
|
||||
/// <summary>
|
||||
/// Adds an assembly to the CommandsManager for command discovery
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
public static void AddAssembly(Assembly assembly)
|
||||
{
|
||||
_console.Log($"Creating new TestClass object with name: {name}");
|
||||
var test = new TestClass(name);
|
||||
_commandsManager.AddAssembly(assembly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an assembly from the CommandsManager
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
public static void RemoveAssembly(Assembly assembly)
|
||||
{
|
||||
_commandsManager.RemoveAssembly(assembly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a custom parser for a given type
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="parser"></param>
|
||||
public static void AddCustomParser(Type type, Func<string, object> parser)
|
||||
{
|
||||
_commandsManager.AddCustomParser(type, parser);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs all registered objects and their GUIDs, optionally filtered by type
|
||||
/// </summary>
|
||||
/// <param name="typeName"></param>
|
||||
[Command(Name = "find", Description = "Lists available registered objects to select from.")]
|
||||
private static void SelectContextFromRegisteredObjects(string typeName = "")
|
||||
private static void FindAllContextsFromRegisteredObjects(string typeName = "")
|
||||
{
|
||||
var registeredObjects = _commandsManager.RegisteredObjectInstances;
|
||||
var filteredObjects = string.IsNullOrEmpty(typeName)
|
||||
@ -119,10 +233,62 @@ public static class COGWHEEL
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the context to the specified registered object by GUID
|
||||
/// </summary>
|
||||
/// <param name="guidString"></param>
|
||||
[Command(Name = "set", Description = "Sets the context to the specified registered object.")]
|
||||
private static void SetContextFromGuid(string guidString)
|
||||
{
|
||||
_commandsManager.SetContext(guidString);
|
||||
SetContext(guidString);
|
||||
Log($"Set context to {_commandsManager.CurrentContext?.GetType().FullName} : {_commandsManager.CurrentContextGuid}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes custom parsers for the Cogwheel system
|
||||
/// </summary>
|
||||
/// <exception cref="FormatException"></exception>
|
||||
private static void InitializeCustomParsers()
|
||||
{
|
||||
// Add custom parser for Vector2
|
||||
_commandsManager.AddCustomParser(typeof(Vector2), s =>
|
||||
{
|
||||
s = s.Trim('(', ')');
|
||||
|
||||
var parts = s.Split(',');
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
throw new FormatException("Input string must have exactly three components separated by commas.");
|
||||
}
|
||||
|
||||
if (!float.TryParse(parts[0].Trim(), out float x) ||
|
||||
!float.TryParse(parts[1].Trim(), out float y))
|
||||
{
|
||||
throw new FormatException("Input string contains invalid float values.");
|
||||
}
|
||||
|
||||
return new Vector2(x, y);
|
||||
});
|
||||
|
||||
// Add custom parser for Vector3
|
||||
_commandsManager.AddCustomParser(typeof(Vector3), s =>
|
||||
{
|
||||
s = s.Trim('(', ')');
|
||||
|
||||
var parts = s.Split(',');
|
||||
if (parts.Length != 3)
|
||||
{
|
||||
throw new FormatException("Input string must have exactly three components separated by commas.");
|
||||
}
|
||||
|
||||
if (!float.TryParse(parts[0].Trim(), out float x) ||
|
||||
!float.TryParse(parts[1].Trim(), out float y) ||
|
||||
!float.TryParse(parts[2].Trim(), out float z))
|
||||
{
|
||||
throw new FormatException("Input string contains invalid float values.");
|
||||
}
|
||||
|
||||
return new Vector3(x, y, z);
|
||||
});
|
||||
}
|
||||
}
|
29
src/CogwheelLib.csproj
Normal file
29
src/CogwheelLib.csproj
Normal file
@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
|
||||
|
||||
<Version>1.3.0</Version>
|
||||
<Authors>Chris Bell</Authors>
|
||||
<Company>Bellsworne Tech</Company>
|
||||
<Description>A suite of development tools for games</Description>
|
||||
<PackageTags>development tools, games</PackageTags>
|
||||
<PackageProjectUrl>https://git.bellsworne.tech/Bellsworne/Cogwheel/packages</PackageProjectUrl>
|
||||
<RepositoryUrl>https://git.bellsworne.tech/Bellsworne/Cogwheel.git</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<!-- <PackageIcon>icon.png</PackageIcon>-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="obj\**\*.AssemblyAttributes.cs" />
|
||||
<None Include="changelog.md" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -2,12 +2,32 @@ using System.Reflection;
|
||||
|
||||
namespace Cogwheel;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a command in the Cogwheel system.
|
||||
/// </summary>
|
||||
public class Command : ICommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the command.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the command.
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method associated with the command.
|
||||
/// </summary>
|
||||
public MethodBase Method { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Command"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the command.</param>
|
||||
/// <param name="description">The description of the command.</param>
|
||||
/// <param name="method">The method associated with the command.</param>
|
||||
public Command(string name, string description, MethodBase method)
|
||||
{
|
||||
Name = name;
|
||||
|
@ -1,8 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Cogwheel;
|
||||
|
||||
/// <summary>
|
||||
/// Attribute for marking methods as commands
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
public class CommandAttribute : Attribute
|
||||
{
|
||||
public string Name = "";
|
||||
/// <summary>
|
||||
/// Optional name of the command, if not provided the method name will be used
|
||||
/// </summary>
|
||||
public string? Name = "";
|
||||
|
||||
/// <summary>
|
||||
/// Optional description of the command
|
||||
/// </summary>
|
||||
public string Description = "";
|
||||
}
|
@ -2,25 +2,63 @@ using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Exception = System.Exception;
|
||||
|
||||
namespace Cogwheel;
|
||||
|
||||
/// <summary>
|
||||
/// Manages commands for the Cogwheel system.
|
||||
/// </summary>
|
||||
public class CommandsManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the console used by the CommandsManager.
|
||||
/// </summary>
|
||||
protected virtual ICogwheelConsole CogwheelConsole { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pattern used to parse commands.
|
||||
/// </summary>
|
||||
protected virtual string CommandPattern { get; set; } = "(?<val>(\\([^\\)]+\\)))|\"(?<val>[^\"]+)\"|'(?<val>[^']+)'|(?<val>[^\\s]+)";
|
||||
|
||||
protected virtual List<Assembly> Assemblies { get; set; } = [];
|
||||
/// <summary>
|
||||
/// Gets or sets the list of assemblies to search for commands.
|
||||
/// </summary>
|
||||
protected virtual List<Assembly> Assemblies { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the dictionary of commands.
|
||||
/// </summary>
|
||||
public virtual Dictionary<string, ICommand> Commands { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the dictionary of custom parsers for command parameters.
|
||||
/// </summary>
|
||||
public virtual Dictionary<Type, Func<string, object>> CustomParsers { get; set; } = new();
|
||||
|
||||
// Context related stuff
|
||||
/// <summary>
|
||||
/// Gets or sets the current context object.
|
||||
/// </summary>
|
||||
public virtual object? CurrentContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the GUID of the current context.
|
||||
/// </summary>
|
||||
public virtual Guid? CurrentContextGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the dictionary of registered object instances.
|
||||
/// </summary>
|
||||
public virtual Dictionary<Guid, object> RegisteredObjectInstances { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the dictionary of registered object GUIDs.
|
||||
/// </summary>
|
||||
public virtual Dictionary<object, Guid> RegisteredObjectGuids { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the CommandsManager with the specified console.
|
||||
/// </summary>
|
||||
/// <param name="console">The console to use.</param>
|
||||
public virtual void Initialize(ICogwheelConsole console)
|
||||
{
|
||||
CogwheelConsole = console;
|
||||
@ -28,7 +66,11 @@ public class CommandsManager
|
||||
CogwheelConsole.Log($"CommandsManager initialized, Commands found: {Commands.Count}");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command and arguments from the input string.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string.</param>
|
||||
/// <returns>A tuple containing the command and arguments, or null if the command is not found.</returns>
|
||||
public virtual (ICommand, List<object>)? GetCommandAndArgsFromString(string input)
|
||||
{
|
||||
var splitString = Regex.Matches(input, CommandPattern).Select(m => m.Groups["val"].Value).ToArray();
|
||||
@ -47,6 +89,12 @@ public class CommandsManager
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command specified by the input string.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string.</param>
|
||||
/// <param name="context">The context object, if any.</param>
|
||||
/// <returns>True if the command was executed successfully, otherwise false.</returns>
|
||||
public virtual bool RunCommand(string input, object? context = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input)) return false;
|
||||
@ -72,11 +120,18 @@ public class CommandsManager
|
||||
return ExecuteCommand(CurrentContext, command.Value);
|
||||
}
|
||||
|
||||
//CogwheelConsole.LogWarning($"Command '{command.Value.Item1.Name}' is not static and no valid context was provided, searching for first registered context of type '{command.Value.Item1.Method.DeclaringType}'");
|
||||
context = GetFirstValidRegisteredContext(command.Value.Item1.Method.DeclaringType);
|
||||
if (context is null)
|
||||
if (command.Value.Item1.Method.DeclaringType != null)
|
||||
{
|
||||
CogwheelConsole.LogError($"No context of type '{command.Value.Item1.Method.DeclaringType}' found");
|
||||
context = GetFirstValidRegisteredContext(command.Value.Item1.Method.DeclaringType);
|
||||
if (context is null)
|
||||
{
|
||||
CogwheelConsole.LogError($"No context of type '{command.Value.Item1.Method.DeclaringType}' found");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CogwheelConsole.LogError($"Command '{command.Value.Item1.Name}' has no declaring type");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -88,6 +143,11 @@ public class CommandsManager
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the usage information for the specified command.
|
||||
/// </summary>
|
||||
/// <param name="command">The command.</param>
|
||||
/// <returns>The usage information for the command.</returns>
|
||||
public virtual string GetCommandUsage(ICommand command)
|
||||
{
|
||||
string output = "";
|
||||
@ -119,49 +179,26 @@ public class CommandsManager
|
||||
return output;
|
||||
}
|
||||
|
||||
public virtual bool ExecuteCommand(object obj, (ICommand command, List<object> args) command)
|
||||
/// <summary>
|
||||
/// Executes the specified command with the given arguments.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object on which to execute the command.</param>
|
||||
/// <param name="command">The command and arguments.</param>
|
||||
/// <returns>True if the command was executed successfully, otherwise false.</returns>
|
||||
public virtual bool ExecuteCommand(object? obj, (ICommand command, List<object> args) command)
|
||||
{
|
||||
var parameters = command.command.Method.GetParameters();
|
||||
for (var parameterIndex = 0; parameterIndex < parameters.Length; parameterIndex++)
|
||||
for (var parameterIndex = 0; parameterIndex < parameters.Length; parameterIndex++)
|
||||
{
|
||||
if (parameters[parameterIndex].IsDefined(typeof(ParamArrayAttribute), false))
|
||||
{
|
||||
if (parameters[parameterIndex].IsDefined(typeof(ParamArrayAttribute), false))
|
||||
var paramList = Activator.CreateInstance(typeof(List<>).MakeGenericType(parameters[parameterIndex].ParameterType.GetElementType()!));
|
||||
for (var argIndex = parameterIndex; argIndex < parameters.Length; argIndex++)
|
||||
{
|
||||
var paramList = Activator.CreateInstance(typeof(List<>).MakeGenericType(parameters[parameterIndex].ParameterType.GetElementType()!));
|
||||
for (var argIndex = parameterIndex; argIndex < parameters.Length; argIndex++)
|
||||
if (TryParseParameter(parameters[parameterIndex].ParameterType.GetElementType(),
|
||||
(string)command.args[argIndex], out var val))
|
||||
{
|
||||
if (TryParseParameter(parameters[parameterIndex].ParameterType.GetElementType(),
|
||||
(string)command.args[argIndex], out var val))
|
||||
{
|
||||
paramList.GetType().GetMethod("Add")?.Invoke(paramList, new[] { val });
|
||||
}
|
||||
else
|
||||
{
|
||||
CogwheelConsole.LogError($"Format exception: could not parse '{command.args[parameterIndex]}' as '{parameters[parameterIndex].ParameterType}'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
command.args = command.args.Take(parameterIndex).ToList();
|
||||
command.args.Add(paramList.GetType().GetMethod("ToArray")?.Invoke(paramList, null));
|
||||
}
|
||||
else if (parameterIndex >= command.args.Count)
|
||||
{
|
||||
if (parameters[parameterIndex].IsOptional)
|
||||
{
|
||||
command.args.Add(Type.Missing);
|
||||
}
|
||||
else
|
||||
{
|
||||
CogwheelConsole.LogError("Not enough args passed");
|
||||
CogwheelConsole.Log(GetCommandUsage(command.command));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TryParseParameter(parameters[parameterIndex].ParameterType, (string)command.args[parameterIndex], out var val))
|
||||
{
|
||||
command.args[parameterIndex] = val;
|
||||
paramList.GetType().GetMethod("Add")?.Invoke(paramList, [val]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -169,21 +206,56 @@ public class CommandsManager
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
command.command.Method.Invoke(obj, command.args.ToArray());
|
||||
return true;
|
||||
command.args = command.args.Take(parameterIndex).ToList();
|
||||
command.args.Add(paramList.GetType().GetMethod("ToArray")?.Invoke(paramList, null));
|
||||
}
|
||||
catch (Exception e)
|
||||
else if (parameterIndex >= command.args.Count)
|
||||
{
|
||||
CogwheelConsole.LogError(e.Message);
|
||||
throw;
|
||||
if (parameters[parameterIndex].IsOptional)
|
||||
{
|
||||
command.args.Add(Type.Missing);
|
||||
}
|
||||
else
|
||||
{
|
||||
CogwheelConsole.LogError($"Not enough args passed. Usage: {GetCommandUsage(command.command)}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TryParseParameter(parameters[parameterIndex].ParameterType, (string)command.args[parameterIndex], out var val))
|
||||
{
|
||||
command.args[parameterIndex] = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
CogwheelConsole.LogError($"Format exception: could not parse '{command.args[parameterIndex]}' as '{parameters[parameterIndex].ParameterType}'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
command.command.Method.Invoke(obj, command.args.ToArray());
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CogwheelConsole.LogError(e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool TryParseParameter(Type parameterType, string parameterString, out object parsedValue)
|
||||
/// <summary>
|
||||
/// Tries to parse the parameter string into the specified parameter type.
|
||||
/// </summary>
|
||||
/// <param name="parameterType">The type of the parameter.</param>
|
||||
/// <param name="parameterString">The parameter string.</param>
|
||||
/// <param name="parsedValue">The parsed value.</param>
|
||||
/// <returns>True if the parameter was parsed successfully, otherwise false.</returns>
|
||||
public virtual bool TryParseParameter(Type parameterType, string parameterString, out object? parsedValue)
|
||||
{
|
||||
if (parameterType == typeof(string))
|
||||
{
|
||||
@ -229,16 +301,22 @@ public class CommandsManager
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command by its name.
|
||||
/// </summary>
|
||||
/// <param name="commandName">The name of the command.</param>
|
||||
/// <returns>The command, or null if not found.</returns>
|
||||
public virtual ICommand? GetCommandByName(string commandName)
|
||||
{
|
||||
return Commands.GetValueOrDefault(commandName);
|
||||
}
|
||||
|
||||
// Context related stuff
|
||||
|
||||
/// <summary>
|
||||
/// Registers the specified object with the CommandsManager.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to register.</param>
|
||||
public virtual void RegisterObject(object obj)
|
||||
{
|
||||
// Use a combination of the object's type and a hash of its properties to generate a deterministic GUID
|
||||
string seed = obj.GetType().FullName + GetObjectPropertiesHash(obj);
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
@ -258,6 +336,12 @@ public class CommandsManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the command context is valid.
|
||||
/// </summary>
|
||||
/// <param name="command">The command.</param>
|
||||
/// <param name="context">The context object, if any.</param>
|
||||
/// <returns>True if the context is valid, otherwise false.</returns>
|
||||
public virtual bool IsCommandContextValid(ICommand command, object? context = null)
|
||||
{
|
||||
if (context is not null)
|
||||
@ -267,6 +351,11 @@ public class CommandsManager
|
||||
return command.Method.DeclaringType == CurrentContext?.GetType();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current context to the specified object.
|
||||
/// </summary>
|
||||
/// <param name="context">The context object.</param>
|
||||
/// <returns>True if the context was set successfully, otherwise false.</returns>
|
||||
public virtual bool SetContext(object context)
|
||||
{
|
||||
CurrentContext = context;
|
||||
@ -279,6 +368,11 @@ public class CommandsManager
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current context to the object with the specified GUID.
|
||||
/// </summary>
|
||||
/// <param name="guid">The GUID of the context object.</param>
|
||||
/// <returns>True if the context was set successfully, otherwise false.</returns>
|
||||
public virtual bool SetContext(Guid guid)
|
||||
{
|
||||
if (RegisteredObjectInstances.ContainsKey(guid))
|
||||
@ -295,6 +389,11 @@ public class CommandsManager
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current context to the object with the specified GUID string.
|
||||
/// </summary>
|
||||
/// <param name="guidString">The GUID string of the context object.</param>
|
||||
/// <returns>True if the context was set successfully, otherwise false.</returns>
|
||||
public virtual bool SetContext(string guidString)
|
||||
{
|
||||
if (Guid.TryParse(guidString, out var guid))
|
||||
@ -306,11 +405,21 @@ public class CommandsManager
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first valid registered context of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of the context.</param>
|
||||
/// <returns>The first valid registered context, or null if not found.</returns>
|
||||
public virtual object? GetFirstValidRegisteredContext(Type type)
|
||||
{
|
||||
return RegisteredObjectInstances.Values.FirstOrDefault(obj => obj.GetType() == type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GUID of the specified context object.
|
||||
/// </summary>
|
||||
/// <param name="context">The context object, if any.</param>
|
||||
/// <returns>The GUID of the context object.</returns>
|
||||
public virtual Guid GetGuidFromContext(object? context = null)
|
||||
{
|
||||
if (context is not null)
|
||||
@ -321,6 +430,71 @@ public class CommandsManager
|
||||
return CurrentContextGuid ?? Guid.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified assembly to the list of assemblies used to search for commands.
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
public void AddAssembly(Assembly assembly)
|
||||
{
|
||||
if (!Assemblies.Contains(assembly))
|
||||
{
|
||||
Assemblies.Add(assembly);
|
||||
RefreshCommandsList();
|
||||
}
|
||||
else
|
||||
{
|
||||
CogwheelConsole.LogWarning($"Tried to add {assembly.FullName} but it is already registered.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified assembly from the list of assemblies used to search for commands.
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
public void RemoveAssembly(Assembly assembly)
|
||||
{
|
||||
if (Assemblies.Contains(assembly))
|
||||
{
|
||||
Assemblies.Remove(assembly);
|
||||
RefreshCommandsList();
|
||||
}
|
||||
else
|
||||
{
|
||||
CogwheelConsole.LogWarning($"Tried to remove {assembly.FullName} but it is not registered.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the list of assemblies used to search for commands.
|
||||
/// </summary>
|
||||
public void ClearAssemblies()
|
||||
{
|
||||
Assemblies.Clear();
|
||||
RefreshCommandsList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a custom parser for the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="parser"></param>
|
||||
public void AddCustomParser(Type type, Func<string, object> parser)
|
||||
{
|
||||
if (!CustomParsers.ContainsKey(type))
|
||||
{
|
||||
CustomParsers[type] = parser;
|
||||
}
|
||||
else
|
||||
{
|
||||
CogwheelConsole.LogWarning($"Tried to add a custom parser for {type.FullName} but it is already registered.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash of the properties of the specified object.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <returns>The hash of the object's properties.</returns>
|
||||
protected virtual string GetObjectPropertiesHash(object obj)
|
||||
{
|
||||
var properties = obj.GetType().GetProperties();
|
||||
@ -335,21 +509,27 @@ public class CommandsManager
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the list of commands by scanning the assemblies.
|
||||
/// </summary>
|
||||
protected virtual void RefreshCommandsList()
|
||||
{
|
||||
foreach (var type in Assembly.GetCallingAssembly().GetTypes())
|
||||
foreach (var assembly in Assemblies)
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
foreach (var method in methods)
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
var attributes = method.GetCustomAttributes<CommandAttribute>();
|
||||
var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
foreach (var attribute in attributes)
|
||||
foreach (var method in methods)
|
||||
{
|
||||
var commandName = attribute.Name ?? method.Name;
|
||||
var newCommand = new Command(commandName, attribute.Description, method);
|
||||
Commands.TryAdd(newCommand.Name, newCommand);
|
||||
var attributes = method.GetCustomAttributes<CommandAttribute>();
|
||||
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
var commandName = attribute.Name ?? method.Name;
|
||||
var newCommand = new Command(commandName, attribute.Description, method);
|
||||
Commands.TryAdd(newCommand.Name, newCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
105
src/DeafultCogwheelConsole.cs
Normal file
105
src/DeafultCogwheelConsole.cs
Normal file
@ -0,0 +1,105 @@
|
||||
namespace Cogwheel;
|
||||
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the Cogwheel console.
|
||||
/// </summary>
|
||||
public class DeafultCogwheelConsole : ICogwheelConsole
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the opening message of the console.
|
||||
/// </summary>
|
||||
public string OpeningMessage { get; set; } = "** COGWHEEL CONSOLE **";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the console is running.
|
||||
/// </summary>
|
||||
public bool IsRunning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the commands manager associated with the console.
|
||||
/// </summary>
|
||||
public CommandsManager CommandsManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the console with the specified commands manager.
|
||||
/// </summary>
|
||||
/// <param name="commandsManager">The commands manager to use.</param>
|
||||
public void Initialize(CommandsManager commandsManager)
|
||||
{
|
||||
CommandsManager = commandsManager;
|
||||
CommandsManager.RegisterObject(this);
|
||||
|
||||
Write(OpeningMessage);
|
||||
|
||||
IsRunning = true;
|
||||
while (IsRunning)
|
||||
{
|
||||
if (CommandsManager.CurrentContext != null)
|
||||
{
|
||||
Console.Write($"({CommandsManager.CurrentContext.GetType().FullName})> ");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write("(Global)> ");
|
||||
}
|
||||
string input = Console.ReadLine();
|
||||
Console.WriteLine();
|
||||
CommandsManager.RunCommand(input);
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log.</param>
|
||||
public void Log(string message)
|
||||
{
|
||||
Console.WriteLine($"[COGWHEEL] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message to log.</param>
|
||||
public void LogError(string message)
|
||||
{
|
||||
Console.WriteLine($"[COGWHEEL ERROR] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The warning message to log.</param>
|
||||
public void LogWarning(string message)
|
||||
{
|
||||
Console.WriteLine($"[COGWHEEL WARNING] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to write.</param>
|
||||
public void Write(string message)
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the console.
|
||||
/// </summary>
|
||||
public void ClearConsole()
|
||||
{
|
||||
Console.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exits the console.
|
||||
/// </summary>
|
||||
public void Exit()
|
||||
{
|
||||
IsRunning = false;
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
namespace Cogwheel;
|
||||
|
||||
public class TestClass
|
||||
{
|
||||
|
||||
public string Name { get; set; } = "Test";
|
||||
|
||||
public TestClass(string name)
|
||||
{
|
||||
Name = name;
|
||||
|
||||
COGWHEEL.RegisterObject(this);
|
||||
}
|
||||
|
||||
[Command(Name = "getname", Description = "Prints the name of the TestClass instance.")]
|
||||
private void GetName()
|
||||
{
|
||||
COGWHEEL.Log($"My name is {Name}");
|
||||
}
|
||||
}
|
52
src/changelog.md
Normal file
52
src/changelog.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.3.0] - 2025-01-03
|
||||
|
||||
### Added
|
||||
- Added more public CommandsManager access methods to the COGWHEEL static class
|
||||
- Ability to add custom parsers to the CommandManager with AddCustomerParser(Type, Func<string, object>)
|
||||
- Added two custom parsers from the COGWHEEL class: `System.Numerics.Vector2` and `System.Numerics.Vector3`
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
## [1.2.0] - 2025-01-02
|
||||
|
||||
### Added
|
||||
- Ability to add assemblies to the CommandManager
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
## [1.1.0] - 2025-01-02
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
## [1.0.0] - 2025-01-02
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
@ -1,17 +1,62 @@
|
||||
namespace Cogwheel;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for the Cogwheel console.
|
||||
/// </summary>
|
||||
public interface ICogwheelConsole
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the opening message of the console.
|
||||
/// </summary>
|
||||
public string OpeningMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the console is running.
|
||||
/// </summary>
|
||||
public bool IsRunning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the commands manager associated with the console.
|
||||
/// </summary>
|
||||
public CommandsManager CommandsManager { get; set; }
|
||||
|
||||
public void Initialize(CommandsManager commandsManager); // Make sure to pass the CommandsManager instance to the Console
|
||||
public void Log(string message);
|
||||
public void LogError(string message);
|
||||
public void LogWarning(string message);
|
||||
public void Write(string message);
|
||||
public void ClearConsole();
|
||||
public void Exit();
|
||||
/// <summary>
|
||||
/// Initializes the console with the specified commands manager.
|
||||
/// </summary>
|
||||
/// <param name="commandsManager">The commands manager to use.</param>
|
||||
public void Initialize(CommandsManager commandsManager);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log.</param>
|
||||
public void Log(string message);
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message to log.</param>
|
||||
public void LogError(string message);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The warning message to log.</param>
|
||||
public void LogWarning(string message);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to write.</param>
|
||||
public void Write(string message);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the console.
|
||||
/// </summary>
|
||||
public void ClearConsole();
|
||||
|
||||
/// <summary>
|
||||
/// Exits the console.
|
||||
/// </summary>
|
||||
public void Exit();
|
||||
}
|
@ -2,9 +2,23 @@ using System.Reflection;
|
||||
|
||||
namespace Cogwheel;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a command in the Cogwheel system.
|
||||
/// </summary>
|
||||
public interface ICommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the command.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the command.
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method associated with the command.
|
||||
/// </summary>
|
||||
public MethodBase Method { get; }
|
||||
}
|
Loading…
Reference in New Issue
Block a user