Add support for initializing blocks with properties

Newly created blocks use the initializer to set properties, allowing the user to set per-block properties
This commit is contained in:
Norbi Peti 2020-07-15 21:58:24 +02:00 committed by NGnius (Graham)
parent 89d32956d9
commit 7336fe8353
15 changed files with 301 additions and 338 deletions

View file

@ -55,13 +55,7 @@ namespace GamecraftModdingAPI
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null)
{
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
return new Block(PlacementEngine.PlaceBlock(block, color, darkness,
position, uscale, scale, player, rotation));
}
return null;
return PlaceNew<Block>(block, position, rotation, color, darkness, uscale, scale, player);
}
/// <summary>
@ -85,8 +79,10 @@ namespace GamecraftModdingAPI
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
var egid = PlacementEngine.PlaceBlock(block, color, darkness,
position, uscale, scale, player, rotation);
return New<T>(egid.entityID, egid.groupID);
position, uscale, scale, player, rotation, out var initializer);
var bl = New<T>(egid.entityID, egid.groupID);
bl.InitData.Group = BlockEngine.InitGroup(initializer);
return bl;
}
return null;
@ -205,6 +201,8 @@ namespace GamecraftModdingAPI
public EGID Id { get; }
internal BlockEngine.BlockInitData InitData;
/// <summary>
/// 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.
@ -236,12 +234,11 @@ namespace GamecraftModdingAPI
/// </summary>
public float3 Scale
{
get => BlockEngine.GetBlockInfo<ScalingEntityStruct>(Id).scale;
get => BlockEngine.GetBlockInfo(this, (ScalingEntityStruct st) => st.scale);
set
{
BlockEngine.SetBlockInfo(this, (ref ScalingEntityStruct st, float3 val) => st.scale = val, value);
if (!Exists) return; //UpdateCollision needs the block to exist
ref var scaling = ref BlockEngine.GetBlockInfo<ScalingEntityStruct>(Id);
scaling.scale = value;
ScalingEngine.UpdateCollision(Id);
}
}
@ -252,11 +249,11 @@ namespace GamecraftModdingAPI
/// </summary>
public int UniformScale
{
get => BlockEngine.GetBlockInfo<UniformBlockScaleEntityStruct>(Id).scaleFactor;
get => BlockEngine.GetBlockInfo(this, (UniformBlockScaleEntityStruct st) => st.scaleFactor);
set
{
ref var scaleStruct = ref BlockEngine.GetBlockInfo<UniformBlockScaleEntityStruct>(Id);
scaleStruct.scaleFactor = value;
BlockEngine.SetBlockInfo(this, (ref UniformBlockScaleEntityStruct st, int val) => st.scaleFactor = val,
value);
Scale = new float3(value, value, value);
}
}
@ -268,8 +265,7 @@ namespace GamecraftModdingAPI
{
get
{
var id = (BlockIDs) BlockEngine.GetBlockInfo<DBEntityStruct>(Id, out var exists).DBID;
return exists ? id : BlockIDs.Invalid;
return BlockEngine.GetBlockInfo(this, (DBEntityStruct st) => (BlockIDs) st.DBID, BlockIDs.Invalid);
}
}
@ -280,17 +276,19 @@ namespace GamecraftModdingAPI
{
get
{
byte index = BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id, out var exists).indexInPalette;
if (!exists) index = byte.MaxValue;
byte index = BlockEngine.GetBlockInfo(this, (ColourParameterEntityStruct st) => st.indexInPalette,
byte.MaxValue);
return new BlockColor(index);
}
set
{
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id);
color.indexInPalette = (byte)(value.Color + value.Darkness * 10);
color.overridePaletteColour = false;
color.needsUpdate = true;
BlockEngine.SetBlockColorFromPalette(ref color);
BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, BlockColor val) =>
{
color.indexInPalette = (byte) (val.Color + val.Darkness * 10);
color.overridePaletteColour = false;
color.needsUpdate = true;
BlockEngine.SetBlockColorFromPalette(ref color);
}, value);
}
}
@ -299,27 +297,31 @@ namespace GamecraftModdingAPI
/// </summary>
public float4 CustomColor
{
get => BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id).overriddenColour;
get => BlockEngine.GetBlockInfo(this, (ColourParameterEntityStruct st) => st.overriddenColour);
set
{
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id);
color.overriddenColour = value;
color.overridePaletteColour = true;
color.needsUpdate = true;
BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, float4 val) =>
{
color.overriddenColour = val;
color.overridePaletteColour = true;
color.needsUpdate = true;
}, value);
}
}
/// <summary>
/// The short text displayed on the block if applicable, or null.
/// The text displayed on the block if applicable, or null.
/// Setting it is temporary to the session, it won't be saved.
/// </summary>
public string Label
{
get => BlockEngine.GetBlockInfo<TextLabelEntityViewStruct>(Id).textLabelComponent?.text;
get => BlockEngine.GetBlockInfo(this, (TextLabelEntityViewStruct st) => st.textLabelComponent?.text);
set
{
ref var text = ref BlockEngine.GetBlockInfo<TextLabelEntityViewStruct>(Id);
if (text.textLabelComponent != null) text.textLabelComponent.text = value;
BlockEngine.SetBlockInfo(this, (ref TextLabelEntityViewStruct text, string val) =>
{
if (text.textLabelComponent != null) text.textLabelComponent.text = val;
}, value);
}
}
@ -346,8 +348,8 @@ namespace GamecraftModdingAPI
/// <returns>The SimBody of the cluster or null if the block doesn't exist.</returns>
public SimBody GetSimBody()
{
uint id = BlockEngine.GetBlockInfo<GridConnectionsEntityStruct>(Id, out var exists).machineRigidBodyId;
return exists ? new SimBody(id) : null;
return BlockEngine.GetBlockInfo(this,
(GridConnectionsEntityStruct st) => new SimBody(st.machineRigidBodyId));
}
public override string ToString()
@ -404,7 +406,9 @@ namespace GamecraftModdingAPI
// So thanks to Microsoft, we've got this horrible implementation using reflection
//Lets improve that using delegates
return New<T>(Id.entityID, Id.groupID);
var block = New<T>(Id.entityID, Id.groupID);
block.InitData = this.InitData;
return block;
}
#if DEBUG

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Gamecraft.Wires;
@ -10,14 +11,13 @@ using Svelto.DataStructures;
using Svelto.ECS;
using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Engine for executing general block actions
/// </summary>
public class BlockEngine : IApiEngine
public partial class BlockEngine : IApiEngine
{
public string Name { get; } = "GamecraftModdingAPIBlockGameEngine";
@ -61,66 +61,54 @@ namespace GamecraftModdingAPI.Blocks
color.paletteColour = paletteEntry.Colour;
}
/// <summary>
/// Get a struct of a block. Can be used to set properties.
/// Returns a default value if not found.
/// </summary>
/// <param name="blockID">The block's ID</param>
/// <typeparam name="T">The struct to query</typeparam>
/// <returns>An editable reference to the struct</returns>
public ref T GetBlockInfo<T>(EGID blockID) where T : struct, IEntityComponent
{
if (!Synced)
{
Sync();
Synced = true;
}
if (entitiesDB.Exists<T>(blockID))
return ref entitiesDB.QueryEntity<T>(blockID);
T[] structHolder = new T[1]; //Create something that can be referenced
return ref structHolder[0]; //Gets a default value automatically
}
/// <summary>
/// Get a struct of a block. Can be used to set properties.
/// Returns a default value if not found.
/// </summary>
/// <param name="blockID">The block's ID</param>
/// <param name="exists">Whether the specified struct exists for the block</param>
/// <typeparam name="T">The struct to query</typeparam>
/// <returns>An editable reference to the struct</returns>
public ref T GetBlockInfo<T>(EGID blockID, out bool exists) where T : struct, IEntityComponent
public U GetBlockInfo<T, U>(Block block, Func<T, U> getter,
U def = default) where T : struct, IEntityComponent
{
if (!Synced)
if (entitiesDB.Exists<T>(block.Id))
return getter(entitiesDB.QueryEntity<T>(block.Id));
if (block.InitData.Group == null) return def;
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
if (initializer.Has<T>())
return getter(initializer.Get<T>());
return def;
}
public delegate void Setter<T, U>(ref T component, U value) where T : struct, IEntityComponent;
public void SetBlockInfo<T, U>(Block block, Setter<T, U> setter, U value) where T : struct, IEntityComponent
{
if (entitiesDB.Exists<T>(block.Id))
setter(ref entitiesDB.QueryEntity<T>(block.Id), value);
if (block.InitData.Group != null)
{
Sync();
Synced = true;
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
ref T structRef = ref (new T[1])[0]; //A reference for a default value for struct
setter(ref structRef, value);
initializer.Init(structRef);
}
exists = entitiesDB.Exists<T>(blockID);
if (exists)
return ref entitiesDB.QueryEntity<T>(blockID);
T[] structHolder = new T[1];
return ref structHolder[0];
}
public bool BlockExists(EGID id)
public bool BlockExists(EGID blockID)
{
if (!Synced)
{
Sync();
Synced = true;
}
return entitiesDB.Exists<DBEntityStruct>(id);
return entitiesDB.Exists<DBEntityStruct>(blockID);
}
public bool GetBlockInfoExists<T>(EGID blockID) where T : struct, IEntityComponent
public bool GetBlockInfoExists<T>(Block block) where T : struct, IEntityComponent
{
if (!Synced)
{
Sync();
Synced = true;
}
return entitiesDB.Exists<T>(blockID);
if (entitiesDB.Exists<T>(block.Id))
return true;
if (block.InitData.Group == null)
return false;
var init = new EntityComponentInitializer(block.Id, block.InitData.Group);
return init.Has<T>();
}
public SimBody[] GetSimBodiesFromID(byte id)
@ -183,17 +171,6 @@ namespace GamecraftModdingAPI.Blocks
return null;
}
/// <summary>
/// Synchronize newly created entity components with entities DB.
/// This forces a partial game tick, so it may be slow.
/// This also has the potential to make Gamecraft unstable.
/// Use this sparingly.
/// </summary>
private static void Sync()
{
DeterministicStepCompositionRootPatch.SubmitEntitiesNow();
}
#if DEBUG
public EntitiesDB GetEntitiesDB()
{

View file

@ -0,0 +1,46 @@
using System;
using System.Linq.Expressions;
using Svelto.DataStructures;
using Svelto.ECS;
using Svelto.ECS.Internal;
namespace GamecraftModdingAPI.Blocks
{
public partial class BlockEngine
{
internal struct BlockInitData
{
public FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> Group;
}
internal delegate FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> GetInitGroup(
EntityComponentInitializer initializer);
internal GetInitGroup InitGroup = CreateAccessor<GetInitGroup>("_group");
//https://stackoverflow.com/questions/55878525/unit-testing-ref-structs-with-private-fields-via-reflection
internal static TDelegate CreateAccessor<TDelegate>(string memberName) where TDelegate : Delegate
{
var invokeMethod = typeof(TDelegate).GetMethod("Invoke");
if (invokeMethod == null)
throw new InvalidOperationException($"{typeof(TDelegate)} signature could not be determined.");
var delegateParameters = invokeMethod.GetParameters();
if (delegateParameters.Length != 1)
throw new InvalidOperationException("Delegate must have a single parameter.");
var paramType = delegateParameters[0].ParameterType;
var objParam = Expression.Parameter(paramType, "obj");
var memberExpr = Expression.PropertyOrField(objParam, memberName);
Expression returnExpr = memberExpr;
if (invokeMethod.ReturnType != memberExpr.Type)
returnExpr = Expression.ConvertChecked(memberExpr, invokeMethod.ReturnType);
var lambda =
Expression.Lambda<TDelegate>(returnExpr, $"Access{paramType.Name}_{memberName}", new[] {objParam});
return lambda.Compile();
}
}
}

View file

@ -1,9 +1,11 @@
using System;
using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Utility;
using RobocraftX.Common;
using Svelto.ECS;
using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class BlockEventsEngine : IReactionaryEngine<DBEntityStruct>
@ -11,11 +13,18 @@ namespace GamecraftModdingAPI.Blocks
public event EventHandler<BlockPlacedRemovedEventArgs> Placed;
public event EventHandler<BlockPlacedRemovedEventArgs> Removed;
public BlockEventsEngine()
{
//Console.WriteLine("Creating BlockEventsEngine\n" + Environment.StackTrace);
}
public void Ready()
{
//Console.WriteLine("BlockEventsEngine registered");
}
public EntitiesDB entitiesDB { get; set; }
public void Dispose()
{
}
@ -23,17 +32,52 @@ namespace GamecraftModdingAPI.Blocks
public string Name { get; } = "GamecraftModdingAPIBlockEventsEngine";
public bool isRemovable { get; } = false;
private bool shouldAddRemove;
public void Add(ref DBEntityStruct entityComponent, EGID egid)
{
ExceptionUtil.InvokeEvent(Placed, this, new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID});
if (!(shouldAddRemove = !shouldAddRemove))
return;
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});
if (!(shouldAddRemove = !shouldAddRemove))
return;
ExceptionUtil.InvokeEvent(Removed, this,
new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID});
}
}
/*[HarmonyPatch]
public static class TestPatch
{
public static void Postfix(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines,
ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid)
{
if (!engines.TryGetValue(new RefWrapper<Type>(TypeSafeDictionary<TValue>._type), out result))
return;
}
public static MethodBase TargetMethod()
{
return AccessTools.Method("Svelto.ECS.Internal.TypeSafeDictionary:AddEntityComponentToEngines");
}
}*/
/*[HarmonyPatch]
public static class TestPatch
{
public static void Postfix(EGID basePartEGID)
{
Console.WriteLine("Patched Add method: " + basePartEGID);
}
public static MethodBase TargetMethod()
{
return AccessTools.Method("RobocraftX.CR.MachineEditing.BuildBlockAdditionalPartEngine:Add");
}
}*/
public struct BlockPlacedRemovedEventArgs
{
public EGID ID;

View file

@ -6,7 +6,7 @@ namespace GamecraftModdingAPI.Blocks
public enum BlockIDs : ushort
{
/// <summary>
/// A custom value for the API. Doesn't exist for Gamecraft.
/// Called "nothing" in Gamecraft. (DBID.NOTHING)
/// </summary>
Invalid = ushort.MaxValue,
AluminiumCube = 0,

View file

@ -14,18 +14,10 @@ namespace GamecraftModdingAPI.Blocks
{
public ConsoleBlock(EGID id): base(id)
{
if (!BlockEngine.GetBlockInfoExists<ConsoleBlockEntityStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public ConsoleBlock(uint id): base(new EGID(id, CommonExclusiveGroups.BUILD_CONSOLE_BLOCK_GROUP))
{
if (!BlockEngine.GetBlockInfoExists<ConsoleBlockEntityStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom console block properties
@ -34,43 +26,47 @@ namespace GamecraftModdingAPI.Blocks
{
get
{
return BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).commandName;
return BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.commandName);
}
set
{
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).commandName.Set(value);
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.commandName.Set(val),
value);
}
}
public string Arg1
{
get => BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg1;
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg1);
set
{
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg1.Set(value);
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg1.Set(val),
value);
}
}
public string Arg2
{
get => BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg2;
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg2);
set
{
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg2.Set(value);
}
set
{
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg2.Set(val),
value);
}
}
public string Arg3
{
get => BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg3;
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg3);
set
{
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg3.Set(value);
}
set
{
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg3.Set(val),
value);
}
}
}
}

