Add wrapped event handler, using the existing ECS object instances
- Added a wrapper class that handles the individual wrapping of event handlers to individually handle exceptions - now it tracks the wrapped event handlers so it can unregister them properly - Fixed an exception that happened when two ECS objects were created with the same EGID - Added support for returning an existing ECS object if it exists instead of always creating a new one - Added a parameter to the entity query extension methods to override the group of the ECS object (could be used for the player properties)
This commit is contained in:
parent
8a03277d84
commit
4bd636b8ed
15 changed files with 164 additions and 115 deletions
|
@ -9,9 +9,9 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
public class AppEngine : IFactoryEngine
|
public class AppEngine : IFactoryEngine
|
||||||
{
|
{
|
||||||
public event EventHandler<MenuEventArgs> EnterMenu;
|
public WrappedHandler<MenuEventArgs> EnterMenu;
|
||||||
|
|
||||||
public event EventHandler<MenuEventArgs> ExitMenu;
|
public WrappedHandler<MenuEventArgs> ExitMenu;
|
||||||
|
|
||||||
public IEntityFactory Factory { set; private get; }
|
public IEntityFactory Factory { set; private get; }
|
||||||
|
|
||||||
|
@ -24,13 +24,13 @@ namespace TechbloxModdingAPI.App
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
IsInMenu = false;
|
IsInMenu = false;
|
||||||
ExceptionUtil.InvokeEvent(ExitMenu, this, new MenuEventArgs { });
|
ExitMenu.Invoke(this, new MenuEventArgs { });
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Ready()
|
public void Ready()
|
||||||
{
|
{
|
||||||
IsInMenu = true;
|
IsInMenu = true;
|
||||||
ExceptionUtil.InvokeEvent(EnterMenu, this, new MenuEventArgs { });
|
EnterMenu.Invoke(this, new MenuEventArgs { });
|
||||||
}
|
}
|
||||||
|
|
||||||
// app functionality
|
// app functionality
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace TechbloxModdingAPI.App
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<MenuEventArgs> EnterMenu
|
public static event EventHandler<MenuEventArgs> EnterMenu
|
||||||
{
|
{
|
||||||
add => appEngine.EnterMenu += ExceptionUtil.WrapHandler(value);
|
add => appEngine.EnterMenu += value;
|
||||||
remove => appEngine.EnterMenu -= value;
|
remove => appEngine.EnterMenu -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ namespace TechbloxModdingAPI.App
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<MenuEventArgs> ExitMenu
|
public static event EventHandler<MenuEventArgs> ExitMenu
|
||||||
{
|
{
|
||||||
add => appEngine.ExitMenu += ExceptionUtil.WrapHandler(value);
|
add => appEngine.ExitMenu += value;
|
||||||
remove => appEngine.ExitMenu -= value;
|
remove => appEngine.ExitMenu -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ namespace TechbloxModdingAPI.App
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<GameEventArgs> Simulate
|
public static event EventHandler<GameEventArgs> Simulate
|
||||||
{
|
{
|
||||||
add => buildSimEventEngine.SimulationMode += ExceptionUtil.WrapHandler(value);
|
add => buildSimEventEngine.SimulationMode += value;
|
||||||
remove => buildSimEventEngine.SimulationMode -= value;
|
remove => buildSimEventEngine.SimulationMode -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ namespace TechbloxModdingAPI.App
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<GameEventArgs> Edit
|
public static event EventHandler<GameEventArgs> Edit
|
||||||
{
|
{
|
||||||
add => buildSimEventEngine.BuildMode += ExceptionUtil.WrapHandler(value);
|
add => buildSimEventEngine.BuildMode += value;
|
||||||
remove => buildSimEventEngine.BuildMode -= value;
|
remove => buildSimEventEngine.BuildMode -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ namespace TechbloxModdingAPI.App
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<GameEventArgs> Enter
|
public static event EventHandler<GameEventArgs> Enter
|
||||||
{
|
{
|
||||||
add => gameEngine.EnterGame += ExceptionUtil.WrapHandler(value);
|
add => gameEngine.EnterGame += value;
|
||||||
remove => gameEngine.EnterGame -= value;
|
remove => gameEngine.EnterGame -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ namespace TechbloxModdingAPI.App
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<GameEventArgs> Exit
|
public static event EventHandler<GameEventArgs> Exit
|
||||||
{
|
{
|
||||||
add => gameEngine.ExitGame += ExceptionUtil.WrapHandler(value);
|
add => gameEngine.ExitGame += value;
|
||||||
remove => gameEngine.ExitGame -= value;
|
remove => gameEngine.ExitGame -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
public class GameBuildSimEventEngine : IApiEngine, IUnorderedInitializeOnTimeRunningModeEntered, IUnorderedInitializeOnTimeStoppedModeEntered
|
public class GameBuildSimEventEngine : IApiEngine, IUnorderedInitializeOnTimeRunningModeEntered, IUnorderedInitializeOnTimeStoppedModeEntered
|
||||||
{
|
{
|
||||||
public event EventHandler<GameEventArgs> SimulationMode;
|
public WrappedHandler<GameEventArgs> SimulationMode;
|
||||||
|
|
||||||
public event EventHandler<GameEventArgs> BuildMode;
|
public WrappedHandler<GameEventArgs> BuildMode;
|
||||||
|
|
||||||
public string Name => "TechbloxModdingAPIBuildSimEventGameEngine";
|
public string Name => "TechbloxModdingAPIBuildSimEventGameEngine";
|
||||||
|
|
||||||
|
@ -27,13 +27,13 @@ namespace TechbloxModdingAPI.App
|
||||||
|
|
||||||
public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps)
|
public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps)
|
||||||
{
|
{
|
||||||
ExceptionUtil.InvokeEvent(SimulationMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
SimulationMode.Invoke(this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||||
return inputDeps;
|
return inputDeps;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps)
|
public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps)
|
||||||
{
|
{
|
||||||
ExceptionUtil.InvokeEvent(BuildMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
BuildMode.Invoke(this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||||
return inputDeps;
|
return inputDeps;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
public class GameGameEngine : IApiEngine
|
public class GameGameEngine : IApiEngine
|
||||||
{
|
{
|
||||||
public event EventHandler<GameEventArgs> EnterGame;
|
public WrappedHandler<GameEventArgs> EnterGame;
|
||||||
|
|
||||||
public event EventHandler<GameEventArgs> ExitGame;
|
public WrappedHandler<GameEventArgs> ExitGame;
|
||||||
|
|
||||||
public string Name => "TechbloxModdingAPIGameInfoMenuEngine";
|
public string Name => "TechbloxModdingAPIGameInfoMenuEngine";
|
||||||
|
|
||||||
|
@ -29,13 +29,13 @@ namespace TechbloxModdingAPI.App
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
ExceptionUtil.InvokeEvent(ExitGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
ExitGame.Invoke(this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||||
IsInGame = false;
|
IsInGame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Ready()
|
public void Ready()
|
||||||
{
|
{
|
||||||
ExceptionUtil.InvokeEvent(EnterGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
EnterGame.Invoke(this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||||
IsInGame = true;
|
IsInGame = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,8 +78,8 @@ namespace TechbloxModdingAPI
|
||||||
/// An event that fires each time a block is placed.
|
/// An event that fires each time a block is placed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<BlockPlacedRemovedEventArgs> Placed
|
public static event EventHandler<BlockPlacedRemovedEventArgs> Placed
|
||||||
{
|
{ //TODO: Rename and add instance version in 3.0
|
||||||
add => BlockEventsEngine.Placed += ExceptionUtil.WrapHandler(value);
|
add => BlockEventsEngine.Placed += value;
|
||||||
remove => BlockEventsEngine.Placed -= value;
|
remove => BlockEventsEngine.Placed -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<BlockPlacedRemovedEventArgs> Removed
|
public static event EventHandler<BlockPlacedRemovedEventArgs> Removed
|
||||||
{
|
{
|
||||||
add => BlockEventsEngine.Removed += ExceptionUtil.WrapHandler(value);
|
add => BlockEventsEngine.Removed += value;
|
||||||
remove => BlockEventsEngine.Removed -= value;
|
remove => BlockEventsEngine.Removed -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,13 +105,25 @@ namespace TechbloxModdingAPI
|
||||||
{CommonExclusiveGroups.WHEELRIG_BLOCK_BUILD_GROUP, (id => new WheelRig(id), typeof(WheelRig))}
|
{CommonExclusiveGroups.WHEELRIG_BLOCK_BUILD_GROUP, (id => new WheelRig(id), typeof(WheelRig))}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a correctly typed instance of this block. The instances are shared for a specific block.
|
||||||
|
/// If an instance is no longer referenced a new instance is returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="egid">The EGID of the block</param>
|
||||||
|
/// <param name="signaling">Whether the block is definitely a signaling block</param>
|
||||||
|
/// <returns></returns>
|
||||||
internal static Block New(EGID egid, bool signaling = false)
|
internal static Block New(EGID egid, bool signaling = false)
|
||||||
{
|
{
|
||||||
return GroupToConstructor.ContainsKey(egid.groupID)
|
if (egid == default) return null;
|
||||||
? GroupToConstructor[egid.groupID].Constructor(egid)
|
if (GroupToConstructor.ContainsKey(egid.groupID))
|
||||||
: signaling
|
{
|
||||||
? new SignalingBlock(egid)
|
var (constructor, type) = GroupToConstructor[egid.groupID];
|
||||||
: new Block(egid);
|
return GetInstance(egid, constructor, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return signaling
|
||||||
|
? GetInstance(egid, e => new SignalingBlock(e))
|
||||||
|
: GetInstance(egid, e => new Block(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block(EGID id)
|
public Block(EGID id)
|
||||||
|
|
|
@ -10,8 +10,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
public class BlockEventsEngine : IReactionaryEngine<BlockTagEntityStruct>
|
public class BlockEventsEngine : IReactionaryEngine<BlockTagEntityStruct>
|
||||||
{
|
{
|
||||||
public event EventHandler<BlockPlacedRemovedEventArgs> Placed;
|
public WrappedHandler<BlockPlacedRemovedEventArgs> Placed;
|
||||||
public event EventHandler<BlockPlacedRemovedEventArgs> Removed;
|
public WrappedHandler<BlockPlacedRemovedEventArgs> Removed;
|
||||||
|
|
||||||
public void Ready()
|
public void Ready()
|
||||||
{
|
{
|
||||||
|
@ -31,16 +31,14 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
if (!(shouldAddRemove = !shouldAddRemove))
|
if (!(shouldAddRemove = !shouldAddRemove))
|
||||||
return;
|
return;
|
||||||
ExceptionUtil.InvokeEvent(Placed, this,
|
Placed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid});
|
||||||
new BlockPlacedRemovedEventArgs {ID = egid});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(ref BlockTagEntityStruct entityComponent, EGID egid)
|
public void Remove(ref BlockTagEntityStruct entityComponent, EGID egid)
|
||||||
{
|
{
|
||||||
if (!(shouldAddRemove = !shouldAddRemove))
|
if (!(shouldAddRemove = !shouldAddRemove))
|
||||||
return;
|
return;
|
||||||
ExceptionUtil.InvokeEvent(Removed, this,
|
Removed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid});
|
||||||
new BlockPlacedRemovedEventArgs {ID = egid});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +47,6 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
public EGID ID;
|
public EGID ID;
|
||||||
private Block block;
|
private Block block;
|
||||||
|
|
||||||
public Block Block => block ?? (block = Block.New(ID));
|
public Block Block => block ??= Block.New(ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,7 +8,7 @@ using TechbloxModdingAPI.Blocks.Engines;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks
|
namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public class Wire
|
public class Wire : EcsObjectBase
|
||||||
{
|
{
|
||||||
internal static SignalEngine signalEngine;
|
internal static SignalEngine signalEngine;
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The wire's in-game id.
|
/// The wire's in-game id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EGID Id
|
public override EGID Id
|
||||||
{
|
{
|
||||||
get => wireEGID;
|
get => wireEGID;
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,7 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
/// <returns>A copy of the wire object.</returns>
|
/// <returns>A copy of the wire object.</returns>
|
||||||
public Wire OutputToInputCopy()
|
public Wire OutputToInputCopy()
|
||||||
{
|
{
|
||||||
return new Wire(wireEGID);
|
return GetInstance(wireEGID, egid => new Wire(egid));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -23,6 +23,22 @@ namespace TechbloxModdingAPI
|
||||||
return _instances.TryGetValue(type, out var dict) ? dict : null;
|
return _instances.TryGetValue(type, out var dict) ? dict : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a cached instance if there's an actively used instance of the object already.
|
||||||
|
/// Objects still get garbage collected and then they will be removed from the cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="egid">The EGID of the entity</param>
|
||||||
|
/// <param name="constructor">The constructor to construct the object</param>
|
||||||
|
/// <typeparam name="T">The object type</typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static T GetInstance<T>(EGID egid, Func<EGID, T> constructor, Type type = null) where T : EcsObjectBase
|
||||||
|
{
|
||||||
|
var instances = GetInstances(type ?? typeof(T));
|
||||||
|
if (instances == null || !instances.TryGetValue(egid, out var instance))
|
||||||
|
return constructor(egid); // It will be added by the constructor
|
||||||
|
return (T)instance;
|
||||||
|
}
|
||||||
|
|
||||||
protected EcsObjectBase()
|
protected EcsObjectBase()
|
||||||
{
|
{
|
||||||
if (!_instances.TryGetValue(GetType(), out var dict))
|
if (!_instances.TryGetValue(GetType(), out var dict))
|
||||||
|
@ -31,9 +47,11 @@ namespace TechbloxModdingAPI
|
||||||
_instances.Add(GetType(), dict);
|
_instances.Add(GetType(), dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once VirtualMemberCallInConstructor
|
// ReSharper disable VirtualMemberCallInConstructor
|
||||||
// The ID should not depend on the constructor
|
// The ID should not depend on the constructor
|
||||||
|
if (!dict.ContainsKey(Id)) // Multiple instances may be created
|
||||||
dict.Add(Id, this);
|
dict.Add(Id, this);
|
||||||
|
// ReSharper enable VirtualMemberCallInConstructor
|
||||||
}
|
}
|
||||||
|
|
||||||
#region ECS initializer stuff
|
#region ECS initializer stuff
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace TechbloxModdingAPI
|
||||||
/// An in-game player character. Any Leo you see is a player.
|
/// An in-game player character. Any Leo you see is a player.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Player : IEquatable<Player>, IEquatable<EGID>
|
public class Player : IEquatable<Player>, IEquatable<EGID>
|
||||||
{
|
{ //TODO: Inherit EcsObjectBase and make Id an EGID, useful for caching
|
||||||
// static functionality
|
// static functionality
|
||||||
private static PlayerEngine playerEngine = new PlayerEngine();
|
private static PlayerEngine playerEngine = new PlayerEngine();
|
||||||
private static Player localPlayer;
|
private static Player localPlayer;
|
||||||
|
@ -448,7 +448,7 @@ namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
||||||
return egid != default && egid.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP
|
return egid != default && egid.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP
|
||||||
? new SimBody(egid)
|
? EcsObjectBase.GetInstance(egid, e => new SimBody(e))
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,7 +461,8 @@ namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
||||||
return egid != default && egid.groupID == WiresGUIExclusiveGroups.WireGroup
|
return egid != default && egid.groupID == WiresGUIExclusiveGroups.WireGroup
|
||||||
? new Wire(new EGID(egid.entityID, NamedExclusiveGroup<WiresGroup>.Group))
|
? EcsObjectBase.GetInstance(new EGID(egid.entityID, NamedExclusiveGroup<WiresGroup>.Group),
|
||||||
|
e => new Wire(e))
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,10 @@ namespace TechbloxModdingAPI
|
||||||
/// The cluster this chunk belongs to, or null if no cluster destruction manager present or the chunk doesn't exist.
|
/// The cluster this chunk belongs to, or null if no cluster destruction manager present or the chunk doesn't exist.
|
||||||
/// Get the SimBody from a Block if possible for good performance here.
|
/// Get the SimBody from a Block if possible for good performance here.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Cluster Cluster => cluster ?? (cluster = clusterId == uint.MaxValue ? Block.BlockEngine.GetCluster(Id.entityID) : new Cluster(clusterId));
|
public Cluster Cluster => cluster ??= clusterId == uint.MaxValue // Return cluster or if it's null then set it
|
||||||
|
? Block.BlockEngine.GetCluster(Id.entityID) // If we don't have a clusterId set then get it from the game
|
||||||
|
: GetInstance(new EGID(clusterId, CommonExclusiveGroups.SIMULATION_CLUSTERS_GROUP),
|
||||||
|
egid => new Cluster(egid)); // Otherwise get the cluster from the ID
|
||||||
|
|
||||||
private Cluster cluster;
|
private Cluster cluster;
|
||||||
private readonly uint clusterId = uint.MaxValue;
|
private readonly uint clusterId = uint.MaxValue;
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
using System;
|
|
||||||
using TechbloxModdingAPI.Events;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
|
||||||
{
|
|
||||||
public static class ExceptionUtil
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Invokes an event with a null-check.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="handler">The event to emit, can be null</param>
|
|
||||||
/// <param name="sender">Event sender</param>
|
|
||||||
/// <param name="args">Event arguments</param>
|
|
||||||
/// <typeparam name="T">Type of the event arguments</typeparam>
|
|
||||||
public static void InvokeEvent<T>(EventHandler<T> handler, object sender, T args)
|
|
||||||
{
|
|
||||||
handler?.Invoke(sender, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Wraps the event handler in a try-catch block to avoid propagating exceptions.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="handler">The handler to wrap (not null)</param>
|
|
||||||
/// <typeparam name="T">Type of the event arguments</typeparam>
|
|
||||||
/// <returns>The wrapped handler</returns>
|
|
||||||
public static EventHandler<T> WrapHandler<T>(EventHandler<T> handler)
|
|
||||||
{
|
|
||||||
return (sender, e) =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
handler(sender, e);
|
|
||||||
}
|
|
||||||
catch (Exception e1)
|
|
||||||
{
|
|
||||||
EventRuntimeException wrappedException =
|
|
||||||
new EventRuntimeException($"EventHandler with arg type {typeof(T).Name} threw an exception", e1);
|
|
||||||
Logging.LogWarning(wrappedException.ToString());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Hybrid;
|
using Svelto.ECS.Hybrid;
|
||||||
using TechbloxModdingAPI.Blocks;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
namespace TechbloxModdingAPI.Utility
|
||||||
{
|
{
|
||||||
|
@ -23,32 +22,36 @@ namespace TechbloxModdingAPI.Utility
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
/// Attempts to query an entity and returns the result in an optional reference.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entitiesDB"></param>
|
/// <param name="entitiesDB">The entities DB to query from</param>
|
||||||
/// <param name="obj"></param>
|
/// <param name="obj">The ECS object to query</param>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <param name="group">The group of the entity if the object can have multiple</param>
|
||||||
/// <returns></returns>
|
/// <typeparam name="T">The component to query</typeparam>
|
||||||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj)
|
/// <returns>A reference to the component or a dummy value</returns>
|
||||||
|
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default)
|
||||||
where T : struct, IEntityViewComponent
|
where T : struct, IEntityViewComponent
|
||||||
{
|
{
|
||||||
var opt = QueryEntityOptional<T>(entitiesDB, obj.Id);
|
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
||||||
return opt ? opt : new OptionalRef<T>(obj, true);
|
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
||||||
|
return opt ? opt : new OptionalRef<T>(obj, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entitiesDB"></param>
|
/// <param name="entitiesDB">The entities DB to query from</param>
|
||||||
/// <param name="obj"></param>
|
/// <param name="obj">The ECS object to query</param>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <param name="group">The group of the entity if the object can have multiple</param>
|
||||||
/// <returns></returns>
|
/// <typeparam name="T">The component to query</typeparam>
|
||||||
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj)
|
/// <returns>A reference to the component or a dummy value</returns>
|
||||||
|
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default)
|
||||||
where T : struct, IEntityViewComponent
|
where T : struct, IEntityViewComponent
|
||||||
{
|
{
|
||||||
var opt = QueryEntityOptional<T>(entitiesDB, obj.Id);
|
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
||||||
|
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
||||||
if (opt) return ref opt.Get();
|
if (opt) return ref opt.Get();
|
||||||
if (obj.InitData.Valid) return ref obj.InitData.Initializer(obj.Id).GetOrCreate<T>();
|
if (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrCreate<T>();
|
||||||
return ref opt.Get(); //Default value
|
return ref opt.Get(); //Default value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,32 +21,36 @@ namespace TechbloxModdingAPI.Utility
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
/// Attempts to query an entity and returns the result in an optional reference.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entitiesDB"></param>
|
/// <param name="entitiesDB">The entities DB to query from</param>
|
||||||
/// <param name="obj"></param>
|
/// <param name="obj">The ECS object to query</param>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <param name="group">The group of the entity if the object can have multiple</param>
|
||||||
/// <returns></returns>
|
/// <typeparam name="T">The component to query</typeparam>
|
||||||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj)
|
/// <returns>A reference to the component or a dummy value</returns>
|
||||||
|
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default)
|
||||||
where T : unmanaged, IEntityComponent
|
where T : unmanaged, IEntityComponent
|
||||||
{
|
{
|
||||||
var opt = QueryEntityOptional<T>(entitiesDB, obj.Id);
|
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
||||||
|
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
||||||
return opt ? opt : new OptionalRef<T>(obj, true);
|
return opt ? opt : new OptionalRef<T>(obj, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entitiesDB"></param>
|
/// <param name="entitiesDB">The entities DB to query from</param>
|
||||||
/// <param name="obj"></param>
|
/// <param name="obj">The ECS object to query</param>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <param name="group">The group of the entity if the object can have multiple</param>
|
||||||
/// <returns></returns>
|
/// <typeparam name="T">The component to query</typeparam>
|
||||||
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj)
|
/// <returns>A reference to the component or a dummy value</returns>
|
||||||
|
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default)
|
||||||
where T : unmanaged, IEntityComponent
|
where T : unmanaged, IEntityComponent
|
||||||
{
|
{
|
||||||
var opt = QueryEntityOptional<T>(entitiesDB, obj.Id);
|
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
||||||
|
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
||||||
if (opt) return ref opt.Get();
|
if (opt) return ref opt.Get();
|
||||||
if (obj.InitData.Valid) return ref obj.InitData.Initializer(obj.Id).GetOrCreate<T>();
|
if (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrCreate<T>();
|
||||||
return ref opt.Get(); //Default value
|
return ref opt.Get(); //Default value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
53
TechbloxModdingAPI/Utility/WrappedHandler.cs
Normal file
53
TechbloxModdingAPI/Utility/WrappedHandler.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TechbloxModdingAPI.Events;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Utility
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Wraps the event handler in a try-catch block to avoid propagating exceptions.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The event arguments type</typeparam>
|
||||||
|
public struct WrappedHandler<T>
|
||||||
|
{
|
||||||
|
private EventHandler<T> eventHandler;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Store wrappers so we can unregister them properly
|
||||||
|
/// </summary>
|
||||||
|
private static Dictionary<EventHandler<T>, EventHandler<T>> wrappers =
|
||||||
|
new Dictionary<EventHandler<T>, EventHandler<T>>();
|
||||||
|
|
||||||
|
public static WrappedHandler<T> operator +(WrappedHandler<T> original, EventHandler<T> added)
|
||||||
|
{
|
||||||
|
EventHandler<T> wrapped = (sender, e) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
added(sender, e);
|
||||||
|
}
|
||||||
|
catch (Exception e1)
|
||||||
|
{
|
||||||
|
EventRuntimeException wrappedException =
|
||||||
|
new EventRuntimeException($"EventHandler with arg type {typeof(T).Name} threw an exception",
|
||||||
|
e1);
|
||||||
|
Logging.LogWarning(wrappedException.ToString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
wrappers.Add(added, wrapped);
|
||||||
|
return new WrappedHandler<T> { eventHandler = original.eventHandler + wrapped };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WrappedHandler<T> operator -(WrappedHandler<T> original, EventHandler<T> removed)
|
||||||
|
{
|
||||||
|
if (!wrappers.TryGetValue(removed, out var wrapped)) return original;
|
||||||
|
wrappers.Remove(removed);
|
||||||
|
return new WrappedHandler<T> { eventHandler = original.eventHandler - wrapped };
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Invoke(object sender, T args)
|
||||||
|
{
|
||||||
|
eventHandler?.Invoke(sender, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue