Add command support

This commit is contained in:
NGnius (Graham) 2019-12-14 23:20:20 -08:00
parent fae054b72b
commit 047f0bb344
22 changed files with 465 additions and 42 deletions

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GamecraftModdingAPI.Commands
{
/// <summary>
/// UNIMPLEMENTED!
/// Convenient factories for command engines
/// </summary>
public static class CommandEngineFactory
{
//TODO
}
}

View file

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Svelto.ECS;
namespace GamecraftModdingAPI.Commands
{
/// <summary>
/// Keeps track of custom commands
/// This is used to add, remove and get command engines
/// </summary>
public static class CommandManager
{
private static Dictionary<string, ICustomCommandEngine> _customCommands = new Dictionary<string, ICustomCommandEngine>();
public static void AddCommand(ICustomCommandEngine engine)
{
_customCommands[engine.Name] = engine;
}
public static bool ExistsCommand(string name)
{
return _customCommands.ContainsKey(name);
}
public static bool ExistsCommand(ICustomCommandEngine engine)
{
return ExistsCommand(engine.Name);
}
public static ICustomCommandEngine GetCommand(string name)
{
return _customCommands[name];
}
public static Dictionary<string, ICustomCommandEngine> GetCommands()
{
return _customCommands;
}
public static void RemoveCommand(string name)
{
_customCommands.Remove(name);
}
public static void RegisterEngines(EnginesRoot enginesRoot)
{
foreach (var key in _customCommands.Keys)
{
enginesRoot.AddEngine(_customCommands[key]);
}
}
}
}

View file