View file

@ -13,18 +13,10 @@ namespace GamecraftModdingAPI.Blocks
{
public Motor(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<MotorReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public Motor(uint id): base(new EGID(id, CommonExclusiveGroups.BUILD_MOTOR_BLOCK_GROUP))
{
if (!BlockEngine.GetBlockInfoExists<MotorReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom motor properties
@ -36,13 +28,12 @@ namespace GamecraftModdingAPI.Blocks
{
get
{
return BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id).maxVelocity;
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.maxVelocity);
}
set
{
ref MotorReadOnlyStruct motor = ref BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id);
motor.maxVelocity = value;
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, float val) => st.maxVelocity = val, value);
}
}
@ -53,13 +44,12 @@ namespace GamecraftModdingAPI.Blocks
{
get
{
return BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id).maxForce;
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.maxForce);
}
set
{
ref MotorReadOnlyStruct motor = ref BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id);
motor.maxForce = value;
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, float val) => st.maxForce = val, value);
}
}
@ -70,13 +60,12 @@ namespace GamecraftModdingAPI.Blocks
{
get
{
return BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id).reverse;
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.reverse);
}
set
{
ref MotorReadOnlyStruct motor = ref BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id);
motor.reverse = value;
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, bool val) => st.reverse = val, value);
}
}
}

