diff --git a/GamecraftModdingAPI/Block.cs b/GamecraftModdingAPI/Block.cs
index 88fcc3d..11a7ddc 100644
--- a/GamecraftModdingAPI/Block.cs
+++ b/GamecraftModdingAPI/Block.cs
@@ -1,4 +1,5 @@
using System;
+using System.Reflection;
using Svelto.ECS;
using Svelto.ECS.EntityStructs;
@@ -11,179 +12,209 @@ using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI
{
- ///
- /// A single (perhaps scaled) block. Properties may return default values if the block is removed and then setting them is ignored.
- ///
- public class Block
- {
- private static readonly PlacementEngine PlacementEngine = new PlacementEngine();
- private static readonly MovementEngine MovementEngine = new MovementEngine();
- private static readonly RotationEngine RotationEngine = new RotationEngine();
- private static readonly RemovalEngine RemovalEngine = new RemovalEngine();
- private static readonly BlockEngine BlockEngine = new BlockEngine();
-
- ///
- /// Place a new block at the given position. If scaled, position means the center of the block. The default block size is 0.2 in terms of position.
- /// Place blocks next to each other to connect them.
- /// The placed block will be a complete block with a placement grid and collision which will be saved along with the game.
- ///
- /// The block's type
- /// The block's color
- /// The block color's darkness (0-9) - 0 is default color
- /// The block's position in the grid - default block size is 0.2
- /// The block's rotation in degrees
- /// The block's uniform scale - default scale is 1 (with 0.2 width)
- /// The block's non-uniform scale - 0 means is used
- /// The player who placed the block
- /// The placed block or null if failed
- public static Block PlaceNew(BlockIDs block, float3 position,
- float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
- int uscale = 1, float3 scale = default, Player player = null)
- {
- if (PlacementEngine.IsInGame && GameState.IsBuildMode())
- {
- try
- {
- return new Block(PlacementEngine.PlaceBlock(block, color, darkness,
- position, uscale, scale, player, rotation));
- }
- catch (Exception e)
- {
- Logging.MetaDebugLog(e);
- }
- }
-
- return null;
- }
-
- ///
- /// Returns the most recently placed block.
- ///
- /// The block object
- public static Block GetLastPlacedBlock()
- {
- return new Block(BlockIdentifiers.LatestBlockID);
- }
-
- public Block(EGID id)
- {
- Id = id;
- }
-
- public Block(uint id)
- {
- Id = new EGID(id, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
- }
-
- public EGID Id { get; }
-
- ///
- /// The block's current position or zero if the block no longer exists.
- /// A block is 0.2 wide by default in terms of position.
- ///
- public float3 Position
- {
- get => Exists ? MovementEngine.GetPosition(Id.entityID) : float3.zero;
- set
- {
- if (Exists) MovementEngine.MoveBlock(Id.entityID, value);
- }
- }
-
- ///
- /// The block's current rotation in degrees or zero if the block doesn't exist.
- ///
- public float3 Rotation
- {
- get => Exists ? RotationEngine.GetRotation(Id.entityID) : float3.zero;
- set
- {
- if (Exists) RotationEngine.RotateBlock(Id.entityID, value);
- }
- }
-
- ///
- /// The block's non-uniform scale or zero if the block's invalid. Independent of the uniform scaling.
- ///
- public float3 Scale
- {
- get => BlockEngine.GetBlockInfo(Id)?.scale ?? float3.zero;
- set
- {
- var def = new ScalingEntityStruct();
- BlockEngine.GetBlockInfo(Id, ref def).scale = value;
- }
- }
-
- ///
- /// The block's uniform scale or zero if the block's invalid. Also sets the non-uniform scale.
- ///
- public int UniformScale
- {
- get => BlockEngine.GetBlockInfo(Id)?.desiredScaleFactor ?? 0;
- set
- {
- var def = new BlockPlacementScaleEntityStruct();
- ref var scaleStruct = ref BlockEngine.GetBlockInfo(Id, ref def);
- scaleStruct.blockPlacementHeight = scaleStruct.blockPlacementWidth =
- scaleStruct.desiredScaleFactor = scaleStruct.snapGridScale = value;
- Scale = new float3(value, value, value);
- }
- }
-
- ///
- /// The block's type (ID). Returns BlockIDs.Invalid if the block doesn't exist anymore.
- ///
- public BlockIDs Type => (BlockIDs) (BlockEngine.GetBlockInfo(Id)?.DBID ?? ushort.MaxValue);
-
- ///
- /// The block's color. Returns BlockColors.Default if the block no longer exists.
- ///
- public BlockColor Color
- {
- get
- {
- byte index = BlockEngine.GetBlockInfo(Id)?.indexInPalette ?? byte.MaxValue;
- if (index == byte.MaxValue) return new BlockColor {Color = BlockColors.Default};
- return new BlockColor {Color = (BlockColors) (index % 10), Darkness = (byte) (index / 10)};
- }
- set
- {
- var def = new ColourParameterEntityStruct();
- ref var color = ref BlockEngine.GetBlockInfo(Id, ref def);
- color.indexInPalette = (byte) (value.Color + value.Darkness * 10);
- color.needsUpdate = true;
- }
- }
-
- ///
- /// Whether the block exists. The other properties will return a default value if the block doesn't exist.
- ///
- public bool Exists => BlockEngine.BlockExists(Id);
-
- ///
- /// Returns an array of blocks that are connected to this one. Returns an empty array if the block doesn't exist.
- ///
- public Block[] GetConnectedCubes() => BlockEngine.GetConnectedBlocks(Id);
-
- ///
- /// Removes this block.
- ///
- /// True if the block exists and could be removed.
- public bool Remove() => RemovalEngine.RemoveBlock(Id);
-
- public override string ToString()
- {
- return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Rotation)}: {Rotation}";
- }
-
- public static void Init()
- {
- GameEngineManager.AddGameEngine(PlacementEngine);
- GameEngineManager.AddGameEngine(MovementEngine);
- GameEngineManager.AddGameEngine(RotationEngine);
- GameEngineManager.AddGameEngine(RemovalEngine);
- GameEngineManager.AddGameEngine(BlockEngine);
- }
- }
+ ///
+ /// A single (perhaps scaled) block. Properties may return default values if the block is removed and then setting them is ignored.
+ ///
+ public class Block
+ {
+ protected static readonly PlacementEngine PlacementEngine = new PlacementEngine();
+ protected static readonly MovementEngine MovementEngine = new MovementEngine();
+ protected static readonly RotationEngine RotationEngine = new RotationEngine();
+ protected static readonly RemovalEngine RemovalEngine = new RemovalEngine();
+ protected static readonly BlockEngine BlockEngine = new BlockEngine();
+
+ ///
+ /// Place a new block at the given position. If scaled, position means the center of the block. The default block size is 0.2 in terms of position.
+ /// Place blocks next to each other to connect them.
+ /// The placed block will be a complete block with a placement grid and collision which will be saved along with the game.
+ ///
+ /// The block's type
+ /// The block's color
+ /// The block color's darkness (0-9) - 0 is default color
+ /// The block's position in the grid - default block size is 0.2
+ /// The block's rotation in degrees
+ /// The block's uniform scale - default scale is 1 (with 0.2 width)
+ /// The block's non-uniform scale - 0 means is used
+ /// The player who placed the block
+ /// The placed block or null if failed
+ public static Block PlaceNew(BlockIDs block, float3 position,
+ float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
+ int uscale = 1, float3 scale = default, Player player = null)
+ {
+ if (PlacementEngine.IsInGame && GameState.IsBuildMode())
+ {
+ try
+ {
+ return new Block(PlacementEngine.PlaceBlock(block, color, darkness,
+ position, uscale, scale, player, rotation));
+ }
+ catch (Exception e)
+ {
+ Logging.MetaDebugLog(e);
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Returns the most recently placed block.
+ ///
+ /// The block object
+ public static Block GetLastPlacedBlock()
+ {
+ return new Block(BlockIdentifiers.LatestBlockID);
+ }
+
+ public Block(EGID id)
+ {
+ Id = id;
+ }
+
+ public Block(uint id)
+ {
+ Id = new EGID(id, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
+ }
+
+ protected static void Sync()
+ {
+ DeterministicStepCompositionRootPatch.SubmitEntitiesNow();
+ }
+
+ public EGID Id { get; }
+
+ ///
+ /// The block's current position or zero if the block no longer exists.
+ /// A block is 0.2 wide by default in terms of position.
+ ///
+ public float3 Position
+ {
+ get => Exists ? MovementEngine.GetPosition(Id.entityID) : float3.zero;
+ set
+ {
+ if (Exists) MovementEngine.MoveBlock(Id.entityID, value);
+ }
+ }
+
+ ///
+ /// The block's current rotation in degrees or zero if the block doesn't exist.
+ ///
+ public float3 Rotation
+ {
+ get => Exists ? RotationEngine.GetRotation(Id.entityID) : float3.zero;
+ set
+ {
+ if (Exists) RotationEngine.RotateBlock(Id.entityID, value);
+ }
+ }
+
+ ///
+ /// The block's non-uniform scale or zero if the block's invalid. Independent of the uniform scaling.
+ ///
+ public float3 Scale
+ {
+ get => BlockEngine.GetBlockInfo(Id)?.scale ?? float3.zero;
+ set
+ {
+ var def = new ScalingEntityStruct();
+ BlockEngine.GetBlockInfo(Id, ref def).scale = value;
+ }
+ }
+
+ ///
+ /// The block's uniform scale or zero if the block's invalid. Also sets the non-uniform scale.
+ ///
+ public int UniformScale
+ {
+ get => BlockEngine.GetBlockInfo(Id)?.desiredScaleFactor ?? 0;
+ set
+ {
+ var def = new BlockPlacementScaleEntityStruct();
+ ref var scaleStruct = ref BlockEngine.GetBlockInfo(Id, ref def);
+ scaleStruct.blockPlacementHeight = scaleStruct.blockPlacementWidth =
+ scaleStruct.desiredScaleFactor = scaleStruct.snapGridScale = value;
+ Scale = new float3(value, value, value);
+ }
+ }
+
+ ///
+ /// The block's type (ID). Returns BlockIDs.Invalid if the block doesn't exist anymore.
+ ///
+ public BlockIDs Type => (BlockIDs)(BlockEngine.GetBlockInfo(Id)?.DBID ?? ushort.MaxValue);
+
+ ///
+ /// The block's color. Returns BlockColors.Default if the block no longer exists.
+ ///
+ public BlockColor Color
+ {
+ get
+ {
+ byte index = BlockEngine.GetBlockInfo(Id)?.indexInPalette ?? byte.MaxValue;
+ if (index == byte.MaxValue) return new BlockColor { Color = BlockColors.Default };
+ return new BlockColor { Color = (BlockColors)(index % 10), Darkness = (byte)(index / 10) };
+ }
+ set
+ {
+ var def = new ColourParameterEntityStruct();
+ ref var color = ref BlockEngine.GetBlockInfo(Id, ref def);
+ color.indexInPalette = (byte)(value.Color + value.Darkness * 10);
+ color.needsUpdate = true;
+ }
+ }
+
+ ///
+ /// Whether the block exists. The other properties will return a default value if the block doesn't exist.
+ ///
+ public bool Exists => BlockEngine.BlockExists(Id);
+
+ ///
+ /// Returns an array of blocks that are connected to this one. Returns an empty array if the block doesn't exist.
+ ///
+ public Block[] GetConnectedCubes() => BlockEngine.GetConnectedBlocks(Id);
+
+ ///
+ /// Removes this block.
+ ///
+ /// True if the block exists and could be removed.
+ public bool Remove() => RemovalEngine.RemoveBlock(Id);
+
+ public override string ToString()
+ {
+ return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Rotation)}: {Rotation}";
+ }
+
+ public static void Init()
+ {
+ GameEngineManager.AddGameEngine(PlacementEngine);
+ GameEngineManager.AddGameEngine(MovementEngine);
+ GameEngineManager.AddGameEngine(RotationEngine);
+ GameEngineManager.AddGameEngine(RemovalEngine);
+ GameEngineManager.AddGameEngine(BlockEngine);
+ }
+
+ public T Specialise(bool sameTick = true) where T : Block
+ {
+ // What have I gotten myself into?
+ // C# can't cast to a child of Block unless the object was originally that child type
+ // And C# doesn't let me make implicit cast operators for child types
+ // So thanks to Microsoft, we've got this horrible implementation using reflection
+ ConstructorInfo ctor = typeof(T).GetConstructor(types: new System.Type[] { typeof(EGID) });
+ if (ctor == null)
+ {
+ throw new BlockSpecializationException("Specialized block constructor does not accept an EGID");
+ }
+ if (sameTick) Sync();
+ return (T)ctor.Invoke(new object[] { Id });
+ }
+
+#if DEBUG
+ public static EntitiesDB entitiesDB
+ {
+ get
+ {
+ return BlockEngine.GetEntitiesDB();
+ }
+ }
+#endif
+ }
}
\ No newline at end of file
diff --git a/GamecraftModdingAPI/Blocks/BlockEngine.cs b/GamecraftModdingAPI/Blocks/BlockEngine.cs
index f678501..d8182ca 100644
--- a/GamecraftModdingAPI/Blocks/BlockEngine.cs
+++ b/GamecraftModdingAPI/Blocks/BlockEngine.cs
@@ -9,66 +9,78 @@ using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Blocks
{
- public class BlockEngine : IApiEngine
- {
- public string Name { get; } = "GamecraftModdingAPIBlockGameEngine";
+ public class BlockEngine : IApiEngine
+ {
+ public string Name { get; } = "GamecraftModdingAPIBlockGameEngine";
- public EntitiesDB entitiesDB { set; private get; }
+ public EntitiesDB entitiesDB { set; private get; }
- public bool isRemovable => false;
+ public bool isRemovable => false;
- public void Dispose()
- {
- }
+ public void Dispose()
+ {
+ }
- public void Ready()
- {
- }
+ public void Ready()
+ {
+ }
- public Block[] GetConnectedBlocks(EGID blockID)
- {
- if (!BlockExists(blockID)) return new Block[0];
- Stack cubeStack = new Stack();
- FasterList cubesToProcess = new FasterList();
- ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID.entityID, cubeStack, cubesToProcess, (in GridConnectionsEntityStruct g) => { return false; });
- var ret = new Block[cubesToProcess.count];
- for (int i = 0; i < cubesToProcess.count; i++)
- ret[i] = new Block(cubesToProcess[i]);
- return ret;
- }
+ public Block[] GetConnectedBlocks(EGID blockID)
+ {
+ if (!BlockExists(blockID)) return new Block[0];
+ Stack cubeStack = new Stack();
+ FasterList cubesToProcess = new FasterList();
+ ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID.entityID, cubeStack, cubesToProcess, (in GridConnectionsEntityStruct g) => { return false; });
+ var ret = new Block[cubesToProcess.count];
+ for (int i = 0; i < cubesToProcess.count; i++)
+ ret[i] = new Block(cubesToProcess[i]);
+ return ret;
+ }
- ///
- /// Get a struct of a block. Can be used to set properties.
- /// When only querying parameters, use the other overload for convenience.
- ///
- ///
- ///
- ///
- ///
- public ref T GetBlockInfo(EGID blockID, ref T def) where T : struct, IEntityComponent
- {
- if (entitiesDB.Exists(blockID))
- return ref entitiesDB.QueryEntity(blockID);
- return ref def;
- }
+ ///
+ /// Get a struct of a block. Can be used to set properties.
+ /// When only querying parameters, use the other overload for convenience.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public ref T GetBlockInfo(EGID blockID, ref T def) where T : struct, IEntityComponent
+ {
+ if (entitiesDB.Exists(blockID))
+ return ref entitiesDB.QueryEntity(blockID);
+ return ref def;
+ }
- ///
- /// Get a struct of a block. Can only be used to retrieve information.
- /// Use the overload with a default parameter to get the struct by reference to set values.
- ///
- /// The block's EGID
- /// The struct's type to get
- /// A copy of the struct or null
- public T? GetBlockInfo(EGID blockID) where T : struct, IEntityComponent
- {
- if (entitiesDB.Exists(blockID))
- return entitiesDB.QueryEntity(blockID);
- return null;
- }
+ ///
+ /// Get a struct of a block. Can only be used to retrieve information.
+ /// Use the overload with a default parameter to get the struct by reference to set values.
+ ///
+ /// The block's EGID
+ /// The struct's type to get
+ /// A copy of the struct or null
+ public T? GetBlockInfo(EGID blockID) where T : struct, IEntityComponent
+ {
+ if (entitiesDB.Exists(blockID))
+ return entitiesDB.QueryEntity(blockID);
+ return null;
+ }
- public bool BlockExists(EGID id)
- {
- return entitiesDB.Exists(id);
- }
- }
+ public bool BlockExists(EGID id)
+ {
+ return entitiesDB.Exists(id);
+ }
+
+ public bool GetBlockInfoExists(EGID blockID) where T : struct, IEntityComponent
+ {
+ return entitiesDB.Exists(blockID);
+ }
+
+#if DEBUG
+ public EntitiesDB GetEntitiesDB()
+ {
+ return entitiesDB;
+ }
+#endif
+ }
}
\ No newline at end of file
diff --git a/GamecraftModdingAPI/Blocks/BlockExceptions.cs b/GamecraftModdingAPI/Blocks/BlockExceptions.cs
new file mode 100644
index 0000000..47af014
--- /dev/null
+++ b/GamecraftModdingAPI/Blocks/BlockExceptions.cs
@@ -0,0 +1,43 @@
+using System;
+
+using GamecraftModdingAPI;
+
+namespace GamecraftModdingAPI.Blocks
+{
+ public class BlockException : GamecraftModdingAPIException
+ {
+ public BlockException()
+ {
+ }
+
+ public BlockException(System.String message) : base(message)
+ {
+ }
+
+ public BlockException(System.String message, Exception innerException) : base(message, innerException)
+ {
+ }
+ }
+
+ public class BlockTypeException : BlockException
+ {
+ public BlockTypeException()
+ {
+ }
+
+ public BlockTypeException(string message) : base(message)
+ {
+ }
+ }
+
+ public class BlockSpecializationException : BlockException
+ {
+ public BlockSpecializationException()
+ {
+ }
+
+ public BlockSpecializationException(string message) : base(message)
+ {
+ }
+ }
+}
diff --git a/GamecraftModdingAPI/Blocks/ConsoleBlock.cs b/GamecraftModdingAPI/Blocks/ConsoleBlock.cs
new file mode 100644
index 0000000..c1dec27
--- /dev/null
+++ b/GamecraftModdingAPI/Blocks/ConsoleBlock.cs
@@ -0,0 +1,97 @@
+using System;
+
+using RobocraftX.Blocks;
+using Svelto.ECS;
+using Unity.Mathematics;
+
+using GamecraftModdingAPI;
+using GamecraftModdingAPI.Utility;
+
+namespace GamecraftModdingAPI.Blocks
+{
+ public class ConsoleBlock : Block
+ {
+ public static ConsoleBlock PlaceNew(float3 position,
+ float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
+ int uscale = 1, float3 scale = default, Player player = null)
+ {
+ if (PlacementEngine.IsInGame && GameState.IsBuildMode())
+ {
+ try
+ {
+ EGID id = PlacementEngine.PlaceBlock(BlockIDs.ConsoleBlock, color, darkness,
+ position, uscale, scale, player, rotation);
+ Sync();
+ return new ConsoleBlock(id);
+ }
+ catch (Exception e)
+ {
+ Logging.MetaDebugLog(e);
+ }
+ }
+
+ return null;
+ }
+
+ public ConsoleBlock(EGID id): base(id)
+ {
+ if (!BlockEngine.GetBlockInfoExists(this.Id))
+ {
+ throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
+ }
+ }
+
+ public ConsoleBlock(uint id): base(id)
+ {
+ if (!BlockEngine.GetBlockInfoExists(this.Id))
+ {
+ throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
+ }
+ }
+
+ // custom console block properties
+
+ public string Command
+ {
+ get
+ {
+ return BlockEngine.GetBlockInfo(Id)?.commandName ?? null;
+ }
+
+ set
+ {
+ BlockEngine.GetBlockInfo(Id)?.commandName.Set(value);
+ }
+ }
+
+ public string Arg1
+ {
+ get => BlockEngine.GetBlockInfo(Id)?.arg1 ?? null;
+
+ set
+ {
+ BlockEngine.GetBlockInfo(Id)?.arg1.Set(value);
+ }
+ }
+
+ public string Arg2
+ {
+ get => BlockEngine.GetBlockInfo(Id)?.arg2 ?? null;
+
+ set
+ {
+ BlockEngine.GetBlockInfo(Id)?.arg2.Set(value);
+ }
+ }
+
+ public string Arg3
+ {
+ get => BlockEngine.GetBlockInfo(Id)?.arg3 ?? null;
+
+ set
+ {
+ BlockEngine.GetBlockInfo(Id)?.arg3.Set(value);
+ }
+ }
+ }
+}
diff --git a/GamecraftModdingAPI/Blocks/TextBlock.cs b/GamecraftModdingAPI/Blocks/TextBlock.cs
new file mode 100644
index 0000000..bd2a23b
--- /dev/null
+++ b/GamecraftModdingAPI/Blocks/TextBlock.cs
@@ -0,0 +1,92 @@
+using System;
+
+using Gamecraft.Blocks.GUI;
+using Svelto.ECS;
+using Unity.Mathematics;
+
+using GamecraftModdingAPI;
+using GamecraftModdingAPI.Utility;
+
+namespace GamecraftModdingAPI.Blocks
+{
+ public class TextBlock : Block
+ {
+
+ public static TextBlock PlaceNew(float3 position,
+ float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
+ int uscale = 1, float3 scale = default, Player player = null)
+ {
+ if (PlacementEngine.IsInGame && GameState.IsBuildMode())
+ {
+ try
+ {
+ EGID id = PlacementEngine.PlaceBlock(BlockIDs.TextBlock, color, darkness,
+ position, uscale, scale, player, rotation);
+ Sync();
+ return new TextBlock(id);
+ }
+ catch (Exception e)
+ {
+ Logging.MetaDebugLog(e);
+ }
+ }
+
+ return null;
+ }
+
+ public TextBlock(EGID id) : base(id)
+ {
+ if (!BlockEngine.GetBlockInfoExists(this.Id))
+ {
+ throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
+ }
+ }
+
+ public TextBlock(uint id) : base(id)
+ {
+ if (!BlockEngine.GetBlockInfoExists(this.Id))
+ {
+ throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
+ }
+ }
+
+ // custom text block properties
+
+ ///
+ /// The text block's current text.
+ ///
+ public string Text
+ {
+ get
+ {
+ return BlockEngine.GetBlockInfo(Id)?.textCurrent ?? null;
+ }
+
+ set
+ {
+ TextBlockDataStruct def = default;
+ ref TextBlockDataStruct tbds = ref BlockEngine.GetBlockInfo(Id, ref def);
+ tbds.textCurrent.Set(value);
+ tbds.textStored.Set(value);
+ BlockEngine.GetBlockInfo(Id)?.newTextBlockStringContent.Set(value);
+ }
+ }
+
+ ///
+ /// The text block's current text block ID (used in ChangeTextBlockCommand).
+ ///
+ public string TextBlockId
+ {
+ get
+ {
+ return BlockEngine.GetBlockInfo(Id)?.textBlockID ?? null;
+ }
+
+ set
+ {
+ BlockEngine.GetBlockInfo(Id)?.textBlockID.Set(value);
+ BlockEngine.GetBlockInfo(Id)?.newTextBlockID.Set(value);
+ }
+ }
+ }
+}
diff --git a/GamecraftModdingAPI/GamecraftModdingAPI.csproj b/GamecraftModdingAPI/GamecraftModdingAPI.csproj
index 55339fa..074c5b6 100644
--- a/GamecraftModdingAPI/GamecraftModdingAPI.csproj
+++ b/GamecraftModdingAPI/GamecraftModdingAPI.csproj
@@ -3,7 +3,7 @@
net472
true
- 1.0.2
+ 1.1.0
Exmods
GNU General Public Licence 3+
https://git.exmods.org/modtainers/GamecraftModdingAPI
diff --git a/GamecraftModdingAPI/Utility/DeterministicStepCompositionRootPatch.cs b/GamecraftModdingAPI/Utility/DeterministicStepCompositionRootPatch.cs
new file mode 100644
index 0000000..9cabbe0
--- /dev/null
+++ b/GamecraftModdingAPI/Utility/DeterministicStepCompositionRootPatch.cs
@@ -0,0 +1,25 @@
+using System;
+
+using RobocraftX.StateSync;
+using Svelto.ECS;
+
+using HarmonyLib;
+
+namespace GamecraftModdingAPI.Utility
+{
+ [HarmonyPatch(typeof(DeterministicStepCompositionRoot), "ResetWorld")]
+ public static class DeterministicStepCompositionRootPatch
+ {
+ private static SimpleEntitiesSubmissionScheduler engineRootScheduler;
+ public static void Postfix(SimpleEntitiesSubmissionScheduler scheduler)
+ {
+ engineRootScheduler = scheduler;
+ }
+
+ internal static void SubmitEntitiesNow()
+ {
+ if (engineRootScheduler != null)
+ engineRootScheduler.SubmitEntities();
+ }
+ }
+}