Create custom error types and error catching

This commit is contained in:
NGnius (Graham) 2020-05-13 16:52:06 -04:00
parent 6f8241554d
commit 9cb6917d28
15 changed files with 317 additions and 24 deletions

View file

@ -148,11 +148,11 @@ namespace GamecraftModdingAPI.Commands
{ {
if (string.IsNullOrWhiteSpace(name)) 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)) 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; fromExisting = true;
return new SimpleCustomCommandEngine( return new SimpleCustomCommandEngine(
@ -170,11 +170,11 @@ namespace GamecraftModdingAPI.Commands
{ {
if (string.IsNullOrWhiteSpace(name)) 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)) 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; fromExisting = true;
return new SimpleCustomCommandEngine<A>( return new SimpleCustomCommandEngine<A>(
@ -193,11 +193,11 @@ namespace GamecraftModdingAPI.Commands
{ {
if (string.IsNullOrWhiteSpace(name)) 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)) 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; fromExisting = true;
return new SimpleCustomCommandEngine<A,B>( return new SimpleCustomCommandEngine<A,B>(
@ -217,11 +217,11 @@ namespace GamecraftModdingAPI.Commands
{ {
if (string.IsNullOrWhiteSpace(name)) 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)) 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; fromExisting = true;
return new SimpleCustomCommandEngine<A,B,C>( return new SimpleCustomCommandEngine<A,B,C>(
@ -239,19 +239,19 @@ namespace GamecraftModdingAPI.Commands
{ {
if (string.IsNullOrWhiteSpace(name)) 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) if (fromExisting)
{ {
throw new InvalidOperationException("Command was already built by FromExisting()"); throw new CommandAlreadyBuiltException("Command was already built by FromExisting()");
} }
if (commandEngine == null) 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)) if (string.IsNullOrWhiteSpace(description))
{ {
Logging.LogWarning($"Command {name} was built without a description"); Logging.LogWarning($"Command {FullName()} was built without a description");
} }
if (register) if (register)
{ {

View file

@ -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)
{
}
}
}

View file

@ -24,7 +24,7 @@ namespace GamecraftModdingAPI.Commands
{ {
if (ExistsCommand(engine)) if (ExistsCommand(engine))
{ {
Logging.LogWarning($"Command {engine.Name} already exists!"); throw new CommandAlreadyExistsException($"Command {engine.Name} already exists");
} }
_customCommands[engine.Name] = engine; _customCommands[engine.Name] = engine;
if (_lastEngineRoot != null) if (_lastEngineRoot != null)

View file

@ -12,7 +12,9 @@ using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Commands namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
public interface ICustomCommandEngine : IApiEngine public interface ICustomCommandEngine : IApiEngine
{ {

View file

@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
@ -42,7 +44,7 @@ namespace GamecraftModdingAPI.Commands
public void Ready() public void Ready()
{ {
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Register(this.Name, this.runCommand, this.Description); CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description);
} }
/// <summary> /// <summary>
@ -62,5 +64,19 @@ namespace GamecraftModdingAPI.Commands
{ {
runCommand(); 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());
}
}
} }
} }

View file

@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
@ -33,7 +35,7 @@ namespace GamecraftModdingAPI.Commands
public void Ready() public void Ready()
{ {
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Register<A>(this.Name, this.runCommand, this.Description); CommandRegistrationHelper.Register<A>(this.Name, this.InvokeCatchError, this.Description);
} }
/// <summary> /// <summary>
@ -52,6 +54,20 @@ namespace GamecraftModdingAPI.Commands
public void Invoke(A a) public void Invoke(A a)
{ {
runCommand(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());
}
} }
} }
} }

View file

@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
@ -33,7 +35,7 @@ namespace GamecraftModdingAPI.Commands
public void Ready() public void Ready()
{ {
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Register<A,B>(this.Name, this.runCommand, this.Description); CommandRegistrationHelper.Register<A,B>(this.Name, this.InvokeCatchError, this.Description);
} }
/// <summary> /// <summary>
@ -52,6 +54,20 @@ namespace GamecraftModdingAPI.Commands
public void Invoke(A a, B b) public void Invoke(A a, B b)
{ {
runCommand(a, 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());
}
} }
} }
} }

View file

@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
@ -33,7 +35,7 @@ namespace GamecraftModdingAPI.Commands
public void Ready() public void Ready()
{ {
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Register<A,B,C>(this.Name, this.runCommand, this.Description); CommandRegistrationHelper.Register<A,B,C>(this.Name, this.InvokeCatchError, this.Description);
} }
/// <summary> /// <summary>
@ -52,6 +54,20 @@ namespace GamecraftModdingAPI.Commands
public void Invoke(A a, B b, C c) public void Invoke(A a, B b, C c)
{ {
runCommand(a, b, 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());
}
} }
} }
} }

View file

@ -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)
{
}
}
}

View file

