From ee7aacc046cd0bf22ef97f07aef6de59da109ccb Mon Sep 17 00:00:00 2001 From: Chris Bell Date: Thu, 3 Oct 2024 15:50:50 -0500 Subject: [PATCH] More built in commands, CommandsManager now has a dependency on CogwheelConsole, can now search for and set context in the console --- Cogwheel/Assets/Cogwheel/Scripts/COGWHEEL.cs | 71 +++++++--- .../Cogwheel/Scripts/CommandsManager.cs | 126 +++++++++++++----- .../Cogwheel/Scripts/ICommandsManager.cs | 11 +- Cogwheel/Assets/Cogwheel/Scripts/Test.cs | 21 +++ Cogwheel/Assets/Cogwheel/Scripts/Test.cs.meta | 11 ++ Cogwheel/Assets/Cogwheel/Scripts/Test2.cs | 14 ++ .../Assets/Cogwheel/Scripts/Test2.cs.meta | 3 + .../Assets/Cogwheel/UI/CogwheelConsole.cs | 7 +- .../Assets/Cogwheel/UI/CogwheelConsole.uxml | 3 + .../Cogwheel/UI/CogwheelConsole.uxml.meta | 10 ++ Cogwheel/Assets/Scenes/SampleScene.unity | 87 +++++++++++- Cogwheel/Assets/UnityDefaultRuntimeTheme.tss | 2 + .../Assets/UnityDefaultRuntimeTheme.tss.meta | 11 ++ 13 files changed, 310 insertions(+), 67 deletions(-) create mode 100644 Cogwheel/Assets/Cogwheel/Scripts/Test.cs create mode 100644 Cogwheel/Assets/Cogwheel/Scripts/Test.cs.meta create mode 100644 Cogwheel/Assets/Cogwheel/Scripts/Test2.cs create mode 100644 Cogwheel/Assets/Cogwheel/Scripts/Test2.cs.meta create mode 100644 Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.uxml create mode 100644 Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.uxml.meta create mode 100644 Cogwheel/Assets/UnityDefaultRuntimeTheme.tss create mode 100644 Cogwheel/Assets/UnityDefaultRuntimeTheme.tss.meta diff --git a/Cogwheel/Assets/Cogwheel/Scripts/COGWHEEL.cs b/Cogwheel/Assets/Cogwheel/Scripts/COGWHEEL.cs index 51c0540..1c6058f 100644 --- a/Cogwheel/Assets/Cogwheel/Scripts/COGWHEEL.cs +++ b/Cogwheel/Assets/Cogwheel/Scripts/COGWHEEL.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using OpenCover.Framework.Model; using UnityEngine; @@ -8,11 +9,11 @@ namespace Cogwheel public static ICommandsManager CommandsManager { get; } public static CogwheelSettings Settings { get; } public static CogwheelConsole Console { get; private set; } + + static Color _color = Color.blue; static COGWHEEL() { - CommandsManager = new CommandsManager(); - CommandsManager.GetCommands(); Settings = Resources.Load("CogwheelSettings"); if (Settings is null) { @@ -20,6 +21,8 @@ namespace Cogwheel return; } Console = GameObject.Instantiate(Settings.CogwheelConsolePrefab).GetComponent(); + + CommandsManager = new CommandsManager(Console); } #region BUILT-IN COMMANDS @@ -35,6 +38,12 @@ namespace Cogwheel { Console.LogError(message); } + + [Command(Description = "Logs a warning to the console.")] + public static void LogWarning(string message) + { + Console.LogWarning(message); + } [Command(Description = "Clears the console.")] public static void Clear() @@ -42,42 +51,60 @@ namespace Cogwheel Console.Clear(); } - [Command(Description = "Lists all available commands.")] - public static void Help() + [Command(Description = "Lists available commands for the current context. Use '-a' to list all commands in registry.")] + public static void List(string arg = "") { foreach (var command in CommandsManager.Commands.Values) { if (command.Method.IsStatic) { - Console.Log($"{command.Name} - {command.Description}"); + Log($"{command.Name} - {command.Description}"); } else { - if (CommandsManager.SceneContext is not null) + if (arg == "-a") { - foreach (var behaviour in CommandsManager.SceneContext.GetComponents()) - { - if (behaviour.GetType() == command.Method.DeclaringType) - { - Console.Log($"({command.Method.DeclaringType.Name}) : {command.Name} - {command.Description}"); - } - else - { - Console.Log($"({command.Method.DeclaringType.Name}) : {command.Name} - {command.Description}"); - } - } + Log(CommandsManager.IsCommandContextValid(command) + ? $"({command.Method.DeclaringType.Name}) : {command.Name} - {command.Description}" + : $"({command.Method.DeclaringType.Name}) : {command.Name} - {command.Description}"); } - else + + if (CommandsManager.IsCommandContextValid(command)) { - Console.Log($"({command.Method.DeclaringType.Name}) : {command.Name} - {command.Description}"); + Log($"({command.Method.DeclaringType.Name}) : {command.Name} - {command.Description}"); } } - - //Console.Log($"({command.Method.DeclaringType.Name}) : {command.Name} - {command.Description}"); } } - + [Command(Description = "Gets usage for a given command")] + public static void Help(string commandName) + { + + Command command = CommandsManager.GetCommandByName(commandName); + if (command is null) + { + Console.LogError($"Command error: '{commandName}' is not a command."); + return; + } + + Console.Log($"-- Command Usage For {commandName} --\n" + + $"Description: {command.Description}\n" + + $"Usage: {CommandsManager.GetCommandUsage(command)}"); + } + + [Command(CommandName = "search", Description = "Searches the scene for a GameObject with the given name and sets the Context to it.")] + public static void SetContextByName(string name) + { + CommandsManager.SetContext(name); + } + + [Command(CommandName = "whois", Description = "Prints out the current Context name")] + public static void PrintContext() + { + Log($"Current context is: {CommandsManager.Context.name}"); + } + #endregion diff --git a/Cogwheel/Assets/Cogwheel/Scripts/CommandsManager.cs b/Cogwheel/Assets/Cogwheel/Scripts/CommandsManager.cs index c8d631d..5a5d05c 100644 --- a/Cogwheel/Assets/Cogwheel/Scripts/CommandsManager.cs +++ b/Cogwheel/Assets/Cogwheel/Scripts/CommandsManager.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; +using Unity.VisualScripting; using UnityEngine; using UnityEngine.UIElements; @@ -13,33 +14,50 @@ namespace Cogwheel public class CommandsManager : ICommandsManager { public Dictionary Commands { get; set; } = new(StringComparer.OrdinalIgnoreCase); - public GameObject SceneContext { get; set; } + public GameObject Context { get; set; } public Dictionary> CustomParsers { get; private set; } = new(); public string CommandPattern { get; set; } = "(?(\\([^\\)]+\\)))|\"(?[^\"]+)\"|'(?[^']+)'|(?[^\\s]+)"; public List Assemblies { get; set; } = new(); - public void GetCommands() + private CogwheelConsole _console; + + public CommandsManager(CogwheelConsole console) { - foreach (var type in Assembly.GetCallingAssembly().GetTypes()) - { - var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance); - - foreach (var method in methods) - { - var attributes = method.GetCustomAttributes(); - - foreach (var attribute in attributes) - { - var commandName = attribute.CommandName ?? method.Name; - var newCommand = new Command(commandName, method, attribute.Description); - Commands.TryAdd(newCommand.Name, newCommand); - } - } - } + _console = console; + + RefreshCommandsList(); } - public (Command, List)? GetCommandFromString(string input) + public void SetContext(GameObject context) + { + Context = context; + } + + public void SetContext(string name) + { + Context = SearchSceneForContext(name); + } + + public GameObject SearchSceneForContext(string name) + { + var go = GameObject.Find(name); + if (go is null) + { + _console.LogError($"Couldn't find an object with the name '{name}' in the scene"); + return null; + } + + _console.Log($"Context set to: '{go.name}' <{go.GetType()}>"); + return go; + } + + public Command GetCommandByName(string commandName) + { + return Commands.Keys.Contains(commandName) ? Commands[commandName] : null; + } + + public (Command, List)? GetCommandAndArgsFromString(string input) { var splitString = Regex.Matches(input, CommandPattern).Select(m => m.Groups["val"].Value).ToArray(); var commandName = splitString[0]; @@ -56,14 +74,14 @@ namespace Cogwheel return null; } - + public bool RunCommand(string input) { if (string.IsNullOrWhiteSpace(input)) return false; - var command = GetCommandFromString(input); + var command = GetCommandAndArgsFromString(input); if (command is null) { - Debug.LogError($"Command '{input}' not found"); + _console.LogError($"Command '{input}' not found"); return false; } @@ -72,13 +90,13 @@ namespace Cogwheel return ExecuteCommand(null, command.Value); } - if (SceneContext is null) + if (Context is null) { - Debug.LogError("SceneContext not set"); + _console.LogError("SceneContext not set"); return false; } - foreach (var component in SceneContext.GetComponents()) + foreach (var component in Context.GetComponents()) { if (command.Value.Item1.Method.DeclaringType!.IsInstanceOfType(component)) { @@ -86,7 +104,7 @@ namespace Cogwheel } } - Debug.LogError($"Instance of '{command.Value.Item1.Method.DeclaringType}' not found in SceneContext"); + _console.LogError($"Instance of '{command.Value.Item1.Method.DeclaringType}' not found in SceneContext"); return false; } @@ -116,8 +134,7 @@ namespace Cogwheel } else { - Debug.LogError($"Format exception: could not parse '{command.args[parameterIndex]}' as '{parameters[parameterIndex].ParameterType}'"); - //print usage + _console.LogError($"Format exception: could not parse '{command.args[parameterIndex]}' as '{parameters[parameterIndex].ParameterType}'"); return false; } } @@ -133,8 +150,8 @@ namespace Cogwheel } else { - Debug.LogError("Not enough args passed"); - //print usage + _console.LogError("Not enough args passed"); + _console.Log(GetCommandUsage(command.command)); return false; } } @@ -146,8 +163,7 @@ namespace Cogwheel } else { - Debug.LogError($"Format exception: could not parse '{command.args[parameterIndex]}' as '{parameters[parameterIndex].ParameterType}'"); - //print usage + _console.LogError($"Format exception: could not parse '{command.args[parameterIndex]}' as '{parameters[parameterIndex].ParameterType}'"); return false; } } @@ -160,8 +176,7 @@ namespace Cogwheel } catch (Exception e) { - Debug.LogError(e.Message); - //print usage + _console.LogError(e.Message); throw; } } @@ -183,7 +198,7 @@ namespace Cogwheel } catch (Exception e) { - Debug.LogError(e.Message); + _console.LogError(e.Message); parsedValue = null; return false; } @@ -201,7 +216,7 @@ namespace Cogwheel } catch (Exception e) { - Debug.LogError(e.Message); + _console.LogError(e.Message); parsedValue = null; return false; } @@ -211,6 +226,45 @@ namespace Cogwheel parsedValue = null; return false; } + + public bool IsCommandContextValid(Command command) + { + if (Context is null) return false; + + List components = new(); + foreach (var component in Context.GetComponents()) + { + components.Add(component.GetType()); + } + + if (components.Contains(command.Method.DeclaringType)) return true; + + components.Clear(); + return false; + } + + private void RefreshCommandsList() + { + foreach (var type in Assembly.GetCallingAssembly().GetTypes()) + { + var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance); + + foreach (var method in methods) + { + var attributes = method.GetCustomAttributes(); + + foreach (var attribute in attributes) + { + var commandName = attribute.CommandName ?? method.Name; + var newCommand = new Command(commandName, method, attribute.Description); + if (!Commands.Values.Contains(newCommand)) + { + Commands.TryAdd(newCommand.Name, newCommand); + } + } + } + } + } } } diff --git a/Cogwheel/Assets/Cogwheel/Scripts/ICommandsManager.cs b/Cogwheel/Assets/Cogwheel/Scripts/ICommandsManager.cs index f41ff5e..2970ccb 100644 --- a/Cogwheel/Assets/Cogwheel/Scripts/ICommandsManager.cs +++ b/Cogwheel/Assets/Cogwheel/Scripts/ICommandsManager.cs @@ -11,15 +11,18 @@ namespace Cogwheel public string CommandPattern { get; set; } public List Assemblies { get; set; } public Dictionary Commands { get; set; } - public GameObject SceneContext { get; set; } + public GameObject Context { get; set; } public Dictionary> CustomParsers { get; } - public void GetCommands(); - public (Command, List)? GetCommandFromString(string input); + public (Command, List)? GetCommandAndArgsFromString(string input); public bool RunCommand(string input); public string GetCommandUsage(Command command); - public bool ExecuteCommand(object obj, (Command command, List args) command); public bool TryParseParameter(Type parameterType, string parameterString, out object parsedValue); + public bool IsCommandContextValid(Command command); + public Command GetCommandByName(string commandName); + public void SetContext(GameObject context); + public void SetContext(string name); + public GameObject SearchSceneForContext(string name); } } \ No newline at end of file diff --git a/Cogwheel/Assets/Cogwheel/Scripts/Test.cs b/Cogwheel/Assets/Cogwheel/Scripts/Test.cs new file mode 100644 index 0000000..29565f9 --- /dev/null +++ b/Cogwheel/Assets/Cogwheel/Scripts/Test.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Cogwheel; +using Unity.VisualScripting; +using UnityEngine; +using UnityEngine.UIElements; + +public class Test : MonoBehaviour +{ + private void Start() + { + var settings = COGWHEEL.Settings; + } + + [Command(CommandName = "private", Description = "This is a private command")] + private void PrivateCommand(int value, string text, float f) + { + Debug.Log($"PrivateCommand called: int is {value} and string is {text} and float is {f}"); + } +} diff --git a/Cogwheel/Assets/Cogwheel/Scripts/Test.cs.meta b/Cogwheel/Assets/Cogwheel/Scripts/Test.cs.meta new file mode 100644 index 0000000..655e9d9 --- /dev/null +++ b/Cogwheel/Assets/Cogwheel/Scripts/Test.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f2dd85165475cf7a81bf4b02e9502f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Cogwheel/Assets/Cogwheel/Scripts/Test2.cs b/Cogwheel/Assets/Cogwheel/Scripts/Test2.cs new file mode 100644 index 0000000..cbc7e07 --- /dev/null +++ b/Cogwheel/Assets/Cogwheel/Scripts/Test2.cs @@ -0,0 +1,14 @@ +using UnityEngine; + +namespace Cogwheel +{ + public class Test2 : MonoBehaviour + { + + [Command(CommandName = "test2", Description = "Command on Test2")] + private void Test2Command(string input) + { + Debug.Log($"Test 2 ran: {input}"); + } + } +} \ No newline at end of file diff --git a/Cogwheel/Assets/Cogwheel/Scripts/Test2.cs.meta b/Cogwheel/Assets/Cogwheel/Scripts/Test2.cs.meta new file mode 100644 index 0000000..60d21fc --- /dev/null +++ b/Cogwheel/Assets/Cogwheel/Scripts/Test2.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a2c4c2a5f3f0432eaf914b6d27031ca6 +timeCreated: 1727877159 \ No newline at end of file diff --git a/Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.cs b/Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.cs index 6be68c3..00e8106 100644 --- a/Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.cs +++ b/Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.cs @@ -42,7 +42,12 @@ public class CogwheelConsole : MonoBehaviour public void LogError(string message) { - Log($"{message}"); + Log($"ERROR: {message}"); + } + + public void LogWarning(string message) + { + Log($"WARNING: {message}"); } public void Clear() diff --git a/Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.uxml b/Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.uxml new file mode 100644 index 0000000..ccf8a5b --- /dev/null +++ b/Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.uxml @@ -0,0 +1,3 @@ + + + diff --git a/Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.uxml.meta b/Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.uxml.meta new file mode 100644 index 0000000..6e4d839 --- /dev/null +++ b/Cogwheel/Assets/Cogwheel/UI/CogwheelConsole.uxml.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 6d915ffc1116cd340b99d408824b929e +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0} diff --git a/Cogwheel/Assets/Scenes/SampleScene.unity b/Cogwheel/Assets/Scenes/SampleScene.unity index 01a8dcc..d1d7d44 100644 --- a/Cogwheel/Assets/Scenes/SampleScene.unity +++ b/Cogwheel/Assets/Scenes/SampleScene.unity @@ -38,6 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 705507994} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: @@ -227,7 +228,6 @@ GameObject: - component: {fileID: 963194228} - component: {fileID: 963194227} - component: {fileID: 963194226} - - component: {fileID: 963194229} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -309,21 +309,100 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &963194229 +--- !u!1 &1312985368 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1312985370} + - component: {fileID: 1312985369} + m_Layer: 0 + m_Name: Test2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1312985369 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 963194225} + m_GameObject: {fileID: 1312985368} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 4a61e49036c312c83a47481446b0c01a, type: 3} + m_Script: {fileID: 11500000, guid: a2c4c2a5f3f0432eaf914b6d27031ca6, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!4 &1312985370 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1312985368} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1830122060 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1830122061} + - component: {fileID: 1830122062} + m_Layer: 0 + m_Name: Test + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1830122061 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1830122060} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1830122062 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1830122060} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f2dd85165475cf7a81bf4b02e9502f4, type: 3} + m_Name: + m_EditorClassIdentifier: + context: {fileID: 0} --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 m_Roots: - {fileID: 963194228} - {fileID: 705507995} + - {fileID: 1830122061} + - {fileID: 1312985370} diff --git a/Cogwheel/Assets/UnityDefaultRuntimeTheme.tss b/Cogwheel/Assets/UnityDefaultRuntimeTheme.tss new file mode 100644 index 0000000..79453c7 --- /dev/null +++ b/Cogwheel/Assets/UnityDefaultRuntimeTheme.tss @@ -0,0 +1,2 @@ +@import url("unity-theme://default"); +VisualElement {} \ No newline at end of file diff --git a/Cogwheel/Assets/UnityDefaultRuntimeTheme.tss.meta b/Cogwheel/Assets/UnityDefaultRuntimeTheme.tss.meta new file mode 100644 index 0000000..fea57bf --- /dev/null +++ b/Cogwheel/Assets/UnityDefaultRuntimeTheme.tss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d98f6689678726a488b8ca0747fb3857 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12388, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0