View file

@ -8,27 +8,22 @@ namespace GamecraftModdingAPI.Blocks
{
public ObjectIdentifier(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<ObjectIdEntityStruct>(Id))
{
throw new BlockTypeException($"Block is not a {GetType().Name} block");
}
}
public ObjectIdentifier(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP))
{
if (!BlockEngine.GetBlockInfoExists<ObjectIdEntityStruct>(Id))
{
throw new BlockTypeException($"Block is not a {GetType().Name} block");
}
}
public char Identifier
{
get => (char) (BlockEngine.GetBlockInfo<ObjectIdEntityStruct>(Id).objectId + 'A');
get => (char) BlockEngine.GetBlockInfo(this, (ObjectIdEntityStruct st) => st.objectId + 'A');
set
{
BlockEngine.GetBlockInfo<ObjectIdEntityStruct>(Id).objectId = (byte) (value - 'A');
Label = value + ""; //The label isn't updated automatically
BlockEngine.SetBlockInfo(this, (ref ObjectIdEntityStruct st, char val) =>
{
st.objectId = (byte) (val - 'A');
Label = val + ""; //The label isn't updated automatically
}, value);
}
}
@ -37,7 +32,7 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public byte SimID
{
get => BlockEngine.GetBlockInfo<ObjectIdEntityStruct>(Id).simObjectId;
get => BlockEngine.GetBlockInfo(this, (ObjectIdEntityStruct st) => st.simObjectId);
}
/// <summary>