@ -26,6 +26,10 @@ namespace GamecraftModdingAPI.Events
public static void AddEventHandler(IEventHandlerEngine engine) public static void AddEventHandler(IEventHandlerEngine engine)
{ {
if (ExistsEventHandler(engine))
{
throw new EventAlreadyExistsException($"IEventHandlerEngine {engine.Name} already exists");
}
_eventHandlers[engine.Name] = engine; _eventHandlers[engine.Name] = engine;
if (_lastEngineRoot != null) if (_lastEngineRoot != null)
{ {
@ -63,6 +67,10 @@ namespace GamecraftModdingAPI.Events
public static void AddEventEmitter(IEventEmitterEngine engine) public static void AddEventEmitter(IEventEmitterEngine engine)
{ {
if (ExistsEventEmitter(engine))
{
throw new EventAlreadyExistsException($"IEventEmitterEngine {engine.Name} already exists");
}
_eventEmitters[engine.Name] = engine; _eventEmitters[engine.Name] = engine;
if (_lastEngineRoot != null) if (_lastEngineRoot != null)
{ {

View file

@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events namespace GamecraftModdingAPI.Events
{ {
/// <summary> /// <summary>
@ -31,7 +33,7 @@ namespace GamecraftModdingAPI.Events
if (entityView.type.Equals(this.type)) if (entityView.type.Equals(this.type))
{ {
isActivated = true; isActivated = true;
onActivated.Invoke(entitiesDB); onActivatedInvokeCatchError(entitiesDB);
} }
} }
@ -43,6 +45,10 @@ namespace GamecraftModdingAPI.Events
public void Activate(bool handle = false) public void Activate(bool handle = false)
{ {
isActivated = true; isActivated = true;
if (handle && entitiesDB != null)
{
onActivatedInvokeCatchError(entitiesDB);
}
} }
public void Ready() { } public void Ready() { }
@ -52,7 +58,7 @@ namespace GamecraftModdingAPI.Events
if (entityView.type.Equals(this.type) && isActivated) if (entityView.type.Equals(this.type) && isActivated)
{ {
isActivated = false; isActivated = false;
onDestroyed.Invoke(entitiesDB); onDestroyedInvokeCatchError(entitiesDB);
} }
} }
@ -61,7 +67,7 @@ namespace GamecraftModdingAPI.Events
if (isActivated) if (isActivated)
{ {
isActivated = false; isActivated = false;
onDestroyed.Invoke(entitiesDB); onDestroyedInvokeCatchError(entitiesDB);
} }
} }
@ -91,6 +97,32 @@ namespace GamecraftModdingAPI.Events
this.onDestroyed = removed; 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) public SimpleEventHandlerEngine(Action activated, Action removed, EventType type, string name, bool simple = true)
: this((EntitiesDB _) => { activated.Invoke(); }, (EntitiesDB _) => { removed.Invoke(); }, (int)type, name) { } : this((EntitiesDB _) => { activated.Invoke(); }, (EntitiesDB _) => { removed.Invoke(); }, (int)type, name) { }

View file

@ -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)
{
}
}
}

View file

@ -50,7 +50,7 @@ namespace GamecraftModdingAPI
this.Id = id; this.Id = id;
if (!Exists(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; this.Type = playerEngine.GetLocalPlayer() == id ? PlayerType.Local : PlayerType.Remote;
} }
@ -73,7 +73,7 @@ namespace GamecraftModdingAPI
} }
if (this.Id == uint.MaxValue) 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; this.Type = player;
} }

View file

@ -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)
{
}
}
}

View file

@ -25,7 +25,8 @@ namespace GamecraftModdingAPI.Tests
#if DEBUG #if DEBUG
: IllusionPlugin.IEnhancedPlugin : IllusionPlugin.IEnhancedPlugin
#endif #endif
{ {
private static Harmony harmony { get; set; } private static Harmony harmony { get; set; }
public string[] Filter { get; } = new string[] { "Gamecraft", "GamecraftPreview" }; 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!"); }, () => { }, EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Mode Build Switched To event!"); }, () => { },
EventType.BuildSwitchedTo, "buildswitch API debug")); EventType.BuildSwitchedTo, "buildswitch API debug"));
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { throw new Exception(""); }, () => {},
EventType.Menu, "menu activated API error thrower test"));
// debug/test commands // debug/test commands
if (Dependency.Hell("ExtraCommands")) if (Dependency.Hell("ExtraCommands"))
{ {
@ -148,6 +152,10 @@ namespace GamecraftModdingAPI.Tests
.Action(() => new Player(Players.PlayerType.Local).GetBlockLookedAt().Type = BlockIDs.AluminiumCube) .Action(() => new Player(Players.PlayerType.Local).GetBlockLookedAt().Type = BlockIDs.AluminiumCube)
.Build(); .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>((float d) => { UnityEngine.Camera.main.fieldOfView = d; }, CommandManager.AddCommand(new SimpleCustomCommandEngine<float>((float d) => { UnityEngine.Camera.main.fieldOfView = d; },
"SetFOV", "Set the player camera's field of view")); "SetFOV", "Set the player camera's field of view"));