@ -15,14 +15,17 @@ using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands
{
/// <summary>
/// Patch of RobocraftX.GUI.CommandLine.CommandLineCompositionRoot.Compose<T>()
/// </summary>
[HarmonyPatch]
public static class CommandPatch
{
public static void Prefix(UnityContext<FullGameCompositionRoot> contextHolder, EnginesRoot enginesRoot, World physicsWorld, Action reloadGame, MultiplayerInitParameters multiplayerParameters)
public static void Postfix(UnityContext<FullGameCompositionRoot> contextHolder, EnginesRoot enginesRoot, World physicsWorld, Action reloadGame, MultiplayerInitParameters multiplayerParameters)
{
Logging.Log("Command Line was loaded");
Logging.MetaDebugLog("Command Line was loaded");
// When a game is loaded, register the command engines
// TODO
CommandManager.RegisterEngines(enginesRoot);
}
public static MethodBase TargetMethod(HarmonyInstance instance)

View file

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using uREPL;
using RobocraftX.CommandLine.Custom;
namespace GamecraftModdingAPI.Commands
{
/// <summary>
/// Convenient methods for registering commands to Gamecraft.
/// All methods register to the command line and console block by default.
/// </summary>
public static class CommandRegistrationHelper
{
public static void Register(string name, Action action, string desc, bool noConsole = false)
{
RuntimeCommands.Register(name, action, desc);
if (noConsole) { return; }
ConsoleCommands.Register(name, action, desc);
}
public static void Register(string name, Action<object> action, string desc, bool noConsole = false)
{
Register<object>(name, action, desc, noConsole);
}
public static void Register(string name, Action<object, object> action, string desc, bool noConsole = false)
{
Register<object, object>(name, action, desc, noConsole);
}
public static void Register(string name, Action<object, object, object> action, string desc, bool noConsole = false)
{
Register<object, object, object>(name, action, desc, noConsole);
}
public static void Register<Param0>(string name, Action<Param0> action, string desc, bool noConsole = false)
{
RuntimeCommands.Register<Param0>(name, action, desc);
if (noConsole) { return; }
ConsoleCommands.Register<Param0>(name, action, desc);
}
public static void Register<Param0, Param1>(string name, Action<Param0, Param1> action, string desc, bool noConsole = false)
{
RuntimeCommands.Register<Param0, Param1>(name, action, desc);
if (noConsole) { return; }
ConsoleCommands.Register<Param0, Param1>(name, action, desc);
}
public static void Register<Param0, Param1, Param2>(string name, Action<Param0, Param1, Param2> action, string desc, bool noConsole = false)
{
RuntimeCommands.Register<Param0, Param1, Param2>(name, action, desc);
if (noConsole) { return; }
ConsoleCommands.Register<Param0, Param1, Param2>(name, action, desc);
}
public static void Unregister(string name, bool noConsole = false)
{
RuntimeCommands.Unregister(name);
if (noConsole) { return; }
ConsoleCommands.Unregister(name);
}
}
}

View file

@ -7,12 +7,19 @@ using Svelto.ECS;
namespace GamecraftModdingAPI.Commands
{
public interface ICustomCommandEngine : IEngine, IQueryingEntitiesEngine
/// <summary>
/// Engine interface to handle command operations
/// </summary>
public interface ICustomCommandEngine : IEngine, IQueryingEntitiesEngine, IDisposable
{
/// <summary>
/// The name of the command
/// </summary>
string Name { get; }
/// <summary>
/// The command's description, shown in command help messages
/// </summary>
string Description { get; }
void ExecuteCommand();
}
}

View file

@ -0,0 +1,56 @@
using Svelto.ECS;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GamecraftModdingAPI.Commands
{
/// <summary>
/// A simple implementation of ICustomCommandEngine sufficient for most commands.
/// This version is for commands which take 0 argument(s)
/// </summary>
class SimpleCustomCommandEngine : ICustomCommandEngine
{
/// <summary>
/// The name of the command
/// </summary>
public string Name { get; }
/// <summary>
/// The command's description, shown in command help messages
/// </summary>
public string Description { get; }
/// <summary>
/// The operation to execute when the command is called by the player
/// </summary>
private Action runCommand;
public IEntitiesDB entitiesDB { set; private get; }
public void Dispose()
{
CommandRegistrationHelper.Unregister(this.Name);
}
public void Ready()
{
CommandRegistrationHelper.Register(this.Name, this.runCommand, this.Description);
}
/// <summary>
/// Construct the engine
/// </summary>
/// <param name="command">The command's operation</param>
/// <param name="name">The name of the command</param>
/// <param name="description">The command's description, shown in command help messages</param>
public SimpleCustomCommandEngine(Action command, string name, string description)
{
this.runCommand = command;
this.Name = name;
this.Description = description;
}
}
}

View file

@ -0,0 +1,47 @@
using Svelto.ECS;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GamecraftModdingAPI.Commands
{
/// <summary>
/// A simple implementation of ICustomCommandEngine sufficient for most commands.
/// This version is for commands which take 1 argument(s)
/// </summary>
class SimpleCustomCommandEngine<A> : ICustomCommandEngine
{
public string Name { get; }
public string Description { get; }
private Action<A> runCommand;
public IEntitiesDB entitiesDB { set; private get; }
public void Dispose()
{
CommandRegistrationHelper.Unregister(this.Name);
}
public void Ready()
{
CommandRegistrationHelper.Register<A>(this.Name, this.runCommand, this.Description);
}
/// <summary>
/// Construct the engine
/// </summary>
/// <param name="command">The command's operation</param>
/// <param name="name">The name of the command</param>
/// <param name="description">The command's description, shown in command help messages</param>
public SimpleCustomCommandEngine(Action<A> command, string name, string description)
{
this.runCommand = command;
this.Name = name;
this.Description = description;
}
}
}

View file

@ -0,0 +1,47 @@
using Svelto.ECS;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GamecraftModdingAPI.Commands
{
/// <summary>
/// A simple implementation of ICustomCommandEngine sufficient for most commands.
/// This version is for commands which take 2 argument(s)
/// </summary>
class SimpleCustomCommandEngine<A,B> : ICustomCommandEngine
{
public string Name { get; }
public string Description { get; }
private Action<A,B> runCommand;
public IEntitiesDB entitiesDB { set; private get; }
public void Dispose()
{
CommandRegistrationHelper.Unregister(this.Name);
}
public void Ready()
{
CommandRegistrationHelper.Register<A,B>(this.Name, this.runCommand, this.Description);
}
/// <summary>
/// Construct the engine
/// </summary>
/// <param name="command">The command's operation</param>
/// <param name="name">The name of the command</param>
/// <param name="description">The command's description, shown in command help messages</param>
public SimpleCustomCommandEngine(Action<A,B> command, string name, string description)
{
this.runCommand = command;
this.Name = name;
this.Description = description;
}
}
}

View file

@ -0,0 +1,47 @@
using Svelto.ECS;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GamecraftModdingAPI.Commands
{
/// <summary>
/// A simple implementation of ICustomCommandEngine sufficient for most commands.
/// This version is for commands which take 3 argument(s)
/// </summary>
class SimpleCustomCommandEngine<A,B,C> : ICustomCommandEngine
{
public string Name { get; }
public string Description { get; }
private Action<A,B,C> runCommand;
public IEntitiesDB entitiesDB { set; private get; }
public void Dispose()
{
CommandRegistrationHelper.Unregister(this.Name);
}
public void Ready()
{
CommandRegistrationHelper.Register<A,B,C>(this.Name, this.runCommand, this.Description);
}
/// <summary>
/// Construct the engine
/// </summary>
/// <param name="command">The command's operation</param>
/// <param name="name">The name of the command</param>
/// <param name="description">The command's description, shown in command help messages</param>
public SimpleCustomCommandEngine(Action<A,B,C> command, string name, string description)
{
this.runCommand = command;
this.Name = name;
this.Description = description;
}
}
}

View file

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Svelto.ECS;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Convenient factories for mod event engines
/// </summary>
public static class EventEngineFactory
{
/// <summary>
/// Factory method which automatically adds the SimpleEventHandlerEngine to the Manager
/// </summary>
/// <param name="name">The name of the engine</param>
/// <param name="type">The type of event to handle</param>
/// <param name="onActivated">The operation to do when the event is created</param>
/// <param name="onDestroyed">The operation to do when the event is destroyed (if applicable)</param>
/// <returns>The created object</returns>
public static SimpleEventHandlerEngine CreateAddSimpleHandler(string name, object type, Action onActivated, Action onDestroyed)
{
var engine = new SimpleEventHandlerEngine(onActivated, onDestroyed, type, name);
EventManager.AddEventHandler(engine);
return engine;
}
/// <summary>
/// Factory method which automatically adds the SimpleEventHandlerEngine to the Manager
/// </summary>
/// <param name="name">The name of the engine</param>
/// <param name="type">The type of event to handle</param>
/// <param name="onActivated">The operation to do when the event is created</param>
/// <param name="onDestroyed">The operation to do when the event is destroyed (if applicable)</param>
/// <returns>The created object</returns>
public static SimpleEventHandlerEngine CreateAddSimpleHandler(string name, object type, Action<IEntitiesDB> onActivated, Action<IEntitiesDB> onDestroyed)
{
var engine = new SimpleEventHandlerEngine(onActivated, onDestroyed, type, name);
EventManager.AddEventHandler(engine);
return engine;
}
/// <summary>
/// Factory method which automatically adds the SimpleEventEmitterEngine to the Manager
/// </summary>
/// <param name="name">The name of the engine</param>
/// <param name="type">The type of event to emit</param>
/// <param name="isRemovable">Will removing this engine not break your code?</param>
/// <returns>The created object</returns>
public static SimpleEventEmitterEngine CreateAddSimpleEmitter(string name, object type, bool isRemovable = true)
{
var engine = new SimpleEventEmitterEngine(type, name, isRemovable);
EventManager.AddEventEmitter(engine);
return engine;
}
}
}

View file

@ -9,9 +9,9 @@ namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Keeps track of event handlers and emitters.
/// This class can be used to add, remove and get event handlers and emitters.
/// This is used to add, remove and get API event handlers and emitters.
/// </summary>
public static class Manager
public static class EventManager
{
private static Dictionary<string, IEventEmitterEngine> _eventEmitters = new Dictionary<string, IEventEmitterEngine>();
@ -98,6 +98,5 @@ namespace GamecraftModdingAPI.Events
enginesRoot.AddEngine(_eventEmitters[key]);
}
}
}
}