View file

@ -13,18 +13,10 @@ namespace GamecraftModdingAPI.Blocks
{
public Piston(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<PistonReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public Piston(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_PISTON_BLOCK_GROUP))
{
if (!BlockEngine.GetBlockInfoExists<PistonReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom piston properties
@ -33,13 +25,13 @@ namespace GamecraftModdingAPI.Blocks
/// The piston's max extension distance.
/// </summary>
public float MaximumExtension
{
get => BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id).maxDeviation;
{
get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.maxDeviation);
set
{
ref PistonReadOnlyStruct piston = ref BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id);
piston.maxDeviation = value;
BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.maxDeviation = val,
value);
}
}
@ -47,13 +39,12 @@ namespace GamecraftModdingAPI.Blocks
/// The piston's max extension force.
/// </summary>
public float MaximumForce
{
get => BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id).maxForce;
{
get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.maxForce);
set
{
ref PistonReadOnlyStruct piston = ref BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id);
piston.maxForce = value;
BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.maxForce = val, value);
}
}
}

View file

@ -40,15 +40,16 @@ 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, Player player, float3 rotation)
float3 scale, Player player, float3 rotation, out EntityComponentInitializer initializer)
{ //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,
initializer = BuildBlock((ushort) block, (byte) (color + darkness * 10), position, uscale, scale, rotation,
(player ?? new Player(PlayerType.Local)).Id);
return initializer.EGID;
}
private EGID BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot, uint playerId)
private EntityComponentInitializer BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot, uint playerId)
{
if (_blockEntityFactory == null)
throw new Exception("The factory is null.");
@ -107,7 +108,7 @@ namespace GamecraftModdingAPI.Blocks
pickedBlock.placedBlockEntityID = structInitializer.EGID;
pickedBlock.placedBlockWasAPickedBlock = false;
Block.BlockEngine.Synced = false; // Block entities will need to be submitted before properties can be used
return structInitializer.EGID;
return structInitializer;
}
public string Name { get; } = "GamecraftModdingAPIPlacementGameEngine";

