diff --git a/GamecraftModdingAPI/Block.cs b/GamecraftModdingAPI/Block.cs
index 271ca1c..4768a57 100644
--- a/GamecraftModdingAPI/Block.cs
+++ b/GamecraftModdingAPI/Block.cs
@@ -23,15 +23,14 @@ namespace GamecraftModdingAPI
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();
protected static readonly SignalEngine SignalEngine = new SignalEngine();
+
+ protected internal 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.
- ///
- /// 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
@@ -48,15 +47,8 @@ namespace GamecraftModdingAPI
{
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 new Block(PlacementEngine.PlaceBlock(block, color, darkness,
+ position, uscale, scale, player, rotation));
}
return null;
@@ -122,17 +114,8 @@ namespace GamecraftModdingAPI
}
}
- public Block(uint id)
+ public Block(uint id) : this(new EGID(id, CommonExclusiveGroups.OWNED_BLOCKS_GROUP))
{
- Id = new EGID(id, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
- 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");
- }
- }
}
///
@@ -264,6 +247,17 @@ namespace GamecraftModdingAPI
/// True if the block exists and could be removed.
public bool Remove() => RemovalEngine.RemoveBlock(Id);
+ ///
+ /// 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.
+ ///
+ ///
+ public SimBody ToSimBody()
+ {
+ uint id = BlockEngine.GetBlockInfo(Id).machineRigidBodyId;
+ return new SimBody(id);
+ }
+
public override string ToString()
{
return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Rotation)}: {Rotation}";
diff --git a/GamecraftModdingAPI/Blocks/ConsoleBlock.cs b/GamecraftModdingAPI/Blocks/ConsoleBlock.cs
index da9c895..173b3f6 100644
--- a/GamecraftModdingAPI/Blocks/ConsoleBlock.cs
+++ b/GamecraftModdingAPI/Blocks/ConsoleBlock.cs
@@ -15,21 +15,14 @@ namespace GamecraftModdingAPI.Blocks
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);
- return new ConsoleBlock(id);
- }
- catch (Exception e)
- {
- Logging.MetaDebugLog(e);
- }
- }
+ if (PlacementEngine.IsInGame && GameState.IsBuildMode())
+ {
+ EGID id = PlacementEngine.PlaceBlock(BlockIDs.ConsoleBlock, color, darkness,
+ position, uscale, scale, player, rotation);
+ return new ConsoleBlock(id);
+ }
- return null;
+ return null;
}
public ConsoleBlock(EGID id): base(id)
diff --git a/GamecraftModdingAPI/Blocks/Motor.cs b/GamecraftModdingAPI/Blocks/Motor.cs
index 8cafccb..c900f10 100644
--- a/GamecraftModdingAPI/Blocks/Motor.cs
+++ b/GamecraftModdingAPI/Blocks/Motor.cs
@@ -25,16 +25,9 @@ namespace GamecraftModdingAPI.Blocks
}
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
- try
- {
- EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
- position, uscale, scale, player, rotation);
- return new Motor(id);
- }
- catch (Exception e)
- {
- Logging.MetaDebugLog(e);
- }
+ EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
+ position, uscale, scale, player, rotation);
+ return new Motor(id);
}
return null;
diff --git a/GamecraftModdingAPI/Blocks/Piston.cs b/GamecraftModdingAPI/Blocks/Piston.cs
index 2f7064c..00ad273 100644
--- a/GamecraftModdingAPI/Blocks/Piston.cs
+++ b/GamecraftModdingAPI/Blocks/Piston.cs
@@ -25,16 +25,9 @@ namespace GamecraftModdingAPI.Blocks
}
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
- try
- {
- EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
- position, uscale, scale, player, rotation);
- return new Piston(id);
- }
- catch (Exception e)
- {
- Logging.MetaDebugLog(e);
- }
+ EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
+ position, uscale, scale, player, rotation);
+ return new Piston(id);
}
return null;
diff --git a/GamecraftModdingAPI/Blocks/Servo.cs b/GamecraftModdingAPI/Blocks/Servo.cs
index dd2cc52..9a66e3b 100644
--- a/GamecraftModdingAPI/Blocks/Servo.cs
+++ b/GamecraftModdingAPI/Blocks/Servo.cs
@@ -21,20 +21,13 @@ namespace GamecraftModdingAPI.Blocks
{
if (!(block == BlockIDs.ServoAxle || block == BlockIDs.ServoHinge || block == BlockIDs.ServoPiston))
{
- throw new BlockTypeException($"Block is not a {typeof(Servo).Name} block");
+ throw new BlockTypeException($"Block is not a {nameof(Servo)} block");
}
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
- try
- {
- EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
- position, uscale, scale, player, rotation);
- return new Servo(id);
- }
- catch (Exception e)
- {
- Logging.MetaDebugLog(e);
- }
+ EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
+ position, uscale, scale, player, rotation);
+ return new Servo(id);
}
return null;
diff --git a/GamecraftModdingAPI/Blocks/SignalingBlock.cs b/GamecraftModdingAPI/Blocks/SignalingBlock.cs
index 9c2f457..f8006f6 100644
--- a/GamecraftModdingAPI/Blocks/SignalingBlock.cs
+++ b/GamecraftModdingAPI/Blocks/SignalingBlock.cs
@@ -25,16 +25,9 @@ namespace GamecraftModdingAPI.Blocks
{
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
- try
- {
- EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
- position, uscale, scale, player, rotation);
- return new SignalingBlock(id);
- }
- catch (Exception e)
- {
- Logging.MetaDebugLog(e);
- }
+ EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
+ position, uscale, scale, player, rotation);
+ return new SignalingBlock(id);
}
return null;
diff --git a/GamecraftModdingAPI/Blocks/SpawnPoint.cs b/GamecraftModdingAPI/Blocks/SpawnPoint.cs
index b43dffb..bb3e7f4 100644
--- a/GamecraftModdingAPI/Blocks/SpawnPoint.cs
+++ b/GamecraftModdingAPI/Blocks/SpawnPoint.cs
@@ -23,20 +23,13 @@ namespace GamecraftModdingAPI.Blocks
{
if (!(block == BlockIDs.LargeSpawn || block == BlockIDs.SmallSpawn || block == BlockIDs.MediumSpawn || block == BlockIDs.PlayerSpawn))
{
- throw new BlockTypeException($"Block is not a {typeof(SpawnPoint).Name} block");
+ throw new BlockTypeException($"Block is not a {nameof(SpawnPoint)} block");
}
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
- try
- {
- EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
- position, uscale, scale, player, rotation);
+ EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
+ position, uscale, scale, player, rotation);
return new SpawnPoint(id);
- }
- catch (Exception e)
- {
- Logging.MetaDebugLog(e);
- }
}
return null;
diff --git a/GamecraftModdingAPI/Blocks/TextBlock.cs b/GamecraftModdingAPI/Blocks/TextBlock.cs
index f2993fb..92f8116 100644
--- a/GamecraftModdingAPI/Blocks/TextBlock.cs
+++ b/GamecraftModdingAPI/Blocks/TextBlock.cs
@@ -18,16 +18,9 @@ namespace GamecraftModdingAPI.Blocks
{
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
- try
- {
- EGID id = PlacementEngine.PlaceBlock(BlockIDs.TextBlock, color, darkness,
- position, uscale, scale, player, rotation);
- return new TextBlock(id);
- }
- catch (Exception e)
- {
- Logging.MetaDebugLog(e);
- }
+ EGID id = PlacementEngine.PlaceBlock(BlockIDs.TextBlock, color, darkness,
+ position, uscale, scale, player, rotation);
+ return new TextBlock(id);
}
return null;
diff --git a/GamecraftModdingAPI/Blocks/Timer.cs b/GamecraftModdingAPI/Blocks/Timer.cs
index a53adf1..d26620e 100644
--- a/GamecraftModdingAPI/Blocks/Timer.cs
+++ b/GamecraftModdingAPI/Blocks/Timer.cs
@@ -21,17 +21,9 @@ namespace GamecraftModdingAPI.Blocks
{
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
-
- try
- {
- EGID id = PlacementEngine.PlaceBlock(BlockIDs.Timer, color, darkness,
- position, uscale, scale, player, rotation);
- return new Timer(id);
- }
- catch (Exception e)
- {
- Logging.MetaDebugLog(e);
- }
+ EGID id = PlacementEngine.PlaceBlock(BlockIDs.Timer, color, darkness,
+ position, uscale, scale, player, rotation);
+ return new Timer(id);
}
return null;
diff --git a/GamecraftModdingAPI/Player.cs b/GamecraftModdingAPI/Player.cs
index 965ba45..0b7a134 100644
--- a/GamecraftModdingAPI/Player.cs
+++ b/GamecraftModdingAPI/Player.cs
@@ -1,6 +1,7 @@
using System;
using Unity.Mathematics;
+using RobocraftX.Common;
using GamecraftModdingAPI.Players;
@@ -221,13 +222,27 @@ namespace GamecraftModdingAPI
///
/// 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
+ /// The block or null if not found
public Block GetBlockLookedAt(float maxDistance = -1f)
{
- return playerEngine.GetBlockLookedAt(Id, maxDistance);
+ var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
+ return egid.HasValue && egid.Value.groupID == CommonExclusiveGroups.OWNED_BLOCKS_GROUP
+ ? new Block(egid.Value)
+ : null;
+ }
+
+ ///
+ /// Returns the rigid body the player is currently looking at during simulation.
+ ///
+ /// The maximum distance from the player (default is the player's building reach)
+ /// The block or null if not found
+ public SimBody GetSimBodyLookedAt(float maxDistance = -1f)
+ {
+ var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
+ return egid.HasValue && egid.Value.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP
+ ? new SimBody(egid.Value)
+ : null;
}
// internal methods
diff --git a/GamecraftModdingAPI/Players/PlayerEngine.cs b/GamecraftModdingAPI/Players/PlayerEngine.cs
index f8e53f0..d2c8fa3 100644
--- a/GamecraftModdingAPI/Players/PlayerEngine.cs
+++ b/GamecraftModdingAPI/Players/PlayerEngine.cs
@@ -238,7 +238,7 @@ namespace GamecraftModdingAPI.Players
return false;
}
- public Block GetBlockLookedAt(uint playerId, float maxDistance = -1f)
+ public EGID? GetThingLookedAt(uint playerId, float maxDistance = -1f)
{
if (!entitiesDB.TryQueryMappedEntities(
CameraExclusiveGroups.CameraGroup, out var mapper))
@@ -248,7 +248,22 @@ namespace GamecraftModdingAPI.Players
? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast)
: maxDistance;
if (rayCast.hit && rayCast.distance <= distance)
- return new Block(rayCast.hitEgid);
+ return rayCast.hitEgid;
+ /*if (rayCast.hit)
+ {
+ *Logging.MetaDebugLog("RayCast EGID: " + rayCast.hitEgid);
+ var d = AccessTools.Field(typeof(ExclusiveGroup), "_knownGroups").GetValue(null) as
+ Dictionary;
+ foreach (var groupStruct in d)
+ {
+ if (groupStruct.Value == rayCast.hitEgid.groupID)
+ {
+ Logging.MetaDebugLog("Group name: " + groupStruct.Key);
+ break; //SIMULATION_BODIES_GROUP
+ }
+ }*
+ //Logging.MetaDebugLog("Position: " + Block.GetBlockPositionTest(rayCast.hitEgid));
+ }*/
return null;
}
diff --git a/GamecraftModdingAPI/SimBody.cs b/GamecraftModdingAPI/SimBody.cs
new file mode 100644
index 0000000..e6d1c68
--- /dev/null
+++ b/GamecraftModdingAPI/SimBody.cs
@@ -0,0 +1,88 @@
+using RobocraftX.Common;
+using RobocraftX.Physics;
+using Svelto.ECS;
+using Unity.Mathematics;
+using Unity.Physics;
+using UnityEngine;
+
+namespace GamecraftModdingAPI
+{
+ ///
+ /// A rigid body (like a cluster of connected blocks) during simulation.
+ ///
+ public class SimBody
+ {
+ public EGID Id { get; }
+
+ public SimBody(EGID id)
+ {
+ Id = id;
+ }
+
+ public SimBody(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_BODIES_GROUP))
+ {
+ }
+
+ public float3 Position
+ {
+ get => GetStruct().position;
+ set => GetStruct().position = value;
+ }
+
+ public float3 Velocity
+ {
+ get => GetStruct().velocity;
+ set => GetStruct().velocity = value;
+ }
+
+ public float3 AngularVelocity
+ {
+ get => GetStruct().angularVelocity;
+ set => GetStruct().angularVelocity = value;
+ }
+
+ public float3 DeltaVelocity
+ {
+ get => GetStruct().deltaVelocity;
+ set => GetStruct().deltaVelocity = value;
+ }
+
+ public float3 DeltaAngularVelocity
+ {
+ get => GetStruct().deltaAngularVelocity;
+ set => GetStruct().deltaAngularVelocity = value;
+ }
+
+ public float3 Rotation
+ {
+ get => ((Quaternion) GetStruct().rotation).eulerAngles;
+ set
+ {
+ ref var str = ref GetStruct();
+ Quaternion quaternion = str.rotation;
+ quaternion.eulerAngles = value;
+ str.rotation = quaternion;
+ }
+ }
+
+ public float Mass
+ {
+ get => math.rcp(GetStruct().physicsMass.InverseMass);
+ set => GetStruct().physicsMass.InverseMass = math.rcp(value);
+ }
+
+ ///
+ /// Whether the body can be moved or static
+ ///
+ public bool Static
+ {
+ get => Block.BlockEngine.GetBlockInfo(Id).isStatic;
+ set => Block.BlockEngine.GetBlockInfo(Id).isStatic = value;
+ }
+
+ private ref RigidBodyEntityStruct GetStruct()
+ {
+ return ref Block.BlockEngine.GetBlockInfo(Id);
+ }
+ }
+}
\ No newline at end of file
diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
index 4c9105c..b662dd7 100644
--- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
+++ b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
@@ -1,8 +1,11 @@
using System;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
+using System.Text;
using HarmonyLib;
+using IllusionInjector;
// test
using Svelto.ECS;
using RobocraftX.Blocks;
@@ -143,15 +146,46 @@ namespace GamecraftModdingAPI.Tests
CommandBuilder.Builder()
.Name("PlaceAluminium")
.Description("Place a block of aluminium at the given coordinates")
- .Action(async (float x, float y, float z) =>
+ .Action((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);
+ var block = Block.PlaceNew(BlockIDs.AluminiumCube, new float3(x, y, z));
+ Logging.CommandLog("Block placed with type: " + block.Type);
})
.Build();
- CommandBuilder.Builder("getBlock")
- .Action(() => uREPL.Log.Output(new Player(Players.PlayerType.Local).GetBlockLookedAt()+"")).Build();
+ CommandBuilder.Builder()
+ .Name("PlaceAluminiumLots")
+ .Description("Place a lot of blocks of aluminium at the given coordinates")
+ .Action((float x, float y, float z) =>
+ {
+ Logging.CommandLog("Starting...");
+ var sw = Stopwatch.StartNew();
+ for (int i = 0; i < 100; i++)
+ for (int j = 0; j < 100; j++)
+ Block.PlaceNew(BlockIDs.AluminiumCube, new float3(x + i, y, z + j));
+ //Block.Sync();
+ sw.Stop();
+ Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms");
+ })
+ .Build();
+ //With Sync(): 1135ms
+ //Without Sync(): 134ms
+ //Async: 348 794ms, doesn't freeze game
+ //Without Sync() but wait for submission: 530ms
+ //With Sync() at the end: 380ms
+
+ Block b = null;
+ CommandBuilder.Builder("moveBlockInSim", "Run in build mode first, then in sim while looking at a block to move it up")
+ .Action(() =>
+ {
+ if (b == null)
+ {
+ b = new Player(PlayerType.Local).GetBlockLookedAt();
+ Logging.CommandLog("Block saved: " + b);
+ }
+ else
+ Logging.CommandLog("Block moved to: " + (b.ToSimBody().Position += new float3(0, 2, 0)));
+ }).Build();
CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.")
.Action(() => { throw new Exception("Error Command always throws an error"); })
@@ -176,6 +210,7 @@ namespace GamecraftModdingAPI.Tests
}).Build();
GameClient.SetDebugInfo("lookedAt", LookedAt);
+ GameClient.SetDebugInfo("InstalledMods", InstalledMods);
/*
CommandManager.AddCommand(new SimpleCustomCommandEngine((float d) => { UnityEngine.Camera.main.fieldOfView = d; },
@@ -237,10 +272,35 @@ namespace GamecraftModdingAPI.Tests
private string LookedAt()
{
if (player == null)
- player = new Player(Players.PlayerType.Local);
- Block block = player.GetBlockLookedAt();
- if (block == null) return "Block: none";
- return "Block: " + block.Type + "\nColor: " + block.Color + "\n" + "At: " + block.Position;
+ player = new Player(PlayerType.Local);
+ if (GameState.IsBuildMode())
+ {
+ Block block = player.GetBlockLookedAt();
+ if (block == null) return "Block: none";
+ return "Block: " + block.Type + "\nColor: " + block.Color + "\n" + "At: " + block.Position;
+ }
+ if (GameState.IsSimulationMode())
+ {
+ SimBody body = player.GetSimBodyLookedAt();
+ if (body == null) return "Body: none";
+ return "Body: " + (body.Static ? "static" : "non-static")
+ + "\nAt: " + body.Position + " - rotated: " + body.Rotation
+ + "\nWith mass: " + body.Mass
+ + "\nVelocity: " + body.Velocity + " - angular: " + body.AngularVelocity
+ + "\nDelta velocity: " + body.DeltaVelocity + " - angular: " + body.DeltaAngularVelocity;
+ }
+
+ return "Switching modes...";
+ }
+
+ private string modsString;
+ private string InstalledMods()
+ {
+ if (modsString != null) return modsString;
+ StringBuilder sb = new StringBuilder("Installed mods:");
+ foreach (var plugin in PluginManager.Plugins)
+ sb.Append("\n" + plugin);
+ return modsString = sb.ToString();
}
public void OnFixedUpdate() { }
diff --git a/GamecraftModdingAPI/Utility/DebugInterfaceEngine.cs b/GamecraftModdingAPI/Utility/DebugInterfaceEngine.cs
index b8b0222..c604c34 100644
--- a/GamecraftModdingAPI/Utility/DebugInterfaceEngine.cs
+++ b/GamecraftModdingAPI/Utility/DebugInterfaceEngine.cs
@@ -52,7 +52,7 @@ namespace GamecraftModdingAPI.Utility
}
catch (Exception e)
{
- Logging.LogException(e, "Failed to inject AddInfo method for the debug display!");
+ Logging.LogWarning("Failed to inject AddInfo method for the debug display!\n" + e);
}
return list;
@@ -68,7 +68,7 @@ namespace GamecraftModdingAPI.Utility
}
catch (Exception e)
{
- Logging.LogException(e, "Unable to get info for " + info.Key);
+ Logging.LogWarning("Unable to get info for " + info.Key + "\n" + e);
}
}
}
diff --git a/GamecraftModdingAPI/Utility/Logging.cs b/GamecraftModdingAPI/Utility/Logging.cs
index 20c17cf..80ef323 100644
--- a/GamecraftModdingAPI/Utility/Logging.cs
+++ b/GamecraftModdingAPI/Utility/Logging.cs
@@ -83,7 +83,7 @@ namespace GamecraftModdingAPI.Utility
}
///
- /// Write an exception to Gamecraft's log
+ /// Write an exception to Gamecraft's log and to the screen and exit game
///
/// The exception to log
/// The extra data to pass to the ILogger.
@@ -95,7 +95,7 @@ namespace GamecraftModdingAPI.Utility
}
///
- /// Write an exception message to Gamecraft's log
+ /// Write an exception message to Gamecraft's log and to the screen and exit game
///
/// The object to log
/// The exception to log