diff --git a/GamecraftModdingAPI/Block.cs b/GamecraftModdingAPI/Block.cs
new file mode 100644
index 0000000..d099aba
--- /dev/null
+++ b/GamecraftModdingAPI/Block.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using GamecraftModdingAPI.Blocks;
+using GamecraftModdingAPI.Utility;
+using RobocraftX.Common;
+using Svelto.ECS;
+using Unity.Mathematics;
+
+namespace GamecraftModdingAPI
+{
+ 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.
+ ///
+ public float3 Position
+ {
+ get => MovementEngine.GetPosition(Id.entityID);
+ set => MovementEngine.MoveBlock(Id.entityID, value);
+ }
+
+ ///
+ /// Returns an array of blocks that are connected to this one.
+ ///
+ public Block[] ConnectedCubes => BlockEngine.GetConnectedBlocks(Id.entityID);
+
+ ///
+ /// The block's current rotation in degrees.
+ ///
+ public float3 Rotation
+ {
+ get => RotationEngine.GetRotation(Id.entityID);
+ set => RotationEngine.RotateBlock(Id.entityID, value);
+ }
+
+ ///
+ /// Removes this block.
+ ///
+ /// True if the block exists and could be removed.
+ public bool Remove()
+ {
+ return RemovalEngine.RemoveBlock(Id);
+ }
+
+ public static void Init()
+ {
+ GameEngineManager.AddGameEngine(PlacementEngine);
+ GameEngineManager.AddGameEngine(MovementEngine);
+ GameEngineManager.AddGameEngine(RotationEngine);
+ GameEngineManager.AddGameEngine(RemovalEngine);
+ GameEngineManager.AddGameEngine(BlockEngine);
+ }
+ }
+}
\ No newline at end of file
diff --git a/GamecraftModdingAPI/Blocks/BlockEngine.cs b/GamecraftModdingAPI/Blocks/BlockEngine.cs
new file mode 100644
index 0000000..ad2af1b
--- /dev/null
+++ b/GamecraftModdingAPI/Blocks/BlockEngine.cs
@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+using GamecraftModdingAPI.Engines;
+using RobocraftX.Blocks;
+using RobocraftX.Common;
+using Svelto.DataStructures;
+using Svelto.ECS;
+using Svelto.ECS.EntityStructs;
+using Unity.Mathematics;
+
+namespace GamecraftModdingAPI.Blocks
+{
+ public class BlockEngine : IApiEngine
+ {
+ public string Name { get; } = "GamecraftModdingAPIBlockGameEngine";
+
+ public EntitiesDB entitiesDB { set; private get; }
+
+ public bool isRemovable => false;
+
+ public void Dispose()
+ {
+ }
+
+ public void Ready()
+ {
+ }
+
+ public Block[] GetConnectedBlocks(uint blockID)
+ {
+ Stack cubeStack = new Stack();
+ FasterList cubesToProcess = new FasterList();
+ ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GamecraftModdingAPI/Blocks/BlockUtility.cs b/GamecraftModdingAPI/Blocks/BlockUtility.cs
deleted file mode 100644
index 342ee73..0000000
--- a/GamecraftModdingAPI/Blocks/BlockUtility.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using RobocraftX.Blocks.Ghost;
-using RobocraftX.Character.Camera;
-using RobocraftX.Character.Factories;
-using Svelto.ECS;
-
-namespace GamecraftModdingAPI.Blocks
-{
- public class BlockUtility
- {
- ///
- /// Returns the block the player is currently looking at.
- ///
- /// The player's ID
- /// The entities DB
- /// The maximum distance from the player (default is the player's building reach)
- /// The block's EGID or null if not found
- public static EGID? GetBlockLookedAt(uint playerId, EntitiesDB entitiesDB, float maxDistance = -1f)
- {
- if (!entitiesDB.TryQueryMappedEntities(
- CameraExclusiveGroups.CameraGroup, out var mapper))
- return null;
- mapper.TryGetEntity(playerId, out CharacterCameraRayCastEntityStruct rayCast);
- float distance = maxDistance < 0
- ? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast)
- : maxDistance;
- if (rayCast.hit && rayCast.distance <= distance)
- return rayCast.hitEgid;
- return null;
- }
- }
-}
\ No newline at end of file
diff --git a/GamecraftModdingAPI/Blocks/Movement.cs b/GamecraftModdingAPI/Blocks/Movement.cs
deleted file mode 100644
index 28575a9..0000000
--- a/GamecraftModdingAPI/Blocks/Movement.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-using Unity.Mathematics;
-
-namespace GamecraftModdingAPI.Blocks
-{
- ///
- /// Common block movement operations.
- /// The functionality of this class only works in build mode.
- ///
- public static class Movement
- {
- private static MovementEngine movementEngine = new MovementEngine();
-
- ///
- /// Move a single block by a specific (x,y,z) amount (offset).
- /// The moved block will remain connected to the blocks it was touching before it was moved.
- /// The block's placement grid and collision box are also moved.
- ///
- /// The block's id
- /// The movement amount (x,y,z)
- /// Whether the operation was successful
- public static bool MoveBlock(uint id, float3 vector)
- {
- if (movementEngine.IsInGame && GamecraftModdingAPI.Utility.GameState.IsBuildMode())
- {
- movementEngine.MoveBlock(id, vector);
- return true;
- }
- return false;
- }
-
- ///
- /// Move all connected blocks by a specific (x,y,z) amount (offset).
- /// The moved blocks will remain connected to the block they're touching.
- /// All of the block's placement grids and collision boxes are also moved.
- /// This is equivalent to calling MoveBlock() for every connected block.
- ///
- /// The starting block's id
- /// The movement amount (x,y,z)
- /// Whether the operation was successful
- public static bool MoveConnectedBlocks(uint id, float3 vector)
- {
- if (movementEngine.IsInGame && GamecraftModdingAPI.Utility.GameState.IsBuildMode())
- {
- movementEngine.MoveConnectedBlocks(id, vector);
- return true;
- }
- return false;
- }
-
- public static void Init()
- {
- GamecraftModdingAPI.Utility.GameEngineManager.AddGameEngine(movementEngine);
- }
- }
-}
diff --git a/GamecraftModdingAPI/Blocks/MovementEngine.cs b/GamecraftModdingAPI/Blocks/MovementEngine.cs
index fbeea99..6615949 100644
--- a/GamecraftModdingAPI/Blocks/MovementEngine.cs
+++ b/GamecraftModdingAPI/Blocks/MovementEngine.cs
@@ -57,30 +57,24 @@ namespace GamecraftModdingAPI.Blocks
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
// main (persistent) position
- posStruct.position += vector;
+ posStruct.position = vector;
// placement grid position
- gridStruct.position += vector;
+ gridStruct.position = vector;
// rendered position
- transStruct.position += vector;
+ transStruct.position = vector;
// collision position
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Translation
{
Value = posStruct.position
});
+ entitiesDB.QueryEntity(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP).isProcessed = false;
return posStruct.position;
}
- public float3 MoveConnectedBlocks(uint blockID, float3 vector)
+ public float3 GetPosition(uint blockID)
{
- Stack cubeStack = new Stack();
- FasterList cubesToMove = new FasterList();
- ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubesToMove, (in GridConnectionsEntityStruct g) => { return false; });
- for (int i = 0; i < cubesToMove.count; i++)
- {
- MoveBlock(cubesToMove[i], vector);
- entitiesDB.QueryEntity(cubesToMove[i], CommonExclusiveGroups.OWNED_BLOCKS_GROUP).isProcessed = false;
- }
- return this.entitiesDB.QueryEntity(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP).position;
+ ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
+ return posStruct.position;
}
}
}
diff --git a/GamecraftModdingAPI/Blocks/Placement.cs b/GamecraftModdingAPI/Blocks/Placement.cs
deleted file mode 100644
index c029ebb..0000000
--- a/GamecraftModdingAPI/Blocks/Placement.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-
-using Unity.Mathematics;
-using Svelto.ECS;
-
-using GamecraftModdingAPI.Utility;
-
-namespace GamecraftModdingAPI.Blocks
-{
- ///
- /// Common block placement operations.
- /// The functionality in this class is for build mode.
- ///
- public static class Placement
- {
- private static PlacementEngine placementEngine = new PlacementEngine();
-
- ///
- /// 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's ID or null if failed
- public static EGID? PlaceBlock(BlockIDs block, float3 position,
- float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
- int uscale = 1, float3 scale = default, uint playerId = 0)
- {
- if (placementEngine.IsInGame && GameState.IsBuildMode())
- {
- try
- {
- return placementEngine.PlaceBlock(block, color, darkness, position, uscale, scale, playerId, rotation);
- }
- catch (Exception e)
- {
- Logging.MetaDebugLog(e);
- }
- }
- return null;
- }
-
- public static void Init()
- {
- GameEngineManager.AddGameEngine(placementEngine);
- }
- }
-}
diff --git a/GamecraftModdingAPI/Blocks/PlacementEngine.cs b/GamecraftModdingAPI/Blocks/PlacementEngine.cs
index 17f3314..d0a1192 100644
--- a/GamecraftModdingAPI/Blocks/PlacementEngine.cs
+++ b/GamecraftModdingAPI/Blocks/PlacementEngine.cs
@@ -22,6 +22,7 @@ using uREPL;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Engines;
+using GamecraftModdingAPI.Players;
namespace GamecraftModdingAPI.Blocks
{
@@ -46,11 +47,12 @@ namespace GamecraftModdingAPI.Blocks
private static BlockEntityFactory _blockEntityFactory; //Injected from PlaceBlockEngine
public EGID PlaceBlock(BlockIDs block, BlockColors color, byte darkness, float3 position, int uscale,
- float3 scale, uint playerId, float3 rotation)
+ float3 scale, Player player, float3 rotation)
{ //It appears that only the non-uniform scale has any visible effect, but if that's not given here it will be set to the uniform one
if (darkness > 9)
throw new Exception("That is too dark. Make sure to use 0-9 as darkness. (0 is default.)");
- return BuildBlock((ushort) block, (byte) (color + darkness * 10), position, uscale, scale, rotation, playerId);
+ return BuildBlock((ushort) block, (byte) (color + darkness * 10), position, uscale, scale, rotation,
+ (player ?? new Player(PlayerType.Local)).Id);
}
private EGID BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot, uint playerId)
diff --git a/GamecraftModdingAPI/Blocks/Removal.cs b/GamecraftModdingAPI/Blocks/Removal.cs
deleted file mode 100644
index 0bb54fb..0000000
--- a/GamecraftModdingAPI/Blocks/Removal.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Svelto.ECS;
-
-using GamecraftModdingAPI.Utility;
-
-namespace GamecraftModdingAPI.Blocks
-{
- public class Removal
- {
- private static RemovalEngine _removalEngine = new RemovalEngine();
-
- ///
- /// Removes the block with the given ID. Returns false if the block doesn't exist or the game isn't in build mode.
- ///
- /// The block to remove
- /// Whether the block was successfully removed
- public static bool RemoveBlock(EGID targetBlock)
- {
- if (GameState.IsBuildMode())
- return _removalEngine.RemoveBlock(targetBlock);
- return false;
- }
-
- public static void Init()
- {
- GameEngineManager.AddGameEngine(_removalEngine);
- }
- }
-}
\ No newline at end of file
diff --git a/GamecraftModdingAPI/Blocks/Rotation.cs b/GamecraftModdingAPI/Blocks/Rotation.cs
deleted file mode 100644
index b881cdb..0000000
--- a/GamecraftModdingAPI/Blocks/Rotation.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-using Unity.Mathematics;
-
-namespace GamecraftModdingAPI.Blocks
-{
- ///
- /// Common block rotation operations.
- /// The functionality in this class is not completely implemented and will only work in build mode.
- ///
- public static class Rotation
- {
- private static RotationEngine rotationEngine = new RotationEngine();
-
- ///
- /// Rotate a single block by a specific amount in degrees.
- /// This not destroy inter-block connections, so neighbouring blocks will remain attached despite appearances.
- /// The cube placement grid and collision are also rotated.
- ///
- /// The block's id
- /// The rotation amount around the x,y,z-axis
- /// Whether the operation was successful
- public static bool RotateBlock(uint id, float3 vector)
- {
- if (rotationEngine.IsInGame && GamecraftModdingAPI.Utility.GameState.IsBuildMode())
- {
- rotationEngine.RotateBlock(id, vector);
- return true;
- }
- return false;
- }
-
- ///
- /// Rotate all connected blocks by a specific amount in degrees.
- /// This does not do anything because it has not been implemented.
- ///
- /// The starting block's id
- /// The rotation around the x,y,z-axis
- /// Whether the operation was successful
- public static bool RotateConnectedBlocks(uint id, float3 vector)
- {
- if (rotationEngine.IsInGame && GamecraftModdingAPI.Utility.GameState.IsBuildMode())
- {
- rotationEngine.RotateConnectedBlocks(id, vector);
- return true;
- }
- return false;
- }
-
- public static void Init()
- {
- GamecraftModdingAPI.Utility.GameEngineManager.AddGameEngine(rotationEngine);
- }
- }
-}
diff --git a/GamecraftModdingAPI/Blocks/RotationEngine.cs b/GamecraftModdingAPI/Blocks/RotationEngine.cs
index d56994f..b295441 100644
--- a/GamecraftModdingAPI/Blocks/RotationEngine.cs
+++ b/GamecraftModdingAPI/Blocks/RotationEngine.cs
@@ -72,14 +72,15 @@ namespace GamecraftModdingAPI.Blocks
{
Value = rotStruct.rotation
});
+ entitiesDB.QueryEntity(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP).isProcessed = false;
return ((Quaternion)rotStruct.rotation).eulerAngles;
}
- public float3 RotateConnectedBlocks(uint blockID, Vector3 vector)
+ public float3 GetRotation(uint blockID)
{
- // TODO: Implement and figure out the math
- throw new NotImplementedException();
+ ref RotationEntityStruct rotStruct = ref entitiesDB.QueryEntity(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
+ return ((Quaternion) rotStruct.rotation).eulerAngles;
}
}
}
diff --git a/GamecraftModdingAPI/Main.cs b/GamecraftModdingAPI/Main.cs
index 4dced5e..ea5911f 100644
--- a/GamecraftModdingAPI/Main.cs
+++ b/GamecraftModdingAPI/Main.cs
@@ -9,7 +9,9 @@ using HarmonyLib;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Events;
+using GamecraftModdingAPI.Players;
using GamecraftModdingAPI.Tasks;
+using uREPL;
namespace GamecraftModdingAPI
{
@@ -61,18 +63,16 @@ namespace GamecraftModdingAPI
EventManager.AddEventEmitter(GameHostTransitionDeterministicGroupEnginePatch.simEngine);
// init block implementors
Logging.MetaDebugLog($"Initializing Blocks");
- Blocks.Movement.Init();
- Blocks.Rotation.Init();
Blocks.Signals.Init();
- Blocks.Placement.Init();
Blocks.Tweakable.Init();
- Blocks.Removal.Init();
// init inventory
Inventory.Hotbar.Init();
// init input
Input.FakeInput.Init();
// init object-oriented classes
Player.Init();
+ Block.Init();
+ RuntimeCommands.Register("getblock", () => new Player(PlayerType.Local).GetBlockLookedAt());
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized");
}
diff --git a/GamecraftModdingAPI/Player.cs b/GamecraftModdingAPI/Player.cs
index 996ea01..3f11cfb 100644
--- a/GamecraftModdingAPI/Player.cs
+++ b/GamecraftModdingAPI/Player.cs
@@ -218,6 +218,18 @@ namespace GamecraftModdingAPI
}
playerEngine.SetLocation(Id, location, exitSeat: exitSeat);
}
+
+ ///
+ /// Returns the block the player is currently looking at.
+ ///
+ /// The player's ID
+ /// The entities DB
+ /// The maximum distance from the player (default is the player's building reach)
+ /// The block's EGID or null if not found
+ public Block GetBlockLookedAt(float maxDistance = -1f)
+ {
+ return playerEngine.GetBlockLookedAt(Id, maxDistance);
+ }
// internal methods
diff --git a/GamecraftModdingAPI/Players/PlayerEngine.cs b/GamecraftModdingAPI/Players/PlayerEngine.cs
index beef48a..d1df72e 100644
--- a/GamecraftModdingAPI/Players/PlayerEngine.cs
+++ b/GamecraftModdingAPI/Players/PlayerEngine.cs
@@ -11,6 +11,9 @@ using Unity.Mathematics;
using Unity.Physics;
using GamecraftModdingAPI.Engines;
+using RobocraftX.Blocks.Ghost;
+using RobocraftX.Character.Camera;
+using RobocraftX.Character.Factories;
namespace GamecraftModdingAPI.Players
{
@@ -234,5 +237,23 @@ namespace GamecraftModdingAPI.Players
s = default;
return false;
}
+
+ public Block GetBlockLookedAt(uint playerId, float maxDistance = -1f)
+ {
+ if (!entitiesDB.TryQueryMappedEntities(
+ CameraExclusiveGroups.CameraGroup, out var mapper))
+ return null;
+ mapper.TryGetEntity(playerId, out CharacterCameraRayCastEntityStruct rayCast);
+ float distance = maxDistance < 0
+ ? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast)
+ : maxDistance;
+ if (rayCast.hit && rayCast.distance <= distance)
+ {
+ Console.WriteLine("Hit block: " + rayCast.hitEgid);
+ return new Block(rayCast.hitEgid);
+ }
+
+ return null;
+ }
}
}
diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
index 80cc916..5ea4965 100644
--- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
+++ b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Reflection;
using HarmonyLib;
@@ -94,22 +95,21 @@ namespace GamecraftModdingAPI.Tests
.Build();
CommandBuilder.Builder()
- .Name("MoveLastBlock")
- .Description("Move the most-recently-placed block, and any connected blocks by the given offset")
- .Action((float x, float y, float z) => {
- bool success = GamecraftModdingAPI.Blocks.Movement.MoveConnectedBlocks(
- GamecraftModdingAPI.Blocks.BlockIdentifiers.LatestBlockID,
- new Unity.Mathematics.float3(x, y, z));
- if (!success)
- {
- GamecraftModdingAPI.Utility.Logging.CommandLogError("Blocks can only be moved in Build mode!");
- }
- }).Build();
+ .Name("MoveLastBlock")
+ .Description("Move the most-recently-placed block, and any connected blocks by the given offset")
+ .Action((float x, float y, float z) =>
+ {
+ if (GameState.IsBuildMode())
+ foreach (var block in Block.GetLastPlacedBlock().ConnectedCubes)
+ block.Position += new Unity.Mathematics.float3(x, y, z);
+ else
+ GamecraftModdingAPI.Utility.Logging.CommandLogError("Blocks can only be moved in Build mode!");
+ }).Build();
CommandBuilder.Builder()
.Name("PlaceAluminium")
.Description("Place a block of aluminium at the given coordinates")
- .Action((float x, float y, float z) => { Blocks.Placement.PlaceBlock(Blocks.BlockIDs.AluminiumCube, new Unity.Mathematics.float3(x, y, z)); })
+ .Action((float x, float y, float z) => { Block.PlaceNew(Blocks.BlockIDs.AluminiumCube, new Unity.Mathematics.float3(x, y, z)); })
.Build();
System.Random random = new System.Random(); // for command below