View file

@ -13,18 +13,10 @@ namespace GamecraftModdingAPI.Blocks
{
public Servo(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<ServoReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public Servo(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_SERVO_BLOCK_GROUP))
{
if (!BlockEngine.GetBlockInfoExists<ServoReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom servo properties
@ -33,13 +25,12 @@ namespace GamecraftModdingAPI.Blocks
/// The servo's minimum angle.
/// </summary>
public float MinimumAngle
{
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).minDeviation;
{
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.minDeviation);
set
{
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
servo.minDeviation = value;
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.minDeviation = val, value);
}
}
@ -48,13 +39,12 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public float MaximumAngle
{
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).maxDeviation;
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.maxDeviation);
set
{
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
servo.maxDeviation = value;
}
set
{
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.maxDeviation = val, value);
}
}
/// <summary>
@ -62,13 +52,12 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public float MaximumForce
{
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).maxForce;
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.maxForce);
set
{
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
servo.maxForce = value;
}
set
{
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.maxForce = val, value);
}
}
/// <summary>
@ -76,13 +65,12 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public bool Reverse
{
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).reverse;
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.reverse);
set
{
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
servo.reverse = value;
}
set
{
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, bool val) => st.reverse = val, value);
}
}
}
}

View file

@ -16,7 +16,7 @@ namespace GamecraftModdingAPI.Blocks
{
public SignalingBlock(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<BlockPortsStruct>(this.Id))
if (!BlockEngine.GetBlockInfoExists<BlockPortsStruct>(this))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
@ -24,17 +24,12 @@ namespace GamecraftModdingAPI.Blocks
public SignalingBlock(uint id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<BlockPortsStruct>(this.Id))
if (!BlockEngine.GetBlockInfoExists<BlockPortsStruct>(this))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
protected ref BlockPortsStruct GetBlockPortsStruct()
{
return ref BlockEngine.GetBlockInfo<BlockPortsStruct>(Id);
}
/// <summary>
/// Generates the input port identifiers.
/// </summary>
@ -53,16 +48,6 @@ namespace GamecraftModdingAPI.Blocks
return SignalEngine.GetSignalOutputs(Id);
}
/// <summary>
/// Gets the port struct.
/// </summary>
/// <returns>The port struct.</returns>
/// <param name="portId">Port identifier.</param>
protected ref PortEntityStruct GetPortStruct(EGID portId)
{
return ref BlockEngine.GetBlockInfo<PortEntityStruct>(portId);
}
/// <summary>
/// Gets the connected wire.
/// </summary>
@ -89,16 +74,16 @@ namespace GamecraftModdingAPI.Blocks
/// The input port count.
/// </summary>
public uint InputCount
{
get => GetBlockPortsStruct().inputCount;
}
{
get => BlockEngine.GetBlockInfo(this, (BlockPortsStruct st) => st.inputCount);
}
/// <summary>
/// The output port count.
/// </summary>
public uint OutputCount
{
get => GetBlockPortsStruct().outputCount;
get => BlockEngine.GetBlockInfo(this, (BlockPortsStruct st) => st.outputCount);
}
}
}

