Compare commits

..

7 Commits

Author SHA1 Message Date
cfa47142ac fixed date on 1.3.0 2025-01-04 01:21:40 +00:00
15b916a52a Update to 1.3.0 2025-01-03 19:20:18 -06:00
8ffedd6ba9 - 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`
2025-01-03 19:12:19 -06:00
2a1c40a51c Update Program.cs with comments explaining how to init Cogwheel 2025-01-02 23:25:40 -06:00
f1a4ea0a65 Fixed the bad csprojects so both can build just fine, and cleaned up unused usings 2025-01-02 23:21:09 -06:00
99ba6e1710 Release 1.2.0 2025-01-02 13:54:01 -06:00
60979355d4 Added project for building dll file 2025-01-01 23:02:41 -06:00
13 changed files with 736 additions and 174 deletions

View File

@ -5,6 +5,14 @@
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Content Include="src\CogwheelLib.csproj" />
<Compile Remove="obj\**\*.AssemblyAttributes.cs" />
</ItemGroup>
</Project> </Project>

View File

@ -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;
}
}

View File

@ -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; using Cogwheel;
// Create a new instance of an ICogwheelConsole implementation
ICogwheelConsole cogwheelConsole = new DeafultCogwheelConsole(); ICogwheelConsole cogwheelConsole = new DeafultCogwheelConsole();
// Create a new instance of a CommandsManager
CommandsManager commandsManager = new 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); COGWHEEL.Initialize(commandsManager, cogwheelConsole);

View File

@ -1,26 +1,50 @@
using System;
using System.Linq;
using System.Numerics;
using System.Reflection;
namespace Cogwheel; namespace Cogwheel;
/// <summary>
/// Static class for interacting with the Cogwheel system
/// </summary>
public static class COGWHEEL public static class COGWHEEL
{ {
private static CommandsManager _commandsManager; private static CommandsManager _commandsManager;
private static ICogwheelConsole _console; 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) public static void Initialize(CommandsManager commandsManager, ICogwheelConsole console)
{ {
_console = console; _console = console;
_commandsManager = commandsManager; _commandsManager = commandsManager;
InitializeCustomParsers();
_commandsManager.Initialize(_console); _commandsManager.Initialize(_console);
_console.Initialize(_commandsManager); _console.Initialize(_commandsManager);
} }
// == Public static methods for the CommandsManager == // // == Public static methods for the CommandsManager == //
/// <summary>
/// Runs a command
/// </summary>
/// <param name="input"></param>
public static void RunCommand(string input) public static void RunCommand(string input)
{ {
_commandsManager.RunCommand(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) public static void RegisterObject(object obj)
{ {
_commandsManager.RegisterObject(obj); _commandsManager.RegisterObject(obj);
@ -28,43 +52,96 @@ public static class COGWHEEL
// == Public static methods for the Console == // // == Public static methods for the Console == //
/// <summary>
/// Logs a message to the console
/// </summary>
/// <param name="message"></param>
public static void Log(string message) public static void Log(string message)
{ {
_console.Log(message); _console.Log(message);
} }
/// <summary>
/// Logs an error message to the console
/// </summary>
/// <param name="message"></param>
public static void LogError(string message) public static void LogError(string message)
{ {
_console.LogError(message); _console.LogError(message);
} }
/// <summary>
/// Logs a warning message to the console
/// </summary>
/// <param name="message"></param>
public static void LogWarning(string message) public static void LogWarning(string message)
{ {
_console.LogWarning(message); _console.LogWarning(message);
} }
/// <summary>
/// Writes a message to the console
/// </summary>
/// <param name="message"></param>
public static void Write(string message) public static void Write(string message)
{ {
_console.Write(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 == // // == Built-in commands == //
/// <summary>
/// Quit the Cogwheel console
/// </summary>
[Command(Name = "quit", Description = "Quits the Cogwheel console.")] [Command(Name = "quit", Description = "Quits the Cogwheel console.")]
public static void QuitCogwheelConsole() public static void QuitCogwheelConsole()
{ {
_console.Exit(); _console.Exit();
} }
/// <summary>
/// Clears the console
/// </summary>
[Command(Name = "clear", Description = "Clears the console.")] [Command(Name = "clear", Description = "Clears the console.")]
public static void ClearConsole() public static void ClearConsole()
{ {
_console.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.")] [Command(Name = "help", Description = "Gets usage for given command.")]
public static void ShowHelp(string commandName) public static void ShowHelp(string commandName)
{ {
ICommand command = _commandsManager.GetCommandByName(commandName); ICommand? command = _commandsManager.GetCommandByName(commandName);
if (command is null) if (command is null)
{ {
_console.LogError($"Command error: '{commandName}' is not a command."); _console.LogError($"Command error: '{commandName}' is not a command.");
@ -76,6 +153,9 @@ public static class COGWHEEL
$"Usage: {_commandsManager.GetCommandUsage(command)}"); $"Usage: {_commandsManager.GetCommandUsage(command)}");
} }
/// <summary>
/// Lists all available commands
/// </summary>
[Command(Name = "list", Description = "Lists all available commands.")] [Command(Name = "list", Description = "Lists all available commands.")]
public static void ListCommands() 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.")] [Command(Name = "what", Description = "Prints the current context.")]
public static void ShowCurrentContext() public static void ShowCurrentContext()
{ {
if (_commandsManager.CurrentContext is null)
{
Log("No context set.");
return;
}
_console.Log($"Current context: {_commandsManager.CurrentContext.GetType()} : {_commandsManager.CurrentContextGuid}"); _console.Log($"Current context: {_commandsManager.CurrentContext.GetType()} : {_commandsManager.CurrentContextGuid}");
} }
[Command(Name = "test", Description = "Creates a new TestClass object.")] /// <summary>
public static void CreateTestObject(string name) /// 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}"); _commandsManager.AddAssembly(assembly);
var test = new TestClass(name);
} }
/// <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.")] [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 registeredObjects = _commandsManager.RegisteredObjectInstances;
var filteredObjects = string.IsNullOrEmpty(typeName) 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.")] [Command(Name = "set", Description = "Sets the context to the specified registered object.")]
private static void SetContextFromGuid(string guidString) private static void SetContextFromGuid(string guidString)
{ {
_commandsManager.SetContext(guidString); SetContext(guidString);
Log($"Set context to {_commandsManager.CurrentContext?.GetType().FullName} : {_commandsManager.CurrentContextGuid}"); 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
View 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>

View File

@ -2,12 +2,32 @@ using System.Reflection;
namespace Cogwheel; namespace Cogwheel;
/// <summary>
/// Represents a command in the Cogwheel system.
/// </summary>
public class Command : ICommand public class Command : ICommand
{ {
/// <summary>
/// Gets the name of the command.
/// </summary>
public string Name { get; } public string Name { get; }
/// <summary>
/// Gets the description of the command.
/// </summary>
public string Description { get; } public string Description { get; }
/// <summary>
/// Gets the method associated with the command.
/// </summary>
public MethodBase Method { get; } 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) public Command(string name, string description, MethodBase method)
{ {
Name = name; Name = name;

View File

@ -1,8 +1,20 @@
using System;
namespace Cogwheel; namespace Cogwheel;
/// <summary>
/// Attribute for marking methods as commands
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class CommandAttribute : Attribute 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 = ""; public string Description = "";
} }

View File

@ -2,25 +2,63 @@ using System.Reflection;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Exception = System.Exception;
namespace Cogwheel; namespace Cogwheel;
/// <summary>
/// Manages commands for the Cogwheel system.
/// </summary>
public class CommandsManager public class CommandsManager
{ {
/// <summary>
/// Gets or sets the console used by the CommandsManager.
/// </summary>
protected virtual ICogwheelConsole CogwheelConsole { get; set; } 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 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(); 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(); 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; } public virtual object? CurrentContext { get; set; }
/// <summary>
/// Gets or sets the GUID of the current context.
/// </summary>
public virtual Guid? CurrentContextGuid { get; set; } 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(); 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(); 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) public virtual void Initialize(ICogwheelConsole console)
{ {
CogwheelConsole = console; CogwheelConsole = console;
@ -28,7 +66,11 @@ public class CommandsManager
CogwheelConsole.Log($"CommandsManager initialized, Commands found: {Commands.Count}"); 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) public virtual (ICommand, List<object>)? GetCommandAndArgsFromString(string input)
{ {
var splitString = Regex.Matches(input, CommandPattern).Select(m => m.Groups["val"].Value).ToArray(); var splitString = Regex.Matches(input, CommandPattern).Select(m => m.Groups["val"].Value).ToArray();
@ -47,6 +89,12 @@ public class CommandsManager
return null; 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) public virtual bool RunCommand(string input, object? context = null)
{ {
if (string.IsNullOrWhiteSpace(input)) return false; if (string.IsNullOrWhiteSpace(input)) return false;
@ -72,13 +120,20 @@ public class CommandsManager
return ExecuteCommand(CurrentContext, command.Value); 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}'"); if (command.Value.Item1.Method.DeclaringType != null)
{
context = GetFirstValidRegisteredContext(command.Value.Item1.Method.DeclaringType); context = GetFirstValidRegisteredContext(command.Value.Item1.Method.DeclaringType);
if (context is null) if (context is null)
{ {
CogwheelConsole.LogError($"No context of type '{command.Value.Item1.Method.DeclaringType}' found"); CogwheelConsole.LogError($"No context of type '{command.Value.Item1.Method.DeclaringType}' found");
return false; return false;
} }
}
else
{
CogwheelConsole.LogError($"Command '{command.Value.Item1.Name}' has no declaring type");
return false;
}
if (IsCommandContextValid(command.Value.Item1, context)) if (IsCommandContextValid(command.Value.Item1, context))
{ {
@ -88,6 +143,11 @@ public class CommandsManager
return false; 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) public virtual string GetCommandUsage(ICommand command)
{ {
string output = ""; string output = "";
@ -119,7 +179,13 @@ public class CommandsManager
return output; 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(); var parameters = command.command.Method.GetParameters();
for (var parameterIndex = 0; parameterIndex < parameters.Length; parameterIndex++) for (var parameterIndex = 0; parameterIndex < parameters.Length; parameterIndex++)
@ -132,7 +198,7 @@ public class CommandsManager
if (TryParseParameter(parameters[parameterIndex].ParameterType.GetElementType(), if (TryParseParameter(parameters[parameterIndex].ParameterType.GetElementType(),
(string)command.args[argIndex], out var val)) (string)command.args[argIndex], out var val))
{ {
paramList.GetType().GetMethod("Add")?.Invoke(paramList, new[] { val }); paramList.GetType().GetMethod("Add")?.Invoke(paramList, [val]);
} }
else else
{ {
@ -152,8 +218,7 @@ public class CommandsManager
} }
else else
{ {
CogwheelConsole.LogError("Not enough args passed"); CogwheelConsole.LogError($"Not enough args passed. Usage: {GetCommandUsage(command.command)}");
CogwheelConsole.Log(GetCommandUsage(command.command));
return false; return false;
} }
} }
@ -183,7 +248,14 @@ public class CommandsManager
} }
} }
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)) if (parameterType == typeof(string))
{ {
@ -229,16 +301,22 @@ public class CommandsManager
return false; 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) public virtual ICommand? GetCommandByName(string commandName)
{ {
return Commands.GetValueOrDefault(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) 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); string seed = obj.GetType().FullName + GetObjectPropertiesHash(obj);
using (MD5 md5 = MD5.Create()) 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) public virtual bool IsCommandContextValid(ICommand command, object? context = null)
{ {
if (context is not null) if (context is not null)
@ -267,6 +351,11 @@ public class CommandsManager
return command.Method.DeclaringType == CurrentContext?.GetType(); 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) public virtual bool SetContext(object context)
{ {
CurrentContext = context; CurrentContext = context;
@ -279,6 +368,11 @@ public class CommandsManager
return true; 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) public virtual bool SetContext(Guid guid)
{ {
if (RegisteredObjectInstances.ContainsKey(guid)) if (RegisteredObjectInstances.ContainsKey(guid))
@ -295,6 +389,11 @@ public class CommandsManager
return true; 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) public virtual bool SetContext(string guidString)
{ {
if (Guid.TryParse(guidString, out var guid)) if (Guid.TryParse(guidString, out var guid))
@ -306,11 +405,21 @@ public class CommandsManager
return false; 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) public virtual object? GetFirstValidRegisteredContext(Type type)
{ {
return RegisteredObjectInstances.Values.FirstOrDefault(obj => obj.GetType() == 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) public virtual Guid GetGuidFromContext(object? context = null)
{ {
if (context is not null) if (context is not null)
@ -321,6 +430,71 @@ public class CommandsManager
return CurrentContextGuid ?? Guid.Empty; 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) protected virtual string GetObjectPropertiesHash(object obj)
{ {
var properties = obj.GetType().GetProperties(); var properties = obj.GetType().GetProperties();
@ -335,9 +509,14 @@ public class CommandsManager
return sb.ToString(); return sb.ToString();
} }
/// <summary>
/// Refreshes the list of commands by scanning the assemblies.
/// </summary>
protected virtual void RefreshCommandsList() protected virtual void RefreshCommandsList()
{ {
foreach (var type in Assembly.GetCallingAssembly().GetTypes()) foreach (var assembly in Assemblies)
{
foreach (var type in assembly.GetTypes())
{ {
var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance); var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance);
@ -355,3 +534,4 @@ public class CommandsManager
} }
} }
} }
}

View 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;
}
}

View File

@ -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
View 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

View File

@ -1,17 +1,62 @@
namespace Cogwheel; namespace Cogwheel;
/// <summary>
/// Interface for the Cogwheel console.
/// </summary>
public interface ICogwheelConsole public interface ICogwheelConsole
{ {
/// <summary>
/// Gets or sets the opening message of the console.
/// </summary>
public string OpeningMessage { get; set; } public string OpeningMessage { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the console is running.
/// </summary>
public bool IsRunning { get; set; } public bool IsRunning { get; set; }
/// <summary>
/// Gets or sets the commands manager associated with the console.
/// </summary>
public CommandsManager CommandsManager { get; set; } public CommandsManager CommandsManager { get; set; }
public void Initialize(CommandsManager commandsManager); // Make sure to pass the CommandsManager instance to the Console /// <summary>
public void Log(string message); /// Initializes the console with the specified commands manager.
public void LogError(string message); /// </summary>
public void LogWarning(string message); /// <param name="commandsManager">The commands manager to use.</param>
public void Write(string message); public void Initialize(CommandsManager commandsManager);
public void ClearConsole();
public void Exit();
/// <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();
} }

View File

@ -2,9 +2,23 @@ using System.Reflection;
namespace Cogwheel; namespace Cogwheel;
/// <summary>
/// Represents a command in the Cogwheel system.
/// </summary>
public interface ICommand public interface ICommand
{ {
/// <summary>
/// Gets the name of the command.
/// </summary>
public string Name { get; } public string Name { get; }
/// <summary>
/// Gets the description of the command.
/// </summary>
public string Description { get; } public string Description { get; }
/// <summary>
/// Gets the method associated with the command.
/// </summary>
public MethodBase Method { get; } public MethodBase Method { get; }
} }