View file

@ -21,9 +21,9 @@ namespace GamecraftModdingAPI.Events
{
// A new EnginesRoot is always created when ActivateGame is called
// so all event emitters and handlers must be re-registered.
Manager.RegisterEngines(____mainGameEnginesRoot);
EventManager.RegisterEngines(____mainGameEnginesRoot);
Logging.Log("Dispatching Game Activated event");
Manager.GetEventEmitter("GamecraftModdingAPIGameActivatedEventEmitter").Emit();
EventManager.GetEventEmitter("GamecraftModdingAPIGameActivatedEventEmitter").Emit();
}
}
}

View file

@ -21,7 +21,7 @@ namespace GamecraftModdingAPI.Events
{
// Event emitters and handlers should already be registered by GameActivatedPatch
Logging.Log("Dispatching Game Reloaded event");
Manager.GetEventEmitter("GamecraftModdingAPIGameReloadedEventEmitter").Emit();
EventManager.GetEventEmitter("GamecraftModdingAPIGameReloadedEventEmitter").Emit();
}
}
}

View file

@ -21,7 +21,7 @@ namespace GamecraftModdingAPI.Events
{
// Event emitters and handlers should already be registered by GameActivated event
Logging.Log("Dispatching Game Switched To event");
Manager.GetEventEmitter("GamecraftModdingAPIGameSwitchedToEventEmitter").Emit();
EventManager.GetEventEmitter("GamecraftModdingAPIGameSwitchedToEventEmitter").Emit();
}
}
}

View file

@ -23,15 +23,15 @@ namespace GamecraftModdingAPI.Events
{
// A new EnginesRoot is always created when ActivateMenu is called
// so all event emitters and handlers must be re-registered.
Manager.RegisterEngines(____frontEndEnginesRoot);
EventManager.RegisterEngines(____frontEndEnginesRoot);
if (firstLoad)
{
firstLoad = false;
Logging.Log("Dispatching App Init event");
Manager.GetEventEmitter("GamecraftModdingAPIApplicationInitializedEventEmitter").Emit();
EventManager.GetEventEmitter("GamecraftModdingAPIApplicationInitializedEventEmitter").Emit();
}
Logging.Log("Dispatching Menu Activated event");
Manager.GetEventEmitter("GamecraftModdingAPIMenuActivatedEventEmitter").Emit();
EventManager.GetEventEmitter("GamecraftModdingAPIMenuActivatedEventEmitter").Emit();
}
}
}

View file

@ -21,7 +21,7 @@ namespace GamecraftModdingAPI.Events
{
// Event emitters and handlers should already be registered by MenuActivated event
Logging.Log("Dispatching Menu Switched To event");
Manager.GetEventEmitter("GamecraftModdingAPIMenuSwitchedToEventEmitter").Emit();
EventManager.GetEventEmitter("GamecraftModdingAPIMenuSwitchedToEventEmitter").Emit();
}
}
}

View file

@ -11,7 +11,7 @@ namespace GamecraftModdingAPI.Events
/// <summary>
/// A simple implementation of IEventEmitterEngine sufficient for most uses
/// </summary>
class SimpleEventEmitterEngine : IEventEmitterEngine
public class SimpleEventEmitterEngine : IEventEmitterEngine
{
public string Name { get; set; }
public object type { get; set; }

View file

@ -10,7 +10,7 @@ namespace GamecraftModdingAPI.Events
/// <summary>
/// A simple implementation of IEventHandlerEngine sufficient for most uses
/// </summary>
class SimpleEventHandlerEngine : IEventHandlerEngine
public class SimpleEventHandlerEngine : IEventHandlerEngine
{
public object type { get; set; }
public string Name { get; set; }
@ -53,15 +53,22 @@ namespace GamecraftModdingAPI.Events
}
/// <summary>
///
/// Construct the engine
/// </summary>
/// <param name="activated"></param>
/// <param name="removed"></param>
/// <param name="type"></param>
/// <param name="name"></param>
/// <param name="activated">The operation to do when the event is created</param>
/// <param name="removed">The operation to do when the event is destroyed (if applicable)</param>
/// <param name="type">The type of event to handle</param>
/// <param name="name">The name of the engine</param>
public SimpleEventHandlerEngine(Action activated, Action removed, object type, string name)
: this((IEntitiesDB _) => { activated.Invoke(); }, (IEntitiesDB _) => { removed.Invoke(); }, type, name) { }
/// <summary>
/// Construct the engine
/// </summary>
/// <param name="activated">The operation to do when the event is created</param>
/// <param name="removed">The operation to do when the event is destroyed (if applicable)</param>
/// <param name="type">The type of event to handler</param>
/// <param name="name">The name of the engine</param>
public SimpleEventHandlerEngine(Action<IEntitiesDB> activated, Action<IEntitiesDB> removed, object type, string name)
{
this.type = type;

View file

@ -22,12 +22,12 @@ namespace GamecraftModdingAPI
harmony.PatchAll(currentAssembly);
}
// create default event emitters
Manager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.ApplicationInitialized, "GamecraftModdingAPIApplicationInitializedEventEmitter", false));
Manager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Menu, "GamecraftModdingAPIMenuActivatedEventEmitter", false));
Manager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.MenuSwitchedTo, "GamecraftModdingAPIMenuSwitchedToEventEmitter", false)); // TODO
Manager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Game, "GamecraftModdingAPIGameActivatedEventEmitter", false));
Manager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameReloaded, "GamecraftModdingAPIGameReloadedEventEmitter", false)); // TODO
Manager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameSwitchedTo, "GamecraftModdingAPIGameSwitchedToEventEmitter", false)); // TODO
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.ApplicationInitialized, "GamecraftModdingAPIApplicationInitializedEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Menu, "GamecraftModdingAPIMenuActivatedEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.MenuSwitchedTo, "GamecraftModdingAPIMenuSwitchedToEventEmitter", false)); // TODO
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Game, "GamecraftModdingAPIGameActivatedEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameReloaded, "GamecraftModdingAPIGameReloadedEventEmitter", false)); // TODO
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameSwitchedTo, "GamecraftModdingAPIGameSwitchedToEventEmitter", false)); // TODO
Logging.Log($"{currentAssembly.GetName().Name} {currentAssembly.GetName().Version} init & patch complete");
}