View file

@ -15,18 +15,10 @@ namespace GamecraftModdingAPI.Blocks
{
public SpawnPoint(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<SpawnPointStatsEntityStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public SpawnPoint(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_SPAWNPOINT_BLOCK_GROUP))
{
if (!BlockEngine.GetBlockInfoExists<SpawnPointStatsEntityStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom spawn point properties
@ -36,16 +28,12 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public uint Lives
{
get
{
return BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id).lives;
}
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.lives);
set
{
ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id);
spses.lives = value;
}
set
{
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, uint val) => st.lives = val, value);
}
}
/// <summary>
@ -53,16 +41,12 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public bool Damageable
{
get
{
return BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id).canTakeDamage;
}
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.canTakeDamage);
set
{
ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id);
spses.canTakeDamage = value;
}
set
{
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, bool val) => st.canTakeDamage = val, value);
}
}
/// <summary>
@ -70,16 +54,12 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public bool GameOverEnabled
{
get
{
return BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id).gameOverScreen;
}
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.gameOverScreen);
set
{
ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id);
spses.gameOverScreen = value;
}
set
{
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, bool val) => st.gameOverScreen = val, value);
}
}
/// <summary>
@ -87,16 +67,12 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public byte Team
{
get
{
return BlockEngine.GetBlockInfo<SpawnPointIdsEntityStruct>(Id).teamId;
}
get => BlockEngine.GetBlockInfo(this, (SpawnPointIdsEntityStruct st) => st.teamId);
set
{
ref SpawnPointIdsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointIdsEntityStruct>(Id);
spses.teamId = value;
}
set
{
BlockEngine.SetBlockInfo(this, (ref SpawnPointIdsEntityStruct st, byte val) => st.teamId = val, value);
}
}
}
}

