diff --git a/GamecraftModdingAPI/Commands/CommandBuilder.cs b/GamecraftModdingAPI/Commands/CommandBuilder.cs
index d5f1b51..7bd5ed9 100644
--- a/GamecraftModdingAPI/Commands/CommandBuilder.cs
+++ b/GamecraftModdingAPI/Commands/CommandBuilder.cs
@@ -148,11 +148,11 @@ namespace GamecraftModdingAPI.Commands
{
if (string.IsNullOrWhiteSpace(name))
{
- throw new InvalidOperationException("Command name must be defined before FromExisting() is called");
+ throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
}
if (!ExistingCommands.Exists(name))
{
- throw new InvalidOperationException("Command cannot be built from existing because it does not exist.");
+ throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
}
fromExisting = true;
return new SimpleCustomCommandEngine(
@@ -170,11 +170,11 @@ namespace GamecraftModdingAPI.Commands
{
if (string.IsNullOrWhiteSpace(name))
{
- throw new InvalidOperationException("Command name must be defined before FromExisting() is called");
+ throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
}
if (!ExistingCommands.Exists(name))
{
- throw new InvalidOperationException("Command cannot be built from existing because it does not exist.");
+ throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
}
fromExisting = true;
return new SimpleCustomCommandEngine(
@@ -193,11 +193,11 @@ namespace GamecraftModdingAPI.Commands
{
if (string.IsNullOrWhiteSpace(name))
{
- throw new InvalidOperationException("Command name must be defined before FromExisting() is called");
+ throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
}
if (!ExistingCommands.Exists(name))
{
- throw new InvalidOperationException("Command cannot be built from existing because it does not exist.");
+ throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
}
fromExisting = true;
return new SimpleCustomCommandEngine(
@@ -217,11 +217,11 @@ namespace GamecraftModdingAPI.Commands
{
if (string.IsNullOrWhiteSpace(name))
{
- throw new InvalidOperationException("Command name must be defined before FromExisting() is called");
+ throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
}
if (!ExistingCommands.Exists(name))
{
- throw new InvalidOperationException("Command cannot be built from existing because it does not exist.");
+ throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
}
fromExisting = true;
return new SimpleCustomCommandEngine(
@@ -239,19 +239,19 @@ namespace GamecraftModdingAPI.Commands
{
if (string.IsNullOrWhiteSpace(name))
{
- throw new InvalidOperationException("Command name must be defined before Build() is called");
+ throw new CommandParameterMissingException("Command name must be defined before Build() is called");
}
if (fromExisting)
{
- throw new InvalidOperationException("Command was already built by FromExisting()");
+ throw new CommandAlreadyBuiltException("Command was already built by FromExisting()");
}
if (commandEngine == null)
{
- throw new InvalidOperationException("Command action must be defined before Build() is called");
+ throw new CommandParameterMissingException("Command action must be defined before Build() is called");
}
if (string.IsNullOrWhiteSpace(description))
{
- Logging.LogWarning($"Command {name} was built without a description");
+ Logging.LogWarning($"Command {FullName()} was built without a description");
}
if (register)
{
diff --git a/GamecraftModdingAPI/Commands/CommandExceptions.cs b/GamecraftModdingAPI/Commands/CommandExceptions.cs
new file mode 100644
index 0000000..39502a7
--- /dev/null
+++ b/GamecraftModdingAPI/Commands/CommandExceptions.cs
@@ -0,0 +1,71 @@
+using System;
+namespace GamecraftModdingAPI.Commands
+{
+ public class CommandException : GamecraftModdingAPIException
+ {
+ public CommandException() : base() {}
+
+ public CommandException(string msg) : base(msg) {}
+
+ public CommandException(string msg, Exception innerException) : base(msg, innerException) {}
+ }
+
+ public class CommandNotFoundException : CommandException
+ {
+ public CommandNotFoundException()
+ {
+ }
+
+ public CommandNotFoundException(string msg) : base(msg)
+ {
+ }
+ }
+
+ public class CommandAlreadyExistsException : CommandException
+ {
+ public CommandAlreadyExistsException()
+ {
+ }
+
+ public CommandAlreadyExistsException(string msg) : base(msg)
+ {
+ }
+ }
+
+ public class CommandRuntimeException : CommandException
+ {
+ public CommandRuntimeException()
+ {
+ }
+
+ public CommandRuntimeException(string msg) : base(msg)
+ {
+ }
+
+ public CommandRuntimeException(string msg, Exception innerException) : base(msg, innerException)
+ {
+ }
+ }
+
+ public class CommandAlreadyBuiltException : CommandException
+ {
+ public CommandAlreadyBuiltException()
+ {
+ }
+
+ public CommandAlreadyBuiltException(string msg) : base(msg)
+ {
+ }
+ }
+
+ public class CommandParameterMissingException : CommandException
+ {
+ public CommandParameterMissingException()
+ {
+ }
+
+ public CommandParameterMissingException(string msg) : base(msg)
+ {
+ }
+ }
+}
diff --git a/GamecraftModdingAPI/Commands/CommandManager.cs b/GamecraftModdingAPI/Commands/CommandManager.cs
index 986405e..876d634 100644
--- a/GamecraftModdingAPI/Commands/CommandManager.cs
+++ b/GamecraftModdingAPI/Commands/CommandManager.cs
@@ -24,7 +24,7 @@ namespace GamecraftModdingAPI.Commands
{
if (ExistsCommand(engine))
{
- Logging.LogWarning($"Command {engine.Name} already exists!");
+ throw new CommandAlreadyExistsException($"Command {engine.Name} already exists");
}
_customCommands[engine.Name] = engine;
if (_lastEngineRoot != null)
diff --git a/GamecraftModdingAPI/Commands/ICustomCommandEngine.cs b/GamecraftModdingAPI/Commands/ICustomCommandEngine.cs
index aafd257..1704e84 100644
--- a/GamecraftModdingAPI/Commands/ICustomCommandEngine.cs
+++ b/GamecraftModdingAPI/Commands/ICustomCommandEngine.cs
@@ -12,7 +12,9 @@ using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Commands
{
///
- /// Engine interface to handle command operations
+ /// Engine interface to handle command operations.
+ /// If you are using implementing this yourself, you must manually register the command.
+ /// See SimpleCustomCommandEngine's Ready() and Dispose() methods for an example of command registration.
///
public interface ICustomCommandEngine : IApiEngine
{
diff --git a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine.cs b/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine.cs
index ebf0758..2ce60a9 100644
--- a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine.cs
+++ b/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine.cs
@@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS;
+using GamecraftModdingAPI.Utility;
+
namespace GamecraftModdingAPI.Commands
{
///
@@ -42,7 +44,7 @@ namespace GamecraftModdingAPI.Commands
public void Ready()
{
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
- CommandRegistrationHelper.Register(this.Name, this.runCommand, this.Description);
+ CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description);
}
///
@@ -62,5 +64,19 @@ namespace GamecraftModdingAPI.Commands
{
runCommand();
}
+
+ private void InvokeCatchError()
+ {
+ try
+ {
+ runCommand();
+ }
+ catch (Exception e)
+ {
+ CommandRuntimeException wrappedException = new CommandRuntimeException($"Command {Name} threw an exception when executed", e);
+ Logging.LogWarning(wrappedException.ToString());
+ Logging.CommandLogError(wrappedException.ToString());
+ }
+ }
}
}
diff --git a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine1.cs b/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine1.cs
index a8dd96b..bac16ef 100644
--- a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine1.cs
+++ b/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine1.cs
@@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS;
+using GamecraftModdingAPI.Utility;
+
namespace GamecraftModdingAPI.Commands
{
///
@@ -33,7 +35,7 @@ namespace GamecraftModdingAPI.Commands
public void Ready()
{
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
- CommandRegistrationHelper.Register(this.Name, this.runCommand, this.Description);
+ CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description);
}
///
@@ -52,6 +54,20 @@ namespace GamecraftModdingAPI.Commands
public void Invoke(A a)
{
runCommand(a);
+ }
+
+ private void InvokeCatchError(A a)
+ {
+ try
+ {
+ runCommand(a);
+ }
+ catch (Exception e)
+ {
+ CommandRuntimeException wrappedException = new CommandRuntimeException($"Command {Name} threw an exception when executed", e);
+ Logging.LogWarning(wrappedException.ToString());
+ Logging.CommandLogError(wrappedException.ToString());
+ }
}
}
}
diff --git a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine2.cs b/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine2.cs
index 8333cd2..4b2b38d 100644
--- a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine2.cs
+++ b/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine2.cs
@@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS;
+using GamecraftModdingAPI.Utility;
+
namespace GamecraftModdingAPI.Commands
{
///
@@ -33,7 +35,7 @@ namespace GamecraftModdingAPI.Commands
public void Ready()
{
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
- CommandRegistrationHelper.Register(this.Name, this.runCommand, this.Description);
+ CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description);
}
///
@@ -52,6 +54,20 @@ namespace GamecraftModdingAPI.Commands
public void Invoke(A a, B b)
{
runCommand(a, b);
+ }
+
+ private void InvokeCatchError(A a, B b)
+ {
+ try
+ {
+ runCommand(a, b);
+ }
+ catch (Exception e)
+ {
+ CommandRuntimeException wrappedException = new CommandRuntimeException($"Command {Name} threw an exception when executed", e);
+ Logging.LogWarning(wrappedException.ToString());
+ Logging.CommandLogError(wrappedException.ToString());
+ }
}
}
}
diff --git a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine3.cs b/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine3.cs
index e5570c3..c9c942e 100644
--- a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine3.cs
+++ b/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine3.cs
@@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS;
+using GamecraftModdingAPI.Utility;
+
namespace GamecraftModdingAPI.Commands
{
///
@@ -33,7 +35,7 @@ namespace GamecraftModdingAPI.Commands
public void Ready()
{
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
- CommandRegistrationHelper.Register(this.Name, this.runCommand, this.Description);
+ CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description);
}
///
@@ -52,6 +54,20 @@ namespace GamecraftModdingAPI.Commands
public void Invoke(A a, B b, C c)
{
runCommand(a, b, c);
+ }
+
+ private void InvokeCatchError(A a, B b, C c)
+ {
+ try
+ {
+ runCommand(a, b, c);
+ }
+ catch (Exception e)
+ {
+ CommandRuntimeException wrappedException = new CommandRuntimeException($"Command {Name} threw an exception when executed", e);
+ Logging.LogWarning(wrappedException.ToString());
+ Logging.CommandLogError(wrappedException.ToString());
+ }
}
}
}
diff --git a/GamecraftModdingAPI/Events/EventExceptions.cs b/GamecraftModdingAPI/Events/EventExceptions.cs
new file mode 100644
index 0000000..ee29480
--- /dev/null
+++ b/GamecraftModdingAPI/Events/EventExceptions.cs
@@ -0,0 +1,55 @@
+using System;
+namespace GamecraftModdingAPI.Events
+{
+ public class EventException : GamecraftModdingAPIException
+ {
+ public EventException()
+ {
+ }
+
+ public EventException(string message) : base(message)
+ {
+ }
+
+ public EventException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+ }
+
+ public class EventNotFoundException : EventException
+ {
+ public EventNotFoundException()
+ {
+ }
+
+ public EventNotFoundException(string message) : base(message)
+ {
+ }
+ }
+
+ public class EventAlreadyExistsException : EventException
+ {
+ public EventAlreadyExistsException()
+ {
+ }
+
+ public EventAlreadyExistsException(string message) : base(message)
+ {
+ }
+ }
+
+ public class EventRuntimeException : EventException
+ {
+ public EventRuntimeException()
+ {
+ }
+
+ public EventRuntimeException(string message) : base(message)
+ {
+ }
+
+ public EventRuntimeException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+ }
+}
diff --git a/GamecraftModdingAPI/Events/EventManager.cs b/GamecraftModdingAPI/Events/EventManager.cs
index 227c7fd..efbf377 100644
--- a/GamecraftModdingAPI/Events/EventManager.cs
+++ b/GamecraftModdingAPI/Events/EventManager.cs
@@ -26,6 +26,10 @@ namespace GamecraftModdingAPI.Events
public static void AddEventHandler(IEventHandlerEngine engine)
{
+ if (ExistsEventHandler(engine))
+ {
+ throw new EventAlreadyExistsException($"IEventHandlerEngine {engine.Name} already exists");
+ }
_eventHandlers[engine.Name] = engine;
if (_lastEngineRoot != null)
{
@@ -63,6 +67,10 @@ namespace GamecraftModdingAPI.Events
public static void AddEventEmitter(IEventEmitterEngine engine)
{
+ if (ExistsEventEmitter(engine))
+ {
+ throw new EventAlreadyExistsException($"IEventEmitterEngine {engine.Name} already exists");
+ }
_eventEmitters[engine.Name] = engine;
if (_lastEngineRoot != null)
{
diff --git a/GamecraftModdingAPI/Events/SimpleEventHandlerEngine.cs b/GamecraftModdingAPI/Events/SimpleEventHandlerEngine.cs
index b656273..133ad29 100644
--- a/GamecraftModdingAPI/Events/SimpleEventHandlerEngine.cs
+++ b/GamecraftModdingAPI/Events/SimpleEventHandlerEngine.cs
@@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS;
+using GamecraftModdingAPI.Utility;
+
namespace GamecraftModdingAPI.Events
{
///
@@ -31,7 +33,7 @@ namespace GamecraftModdingAPI.Events
if (entityView.type.Equals(this.type))
{
isActivated = true;
- onActivated.Invoke(entitiesDB);
+ onActivatedInvokeCatchError(entitiesDB);
}
}
@@ -43,6 +45,10 @@ namespace GamecraftModdingAPI.Events
public void Activate(bool handle = false)
{
isActivated = true;
+ if (handle && entitiesDB != null)
+ {
+ onActivatedInvokeCatchError(entitiesDB);
+ }
}
public void Ready() { }
@@ -52,7 +58,7 @@ namespace GamecraftModdingAPI.Events
if (entityView.type.Equals(this.type) && isActivated)
{
isActivated = false;
- onDestroyed.Invoke(entitiesDB);
+ onDestroyedInvokeCatchError(entitiesDB);
}
}
@@ -61,7 +67,7 @@ namespace GamecraftModdingAPI.Events
if (isActivated)
{
isActivated = false;
- onDestroyed.Invoke(entitiesDB);
+ onDestroyedInvokeCatchError(entitiesDB);
}
}
@@ -91,6 +97,32 @@ namespace GamecraftModdingAPI.Events
this.onDestroyed = removed;
}
+ private void onActivatedInvokeCatchError(EntitiesDB _entitiesDB)
+ {
+ try
+ {
+ onActivated.Invoke(_entitiesDB);
+ }
+ catch (Exception e)
+ {
+ EventRuntimeException wrappedException = new EventRuntimeException($"EventHandler {Name} threw an exception when activated", e);
+ Logging.LogWarning(wrappedException.ToString());
+ }
+ }
+
+ private void onDestroyedInvokeCatchError(EntitiesDB _entitiesDB)
+ {
+ try
+ {
+ onDestroyed.Invoke(_entitiesDB);
+ }
+ catch (Exception e)
+ {
+ EventRuntimeException wrappedException = new EventRuntimeException($"EventHandler {Name} threw an exception when destroyed", e);
+ Logging.LogWarning(wrappedException.ToString());
+ }
+ }
+
public SimpleEventHandlerEngine(Action activated, Action removed, EventType type, string name, bool simple = true)
: this((EntitiesDB _) => { activated.Invoke(); }, (EntitiesDB _) => { removed.Invoke(); }, (int)type, name) { }
diff --git a/GamecraftModdingAPI/GamecraftModdingAPIException.cs b/GamecraftModdingAPI/GamecraftModdingAPIException.cs
new file mode 100644
index 0000000..bc944aa
--- /dev/null
+++ b/GamecraftModdingAPI/GamecraftModdingAPIException.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace GamecraftModdingAPI
+{
+ public class GamecraftModdingAPIException : Exception
+ {
+ public GamecraftModdingAPIException()
+ {
+ }
+
+ public GamecraftModdingAPIException(string message) : base(message)
+ {
+ }
+
+ public GamecraftModdingAPIException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ protected GamecraftModdingAPIException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/GamecraftModdingAPI/Player.cs b/GamecraftModdingAPI/Player.cs
index 29546da..965ba45 100644
--- a/GamecraftModdingAPI/Player.cs
+++ b/GamecraftModdingAPI/Player.cs
@@ -50,7 +50,7 @@ namespace GamecraftModdingAPI
this.Id = id;
if (!Exists(id))
{
- throw new InvalidOperationException($"No player with id {id} exists");
+ throw new PlayerNotFoundException($"No player with id {id} exists");
}
this.Type = playerEngine.GetLocalPlayer() == id ? PlayerType.Local : PlayerType.Remote;
}
@@ -73,7 +73,7 @@ namespace GamecraftModdingAPI
}
if (this.Id == uint.MaxValue)
{
- throw new InvalidOperationException($"No player of {player} type exists");
+ throw new PlayerNotFoundException($"No player of {player} type exists");
}
this.Type = player;
}
diff --git a/GamecraftModdingAPI/Players/PlayerExceptions.cs b/GamecraftModdingAPI/Players/PlayerExceptions.cs
new file mode 100644
index 0000000..94312b9
--- /dev/null
+++ b/GamecraftModdingAPI/Players/PlayerExceptions.cs
@@ -0,0 +1,29 @@
+using System;
+namespace GamecraftModdingAPI.Players
+{
+ public class PlayerException : GamecraftModdingAPIException
+ {
+ public PlayerException()
+ {
+ }
+
+ public PlayerException(string message) : base(message)
+ {
+ }
+
+ public PlayerException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+ }
+
+ public class PlayerNotFoundException : PlayerException
+ {
+ public PlayerNotFoundException()
+ {
+ }
+
+ public PlayerNotFoundException(string message) : base(message)
+ {
+ }
+ }
+}
diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
index c241159..183b4f0 100644
--- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
+++ b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
@@ -25,7 +25,8 @@ namespace GamecraftModdingAPI.Tests
#if DEBUG
: IllusionPlugin.IEnhancedPlugin
#endif
- {
+ {
+
private static Harmony harmony { get; set; }
public string[] Filter { get; } = new string[] { "Gamecraft", "GamecraftPreview" };
@@ -80,6 +81,9 @@ namespace GamecraftModdingAPI.Tests
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Mode Build Switched To event!"); }, () => { },
EventType.BuildSwitchedTo, "buildswitch API debug"));
+ EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { throw new Exception(""); }, () => {},
+ EventType.Menu, "menu activated API error thrower test"));
+
// debug/test commands
if (Dependency.Hell("ExtraCommands"))
{
@@ -148,6 +152,10 @@ namespace GamecraftModdingAPI.Tests
.Action(() => new Player(Players.PlayerType.Local).GetBlockLookedAt().Type = BlockIDs.AluminiumCube)
.Build();
+ CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.")
+ .Action(() => { throw new Exception("Error Command always throws an error"); })
+ .Build();
+
/*
CommandManager.AddCommand(new SimpleCustomCommandEngine((float d) => { UnityEngine.Camera.main.fieldOfView = d; },
"SetFOV", "Set the player camera's field of view"));