diff --git a/GamecraftModdingAPI/Block.cs b/GamecraftModdingAPI/Block.cs
index d7c37dc..7a65d04 100644
--- a/GamecraftModdingAPI/Block.cs
+++ b/GamecraftModdingAPI/Block.cs
@@ -25,6 +25,7 @@ namespace GamecraftModdingAPI
protected static readonly RotationEngine RotationEngine = new RotationEngine();
protected static readonly RemovalEngine RemovalEngine = new RemovalEngine();
protected static readonly SignalEngine SignalEngine = new SignalEngine();
+ protected static readonly BlockEventsEngine BlockEventsEngine = new BlockEventsEngine();
protected internal static readonly BlockEngine BlockEngine = new BlockEngine();
@@ -109,17 +110,27 @@ namespace GamecraftModdingAPI
return new Block(BlockIdentifiers.LatestBlockID);
}
+ ///
+ /// An event that fires each time a block is placed.
+ ///
+ public static event EventHandler Placed
+ {
+ add => BlockEventsEngine.Placed += value;
+ remove => BlockEventsEngine.Placed -= value;
+ }
+
+ ///
+ /// An event that fires each time a block is removed.
+ ///
+ public static event EventHandler Removed
+ {
+ add => BlockEventsEngine.Removed += value;
+ remove => BlockEventsEngine.Removed -= value;
+ }
+
public Block(EGID id)
{
Id = id;
- if (!BlockEngine.BlockExists(Id))
- {
- /*Sync();
- if (!BlockEngine.BlockExists(Id))
- {
- throw new BlockDoesNotExistException($"Block {Id.entityID} must be placed using PlaceNew(...) since it does not exist yet");
- }*/
- }
}
public Block(uint id) : this(new EGID(id, CommonExclusiveGroups.OWNED_BLOCKS_GROUP))
@@ -264,11 +275,11 @@ namespace GamecraftModdingAPI
/// Returns the rigid body of the cluster of blocks this one belongs to during simulation.
/// Can be used to apply forces or move the block around while the simulation is running.
///
- /// The SimBody of the cluster
+ /// The SimBody of the cluster or null if the block doesn't exist.
public SimBody GetSimBody()
{
- uint id = BlockEngine.GetBlockInfo(Id).machineRigidBodyId;
- return new SimBody(id);
+ uint id = BlockEngine.GetBlockInfo(Id, out var exists).machineRigidBodyId;
+ return exists ? new SimBody(id) : null;
}
public override string ToString()
@@ -283,6 +294,7 @@ namespace GamecraftModdingAPI
GameEngineManager.AddGameEngine(RotationEngine);
GameEngineManager.AddGameEngine(RemovalEngine);
GameEngineManager.AddGameEngine(BlockEngine);
+ GameEngineManager.AddGameEngine(BlockEventsEngine);
}
///
diff --git a/GamecraftModdingAPI/Blocks/BlockEventsEngine.cs b/GamecraftModdingAPI/Blocks/BlockEventsEngine.cs
new file mode 100644
index 0000000..3c34b65
--- /dev/null
+++ b/GamecraftModdingAPI/Blocks/BlockEventsEngine.cs
@@ -0,0 +1,42 @@
+using System;
+using GamecraftModdingAPI.Engines;
+using GamecraftModdingAPI.Utility;
+using RobocraftX.Common;
+using Svelto.ECS;
+
+namespace GamecraftModdingAPI.Blocks
+{
+ public class BlockEventsEngine : IReactionaryEngine
+ {
+ public event EventHandler Placed;
+ public event EventHandler Removed;
+
+ public void Ready()
+ {
+ }
+
+ public EntitiesDB entitiesDB { get; set; }
+ public void Dispose()
+ {
+ }
+
+ public string Name { get; } = "GamecraftModdingAPIBlockEventsEngine";
+ public bool isRemovable { get; } = false;
+
+ public void Add(ref DBEntityStruct entityComponent, EGID egid)
+ {
+ ExceptionUtil.InvokeEvent(Placed, this, new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID});
+ }
+
+ public void Remove(ref DBEntityStruct entityComponent, EGID egid)
+ {
+ ExceptionUtil.InvokeEvent(Removed, this, new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID});
+ }
+ }
+
+ public struct BlockPlacedRemovedEventArgs
+ {
+ public EGID ID;
+ public BlockIDs Type;
+ }
+}
\ No newline at end of file
diff --git a/GamecraftModdingAPI/Blocks/BlockExceptions.cs b/GamecraftModdingAPI/Blocks/BlockExceptions.cs
index 557282a..47af014 100644
--- a/GamecraftModdingAPI/Blocks/BlockExceptions.cs
+++ b/GamecraftModdingAPI/Blocks/BlockExceptions.cs
@@ -40,15 +40,4 @@ namespace GamecraftModdingAPI.Blocks
{
}
}
-
- public class BlockDoesNotExistException : BlockException
- {
- public BlockDoesNotExistException()
- {
- }
-
- public BlockDoesNotExistException(string message) : base(message)
- {
- }
- }
}
diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
index 783a33d..1e249bb 100644
--- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
+++ b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
@@ -226,6 +226,10 @@ namespace GamecraftModdingAPI.Tests
GameClient.SetDebugInfo("lookedAt", LookedAt);
GameClient.SetDebugInfo("InstalledMods", InstalledMods);
+ Block.Placed += (sender, args) =>
+ Logging.MetaDebugLog("Placed block " + args.Type + " with ID " + args.ID);
+ Block.Removed += (sender, args) =>
+ Logging.MetaDebugLog("Removed block " + args.Type + " with ID " + args.ID);
/*
CommandManager.AddCommand(new SimpleCustomCommandEngine((float d) => { UnityEngine.Camera.main.fieldOfView = d; },
diff --git a/GamecraftModdingAPI/Utility/ExceptionUtil.cs b/GamecraftModdingAPI/Utility/ExceptionUtil.cs
new file mode 100644
index 0000000..63d0fef
--- /dev/null
+++ b/GamecraftModdingAPI/Utility/ExceptionUtil.cs
@@ -0,0 +1,29 @@
+using System;
+using GamecraftModdingAPI.Events;
+
+namespace GamecraftModdingAPI.Utility
+{
+ public static class ExceptionUtil
+ {
+ ///
+ /// Invokes an event in a try-catch block to avoid propagating exceptions.
+ ///
+ /// The event to emit, can be null
+ /// Event sender
+ /// Event arguments
+ /// Type of the event arguments
+ public static void InvokeEvent(EventHandler handler, object sender, T args)
+ {
+ try
+ {
+ handler?.Invoke(sender, args);
+ }
+ catch (Exception e)
+ {
+ EventRuntimeException wrappedException =
+ new EventRuntimeException($"EventHandler with arg type {typeof(T).Name} threw an exception", e);
+ Logging.LogWarning(wrappedException.ToString());
+ }
+ }
+ }
+}
\ No newline at end of file