diff --git a/GamecraftModdingAPI/Block.cs b/GamecraftModdingAPI/Block.cs index 46fb00c..271ca1c 100644 --- a/GamecraftModdingAPI/Block.cs +++ b/GamecraftModdingAPI/Block.cs @@ -1,5 +1,6 @@ using System; using System.Reflection; +using System.Threading.Tasks; using Svelto.ECS; using Svelto.ECS.EntityStructs; @@ -29,6 +30,8 @@ namespace GamecraftModdingAPI /// 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. + /// + /// This method causes a sync which may have a performance impact. Use the async version if possible. /// /// The block's type /// The block's color @@ -59,6 +62,44 @@ namespace GamecraftModdingAPI return null; } + /// + /// 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. + /// + /// This method waits for the block to be constructed in 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 async Task PlaceNewAsync(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 + { + var ret = new Block(PlacementEngine.PlaceBlock(block, color, darkness, + position, uscale, scale, player, rotation)); + await AsyncUtils.WaitForSubmission(); + return ret; + } + catch (Exception e) + { + Logging.MetaDebugLog(e); + } + } + + return null; + } + /// /// Returns the most recently placed block. /// diff --git a/GamecraftModdingAPI/Blocks/BlockEngine.cs b/GamecraftModdingAPI/Blocks/BlockEngine.cs index 6275ca1..a47a328 100644 --- a/GamecraftModdingAPI/Blocks/BlockEngine.cs +++ b/GamecraftModdingAPI/Blocks/BlockEngine.cs @@ -10,6 +10,9 @@ using GamecraftModdingAPI.Engines; namespace GamecraftModdingAPI.Blocks { + /// + /// Engine for executing general block actions + /// public class BlockEngine : IApiEngine { public string Name { get; } = "GamecraftModdingAPIBlockGameEngine"; diff --git a/GamecraftModdingAPI/Events/GameActivatedComposePatch.cs b/GamecraftModdingAPI/Events/GameActivatedComposePatch.cs index 9b2bf39..35f075e 100644 --- a/GamecraftModdingAPI/Events/GameActivatedComposePatch.cs +++ b/GamecraftModdingAPI/Events/GameActivatedComposePatch.cs @@ -28,6 +28,8 @@ namespace GamecraftModdingAPI.Events { // register custom game engines GameEngineManager.RegisterEngines(enginesRoot); + // initialize AsyncUtils + AsyncUtils.Setup(enginesRoot); // A new EnginesRoot is always created when ActivateGame is called // so all event emitters and handlers must be re-registered. EventManager.RegisterEngines(enginesRoot); diff --git a/GamecraftModdingAPI/Main.cs b/GamecraftModdingAPI/Main.cs index 56f481f..bea58e1 100644 --- a/GamecraftModdingAPI/Main.cs +++ b/GamecraftModdingAPI/Main.cs @@ -71,6 +71,7 @@ namespace GamecraftModdingAPI Player.Init(); Block.Init(); GameClient.Init(); + AsyncUtils.Init(); Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized"); } diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs index 46a7ef8..4c9105c 100644 --- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs +++ b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs @@ -143,7 +143,11 @@ namespace GamecraftModdingAPI.Tests CommandBuilder.Builder() .Name("PlaceAluminium") .Description("Place a block of aluminium at the given coordinates") - .Action((float x, float y, float z) => { Block.PlaceNew(Blocks.BlockIDs.AluminiumCube, new Unity.Mathematics.float3(x, y, z)); }) + .Action(async (float x, float y, float z) => + { + var block = await Block.PlaceNewAsync(BlockIDs.AluminiumCube, new float3(x, y, z)); + Logging.MetaDebugLog("Block placed with type: " + block.Type); + }) .Build(); CommandBuilder.Builder("getBlock") @@ -170,7 +174,7 @@ namespace GamecraftModdingAPI.Tests Logging.CommandLog("Colored block to " + color); }).Build(); - + GameClient.SetDebugInfo("lookedAt", LookedAt); /* diff --git a/GamecraftModdingAPI/Utility/AsyncUtils.cs b/GamecraftModdingAPI/Utility/AsyncUtils.cs new file mode 100644 index 0000000..fcb5878 --- /dev/null +++ b/GamecraftModdingAPI/Utility/AsyncUtils.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; + +using Svelto.ECS; + +namespace GamecraftModdingAPI.Utility +{ + public static class AsyncUtils + { + private static AsyncUtilsEngine gameEngine = new AsyncUtilsEngine(); + + /// + /// Waits for entity submission asynchronously. + /// + public static async Task WaitForSubmission() + { + await gameEngine.WaitForSubmission(); + } + + public static void Setup(EnginesRoot enginesRoot) + { + gameEngine.Setup(enginesRoot.GenerateEntityFunctions(), enginesRoot.GenerateEntityFactory()); + } + + public static void Init() + { + GameEngineManager.AddGameEngine(gameEngine); + } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Utility/AsyncUtilsEngine.cs b/GamecraftModdingAPI/Utility/AsyncUtilsEngine.cs new file mode 100644 index 0000000..9027de1 --- /dev/null +++ b/GamecraftModdingAPI/Utility/AsyncUtilsEngine.cs @@ -0,0 +1,50 @@ +using System.Collections; +using System.Threading.Tasks; + +using RobocraftX.Schedulers; +using Svelto.ECS; +using Svelto.Tasks.ExtraLean; + +using GamecraftModdingAPI.Engines; + +namespace GamecraftModdingAPI.Utility +{ + public class AsyncUtilsEngine : IApiEngine + { + private IEntityFunctions _efu; + private IEntityFactory _efa; + private IEnumerator WaitForSubmissionInternal(IEntityFunctions efu, IEntityFactory efa, + EntitiesDB entitiesDB, TaskCompletionSource task) + { + var waitEnumerator = new WaitForSubmissionEnumerator(efu, efa, entitiesDB); + while (waitEnumerator.MoveNext()) + yield return null; + task.SetResult(null); + } + + public Task WaitForSubmission() + { + var task = new TaskCompletionSource(); + WaitForSubmissionInternal(_efu, _efa, entitiesDB, task).RunOn(ExtraLean.EveryFrameStepRunner); + return task.Task; + } + + public void Setup(IEntityFunctions efu, IEntityFactory efa) + { + _efu = efu; + _efa = efa; + } + + public void Ready() + { + } + + public EntitiesDB entitiesDB { get; set; } + public void Dispose() + { + } + + public string Name { get; } = "GamecraftModdingAPIAsyncUtilsGameEngine"; + public bool isRemovable { get; } = false; + } +} \ No newline at end of file