View file

@ -1,12 +1,18 @@
using System;
using System.Reflection;
using Harmony;
using GamecraftModdingAPI.Commands;
using GamecraftModdingAPI.Events;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Tests
{
// unused by design
/// <summary>
/// Modding API implemented as a standalone IPA Plugin.
/// Ideally, GamecraftModdingAPI should be loaded by another mod; not itself
/// </summary>
public class GamecraftModdingAPIPluginTest
#if DEBUG
: IllusionPlugin.IEnhancedPlugin
@ -37,21 +43,27 @@ namespace GamecraftModdingAPI.Tests
//MinimumSpecsCheckPatch.ForcePassMinimumSpecCheck = true;
// debug/test handlers
Manager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("App Inited event!"); }, () => { },
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("App Inited event!"); }, () => { },
EventType.ApplicationInitialized, "appinit API debug"));
Manager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Menu Activated event!"); },
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Menu Activated event!"); },
() => { Logging.Log("Menu Destroyed event!"); },
EventType.Menu, "menuact API debug"));
Manager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Menu Switched To event!"); }, () => { },
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Menu Switched To event!"); }, () => { },
EventType.MenuSwitchedTo, "menuswitch API debug"));
Manager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Activated event!"); },
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Activated event!"); },
() => { Logging.Log("Game Destroyed event!"); },
EventType.Game, "gameact API debug"));
Manager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Reloaded event!"); }, () => { },
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Reloaded event!"); }, () => { },
EventType.GameReloaded, "gamerel API debug"));
Manager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Switched To event!"); }, () => { },
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Switched To event!"); }, () => { },
EventType.GameSwitchedTo, "gameswitch API debug"));
// debug/test commands
CommandManager.AddCommand(new SimpleCustomCommandEngine(() => { UnityEngine.Application.Quit(); },
"Exit", "Close Gamecraft without any prompts"));
CommandManager.AddCommand(new SimpleCustomCommandEngine<float>((float d) => { UnityEngine.Camera.main.fieldOfView = d; },
"SetFOV", "Set the player camera's field of view"));
}
public void OnFixedUpdate() { }

View file

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Reflection;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -158,14 +158,14 @@ namespace GamecraftModdingAPI.Utility
}
/// <summary>
/// Write a descriptive message to Gamecraft's log including the current time and the DLL's name
/// Write a descriptive message to Gamecraft's log including the current time and the calling method's name
/// </summary>
/// <param name="obj">The object to log</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MetaLog(object obj)
{
var callAsm = Assembly.GetCallingAssembly();
Log($"[{DateTime.Now.ToString()}][{callAsm.GetName().Name}]{obj.ToString()}");
var method = (new StackTrace()).GetFrame(1).GetMethod();
Log($"[{DateTime.Now.ToString()}][{method.DeclaringType.Name}.{method.Name}]{obj.ToString()}");
}
}
}

1
ref2
View file

@ -1 +0,0 @@
Z:/