diff --git a/GamecraftModdingAPI/Commands/CommandBuilder.cs b/GamecraftModdingAPI/Commands/CommandBuilder.cs new file mode 100644 index 0000000..f0f9258 --- /dev/null +++ b/GamecraftModdingAPI/Commands/CommandBuilder.cs @@ -0,0 +1,181 @@ +using System; + +using Svelto.ECS; + +using GamecraftModdingAPI.Utility; + +namespace GamecraftModdingAPI.Commands +{ + /// + /// Custom Command builder. + /// + public class CommandBuilder + { + private string name; + + private string description; + + private short parameterCount; + + private ICustomCommandEngine commandEngine; + + /// + /// Create a new command builder. + /// + public CommandBuilder() + { + name = ""; + description = null; + parameterCount = -1; + } + + /// + /// Create and return a command builder. + /// + /// The builder. + public static CommandBuilder Builder() + { + return new CommandBuilder(); + } + + /// + /// Create a new command builder. + /// + /// The command name. + /// The command description (shown in help). + public CommandBuilder(string name, string description = null) + { + this.name = name; + this.description = description; + parameterCount = -1; + } + + /// + /// Create and return a command builder. + /// If name and description are provided, this is equivalent to Builder().Name(name).Description(description) + /// + /// The command name. + /// The command description (shown in help). + /// The builder. + public static CommandBuilder Builder(string name, string description = null) + { + return new CommandBuilder(name, description); + } + + /// + /// Name the command. + /// + /// The builder. + /// The command name. + public CommandBuilder Name(string name) + { + this.name = name; + return this; + } + + /// + /// Describe the command. + /// + /// The builder. + /// The command description (shown in help). + public CommandBuilder Description(string description) + { + this.description = description; + return this; + } + + /// + /// Set the action the command performs. + /// + /// The builder. + /// The action to perform when the command is called. + public CommandBuilder Action(Action action) + { + parameterCount = 0; + commandEngine = new SimpleCustomCommandEngine(action, name, description); + return this; + } + + /// + /// Set the action the command performs. + /// + /// The builder. + /// The action to perform when the command is called. + /// The 1st parameter's type. + public CommandBuilder Action(Action action) + { + parameterCount = 1; + commandEngine = new SimpleCustomCommandEngine(action, name, description); + return this; + } + + /// + /// Set the action the command performs. + /// + /// The builder. + /// The action to perform when the command is called. + /// The 1st parameter's type. + /// The 2nd parameter's type. + public CommandBuilder Action(Action action) + { + parameterCount = 2; + commandEngine = new SimpleCustomCommandEngine(action, name, description); + return this; + } + + /// + /// Set the action the command performs. + /// + /// The builder. + /// The action to perform when the command is called. + /// The 1st parameter's type. + /// The 2nd parameter's type. + /// The 3rd parameter's type. + public CommandBuilder Action(Action action) + { + parameterCount = 3; + commandEngine = new SimpleCustomCommandEngine(action, name, description); + return this; + } + + /// + /// Build the command. + /// + /// The built command. + /// Automatically register the command with CommandManager.AddCommand()? + public ICustomCommandEngine Build(bool register = true) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new InvalidOperationException("Command name must be defined before Build() is called"); + } + if (commandEngine == null) + { + throw new InvalidOperationException("Command action must be defined before Build() is called"); + } + if (string.IsNullOrWhiteSpace(description)) + { + Logging.LogWarning($"Command {name} was built without a description"); + } + if (register) + { + CommandManager.AddCommand(commandEngine); + Logging.MetaDebugLog($"Command {FullName()} was automatically registered"); + } + return commandEngine; + } + + /// + /// Get the full command name, in the form [name]::[description]::[# of parameters] + /// + /// The name. + public string FullName() + { + if (string.IsNullOrWhiteSpace(description)) + { + return name + "::" + parameterCount; + } + return name + "::" + description + "::" + parameterCount; + } + } +} diff --git a/GamecraftModdingAPI/Commands/CommandManager.cs b/GamecraftModdingAPI/Commands/CommandManager.cs index f2f79b4..986405e 100644 --- a/GamecraftModdingAPI/Commands/CommandManager.cs +++ b/GamecraftModdingAPI/Commands/CommandManager.cs @@ -22,6 +22,10 @@ namespace GamecraftModdingAPI.Commands public static void AddCommand(ICustomCommandEngine engine) { + if (ExistsCommand(engine)) + { + Logging.LogWarning($"Command {engine.Name} already exists!"); + } _customCommands[engine.Name] = engine; if (_lastEngineRoot != null) { diff --git a/GamecraftModdingAPI/GamecraftModdingAPI.csproj b/GamecraftModdingAPI/GamecraftModdingAPI.csproj index 9b76ea5..263c18a 100644 --- a/GamecraftModdingAPI/GamecraftModdingAPI.csproj +++ b/GamecraftModdingAPI/GamecraftModdingAPI.csproj @@ -3,7 +3,7 @@ net472 true - 0.2.2 + 1.0.0 Exmods GNU General Public Licence 3+ https://git.exmods.org/modtainers/GamecraftModdingAPI diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs index 11d63c6..732854e 100644 --- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs +++ b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs @@ -81,8 +81,67 @@ namespace GamecraftModdingAPI.Tests // debug/test commands if (Dependency.Hell("ExtraCommands")) { - CommandManager.AddCommand(new SimpleCustomCommandEngine(() => { UnityEngine.Application.Quit(); }, - "Exit", "Close Gamecraft without any prompts")); + CommandBuilder.Builder() + .Name("Exit") + .Description("Close Gamecraft immediately, without any prompts") + .Action(() => { UnityEngine.Application.Quit(); }) + .Build(); + + CommandBuilder.Builder() + .Name("SetFOV") + .Description("Set the player camera's field of view") + .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; }) + .Build(); + + CommandBuilder.Builder() + .Name("MoveLastBlock") + .Description("Move the most-recently-placed block, and any connected blocks by the given offset") + .Action((float x, float y, float z) => { + bool success = GamecraftModdingAPI.Blocks.Movement.MoveConnectedBlocks( + GamecraftModdingAPI.Blocks.BlockIdentifiers.LatestBlockID, + new Unity.Mathematics.float3(x, y, z)); + if (!success) + { + GamecraftModdingAPI.Utility.Logging.CommandLogError("Blocks can only be moved in Build mode!"); + } + }).Build(); + + CommandBuilder.Builder() + .Name("PlaceAluminium") + .Description("Place a block of aluminium at the given coordinates") + .Action((float x, float y, float z) => { Blocks.Placement.PlaceBlock(Blocks.BlockIDs.AluminiumCube, new Unity.Mathematics.float3(x, y, z)); }) + .Build(); + + System.Random random = new System.Random(); // for command below + CommandBuilder.Builder() + .Name("RandomizeSignalsInputs") + .Description("Do the thing") + .Action(() => { + if (!GameState.IsSimulationMode()) + { + Logging.CommandLogError("You must be in simulation mode for this to work!"); + return; + } + Tasks.Repeatable task = new Tasks.Repeatable( + () => { + uint count = 0; + EGID[] eBlocks = Blocks.Signals.GetElectricBlocks(); + for (uint i = 0u; i < eBlocks.Length; i++) + { + uint[] ids = Blocks.Signals.GetSignalIDs(eBlocks[i]); + for (uint j = 0u; j < ids.Length; j++) + { + Blocks.Signals.SetSignalByID(ids[j], (float)random.NextDouble()); + count++; + } + } + Logging.MetaDebugLog($"Did the thing on {count} inputs"); + }, + () => { return GameState.IsSimulationMode(); }); + Tasks.Scheduler.Schedule(task); + }).Build(); + + /* CommandManager.AddCommand(new SimpleCustomCommandEngine((float d) => { UnityEngine.Camera.main.fieldOfView = d; }, "SetFOV", "Set the player camera's field of view")); CommandManager.AddCommand(new SimpleCustomCommandEngine( @@ -98,10 +157,6 @@ namespace GamecraftModdingAPI.Tests CommandManager.AddCommand(new SimpleCustomCommandEngine( (x, y, z) => { Blocks.Placement.PlaceBlock(Blocks.BlockIDs.AluminiumCube, new Unity.Mathematics.float3(x, y, z)); }, "PlaceAluminium", "Place a block of aluminium at the given coordinates")); - Analytics.DeltaDNAHelper.PlayerLifetimeParameters plp = new Analytics.DeltaDNAHelper.PlayerLifetimeParameters(); - CommandManager.AddCommand(new SimpleCustomCommandEngine( - (s) => { Analytics.DeltaDNAHelper.SendActionCompletedEvent(in plp, s.Replace(", ", " ")); }, - "SendAnalyticsAction", "Send an analytics action")); System.Random random = new System.Random(); // for command below CommandManager.AddCommand(new SimpleCustomCommandEngine( () => { @@ -127,6 +182,7 @@ namespace GamecraftModdingAPI.Tests () => { return GameState.IsSimulationMode(); }); Tasks.Scheduler.Schedule(task); }, "RandomizeSignalsInputs", "Do the thing")); + */ } // dependency test diff --git a/doxygen.conf b/doxygen.conf index 4596b01..0535320 100644 --- a/doxygen.conf +++ b/doxygen.conf @@ -38,7 +38,7 @@ PROJECT_NAME = "GamecraftModdingAPI" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "v0.2.2" +PROJECT_NUMBER = "v1.0.0" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a