View file

@ -14,18 +14,10 @@ namespace GamecraftModdingAPI.Blocks
{
public TextBlock(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<TextBlockDataStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public TextBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_TEXT_BLOCK_GROUP))
{
if (!BlockEngine.GetBlockInfoExists<TextBlockDataStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom text block properties
@ -35,35 +27,34 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public string Text
{
get
{
return BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id).textCurrent;
}
get => BlockEngine.GetBlockInfo(this, (TextBlockDataStruct st) => st.textCurrent);
set
{
ref TextBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id);
tbds.textCurrent.Set(value);
tbds.textStored.Set(value);
BlockEngine.GetBlockInfo<TextBlockNetworkDataStruct>(Id).newTextBlockStringContent.Set(value);
}
}
set
{
BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) =>
{
tbds.textCurrent.Set(val);
tbds.textStored.Set(val);
}, value);
BlockEngine.SetBlockInfo(this,
(ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockStringContent.Set(val), value);
}
}
/// <summary>
/// The text block's current text block ID (used in ChangeTextBlockCommand).
/// The text block's current text block ID (used in ChangeTextBlockCommand).
/// </summary>
public string TextBlockId
public string TextBlockId
{
get
{
return BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id).textBlockID;
}
get => BlockEngine.GetBlockInfo(this, (TextBlockDataStruct st) => st.textBlockID);
set
{
BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id).textBlockID.Set(value);
BlockEngine.GetBlockInfo<TextBlockNetworkDataStruct>(Id).newTextBlockID.Set(value);
}
set
{
BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) =>
tbds.textBlockID.Set(val), value);
BlockEngine.SetBlockInfo(this,
(ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockID.Set(val), value);
}
}
}
}

View file

@ -15,18 +15,10 @@ namespace GamecraftModdingAPI.Blocks
{
public Timer(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<TimerBlockDataStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public Timer(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_TIMER_BLOCK_GROUP))
{
if (!BlockEngine.GetBlockInfoExists<TimerBlockDataStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom timer properties
@ -36,16 +28,13 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public float Start
{
get
{
return BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id).startTime;
}
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.startTime);
set
{
ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id);
tbds.startTime = value;
}
set
{
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, float val) => tbds.startTime = val,
value);
}
}
/// <summary>
@ -53,16 +42,13 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public float End
{
get
{
return BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id).endTime;
}
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.endTime);
set
{
ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id);
tbds.endTime = value;
}
set
{
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, float val) => tbds.endTime = val,
value);
}
}
/// <summary>
@ -70,16 +56,13 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public bool DisplayMilliseconds
{
get
{
return BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id).outputFormatHasMS;
}
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.outputFormatHasMS);
set
{
ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id);
tbds.outputFormatHasMS = value;
}
set
{
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, bool val) => tbds.outputFormatHasMS = val,
value);
}
}
/// <summary>
@ -87,16 +70,13 @@ namespace GamecraftModdingAPI.Blocks
/// </summary>
public int CurrentTime
{
get
{
return BlockEngine.GetBlockInfo<TimerBlockLabelCacheEntityStruct>(Id).timeLastRenderFrameMS;
}
get => BlockEngine.GetBlockInfo(this, (TimerBlockLabelCacheEntityStruct st) => st.timeLastRenderFrameMS);
set
{
ref TimerBlockLabelCacheEntityStruct tblces = ref BlockEngine.GetBlockInfo<TimerBlockLabelCacheEntityStruct>(Id);
tblces.timeLastRenderFrameMS = value;
}
set
{
BlockEngine.SetBlockInfo(this, (ref TimerBlockLabelCacheEntityStruct tbds, int val) => tbds.timeLastRenderFrameMS = val,
value);
}
}
}
}