Added extension methods to query data from ECS objects Added base class for ECS objects Added support for representing in-construction ECS objects with an OptionalRef<T>tags/v2.0.0
@@ -20,7 +20,7 @@ namespace TechbloxModdingAPI | |||
/// A single (perhaps scaled) block. Properties may return default values if the block is removed and then setting them is ignored. | |||
/// For specific block type operations, use the specialised block classes in the TechbloxModdingAPI.Blocks namespace. | |||
/// </summary> | |||
public class Block : IEquatable<Block>, IEquatable<EGID> | |||
public class Block : EcsObjectBase, IEquatable<Block>, IEquatable<EGID> | |||
{ | |||
protected static readonly PlacementEngine PlacementEngine = new PlacementEngine(); | |||
protected static readonly MovementEngine MovementEngine = new MovementEngine(); | |||
@@ -78,8 +78,7 @@ namespace TechbloxModdingAPI | |||
var initializer = PlacementEngine.PlaceBlock(block, position, player, autoWire); | |||
var egid = initializer.EGID; | |||
var bl = New<T>(egid.entityID, egid.groupID); | |||
bl.InitData.Group = BlockEngine.InitGroup(initializer); | |||
bl.InitData.Reference = initializer.reference; | |||
bl.InitData = initializer; | |||
Placed += bl.OnPlacedInit; | |||
return bl; | |||
} | |||
@@ -241,13 +240,12 @@ namespace TechbloxModdingAPI | |||
throw new BlockException("Blocks can only be placed in build mode."); | |||
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire); | |||
Id = initializer.EGID; | |||
InitData.Group = BlockEngine.InitGroup(initializer); | |||
InitData = initializer; | |||
Placed += OnPlacedInit; | |||
} | |||
public EGID Id { get; } | |||
public override EGID Id { get; } | |||
internal BlockEngine.BlockInitData InitData; | |||
private EGID copiedFrom; | |||
/// <summary> | |||
@@ -9,6 +9,7 @@ using RobocraftX.Blocks; | |||
using RobocraftX.Common; | |||
using RobocraftX.Physics; | |||
using RobocraftX.Rendering; | |||
using RobocraftX.Rendering.GPUI; | |||
using Svelto.ECS.EntityStructs; | |||
using Svelto.DataStructures; | |||
@@ -16,6 +17,7 @@ using Svelto.ECS; | |||
using Svelto.ECS.Hybrid; | |||
using Unity.Mathematics; | |||
using TechbloxModdingAPI.Engines; | |||
using TechbloxModdingAPI.Utility; | |||
namespace TechbloxModdingAPI.Blocks | |||
{ | |||
@@ -68,14 +70,13 @@ namespace TechbloxModdingAPI.Blocks | |||
: entitiesDB.QueryEntity<PaletteEntryEntityStruct>(index, | |||
CommonExclusiveGroups.COLOUR_PALETTE_GROUP).Colour; | |||
public ref T GetBlockInfo<T>(EGID blockID) where T : unmanaged, IEntityComponent | |||
public OptionalRef<T> GetBlockInfo<T>(EGID blockID) where T : unmanaged, IEntityComponent | |||
{ | |||
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 | |||
return entitiesDB.TryQueryEntitiesAndIndex<T>(blockID, out uint index, out var array) | |||
? new OptionalRef<T>(array, index) | |||
: new OptionalRef<T>(); | |||
} | |||
public ref T GetBlockInfoViewStruct<T>(EGID blockID) where T : struct, INeedEGID, IEntityViewComponent | |||
{ | |||
if (entitiesDB.Exists<T>(blockID)) | |||
@@ -149,6 +150,12 @@ namespace TechbloxModdingAPI.Blocks | |||
entitiesDB.QueryEntity<RenderingDataStruct>(id).matrix = float4x4.TRS(pos.position, rot.rotation, scale.scale); | |||
} | |||
internal void UpdatePrefab(Block block, ushort type, byte material, bool flipped) | |||
{ | |||
uint pid = PrefabsID.GetOrCreatePrefabID(type, material, 0, flipped); | |||
entitiesDB.QueryEntityOrDefault<GFXPrefabEntityStructGPUI>() | |||
} | |||
public bool BlockExists(EGID blockID) | |||
{ | |||
return entitiesDB.Exists<DBEntityStruct>(blockID); | |||
@@ -1,33 +1,43 @@ | |||
using System; | |||
using System; | |||
using System.Linq.Expressions; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS; | |||
using Svelto.ECS.Internal; | |||
using TechbloxModdingAPI.Blocks; | |||
namespace TechbloxModdingAPI.Blocks | |||
namespace TechbloxModdingAPI | |||
{ | |||
public partial class BlockEngine | |||
public abstract class EcsObjectBase | |||
{ | |||
public abstract EGID Id { get; } //Abstract to support the 'place' Block constructor | |||
protected internal EcsInitData InitData; | |||
/// <summary> | |||
/// Holds information needed to construct a component initializer | |||
/// </summary> | |||
internal struct BlockInitData | |||
protected internal struct EcsInitData | |||
{ | |||
public FasterDictionary<RefWrapperType, ITypeSafeDictionary> Group; | |||
public EntityReference Reference; | |||
private FasterDictionary<RefWrapperType, ITypeSafeDictionary> group; | |||
private EntityReference reference; | |||
public static implicit operator EcsInitData(EntityInitializer initializer) => new EcsInitData | |||
{group = GetInitGroup(initializer), reference = initializer.reference}; | |||
public EntityInitializer Initializer(EGID id) => new EntityInitializer(id, group, reference); | |||
public bool Valid => group != null; | |||
} | |||
internal delegate FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetInitGroup( | |||
private delegate FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetInitGroupFunc( | |||
EntityInitializer initializer); | |||
/// <summary> | |||
/// Accesses the group field of the initializer | |||
/// </summary> | |||
internal GetInitGroup InitGroup = CreateAccessor<GetInitGroup>("_group"); | |||
private static GetInitGroupFunc GetInitGroup = CreateAccessor<GetInitGroupFunc>("_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 | |||
private static TDelegate CreateAccessor<TDelegate>(string memberName) where TDelegate : Delegate | |||
{ | |||
var invokeMethod = typeof(TDelegate).GetMethod("Invoke"); | |||
if (invokeMethod == null) |
@@ -0,0 +1,53 @@ | |||
using Svelto.ECS; | |||
using TechbloxModdingAPI.Blocks; | |||
namespace TechbloxModdingAPI.Utility | |||
{ | |||
public static class ApiExtensions | |||
{ | |||
/// <summary> | |||
/// Attempts to query an entity and returns an optional that contains the result if succeeded. | |||
/// </summary> | |||
/// <param name="entitiesDB">The entities DB</param> | |||
/// <param name="egid">The EGID to query</param> | |||
/// <typeparam name="T">The component type to query</typeparam> | |||
/// <returns>An optional that contains the result on success or is empty if not found</returns> | |||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EGID egid) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
return entitiesDB.TryQueryEntitiesAndIndex<T>(egid, out uint index, out var array) | |||
? new OptionalRef<T>(array, index) | |||
: new OptionalRef<T>(); | |||
} | |||
/// <summary> | |||
/// Attempts to query an entity and returns the result or a dummy value that can be modified. | |||
/// </summary> | |||
/// <param name="entitiesDB"></param> | |||
/// <param name="obj"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
var opt = QueryEntityOptional<T>(entitiesDB, obj.Id); | |||
return opt ? opt : new OptionalRef<T>(obj); | |||
} | |||
/// <summary> | |||
/// Attempts to query an entity and returns the result or a dummy value that can be modified. | |||
/// </summary> | |||
/// <param name="entitiesDB"></param> | |||
/// <param name="obj"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
var opt = QueryEntityOptional<T>(entitiesDB, obj.Id); | |||
if (opt) return ref opt.Get(); | |||
if (obj.InitData.Valid) return ref obj.InitData.Initializer(obj.Id).GetOrCreate<T>(); | |||
return ref opt.Get(); //Default value | |||
} | |||
} | |||
} |
@@ -9,31 +9,51 @@ using Svelto.ECS; | |||
namespace TechbloxModdingAPI | |||
{ | |||
public struct OptionalRef<T> where T : unmanaged | |||
public ref struct OptionalRef<T> where T : unmanaged, IEntityComponent | |||
{ | |||
private bool exists; | |||
private NB<T> array; | |||
private uint index; | |||
private EntityInitializer initializer; | |||
public OptionalRef(NB<T> array, uint index) | |||
{ | |||
exists = true; | |||
this.array = array; | |||
this.index = index; | |||
initializer = default; | |||
} | |||
public OptionalRef(ref T value) | |||
/// <summary> | |||
/// Wraps the initializer data, if present. | |||
/// </summary> | |||
/// <param name="obj">The object with the initializer</param> | |||
public OptionalRef(EcsObjectBase obj) | |||
{ | |||
exists = true; | |||
if (obj.InitData.Valid) | |||
{ | |||
initializer = obj.InitData.Initializer(obj.Id); | |||
exists = true; | |||
} | |||
else | |||
{ | |||
initializer = default; | |||
exists = false; | |||
} | |||
array = default; | |||
index = default; | |||
} | |||
public ref T Get(T def = default) | |||
/// <summary> | |||
/// Returns the value or a default value if empty. Supports objects that are being initialized. | |||
/// </summary> | |||
/// <returns>The value or the default value</returns> | |||
public ref T Get() | |||
{ | |||
if (exists) | |||
if (!exists) return ref CompRefCache<T>.Default; | |||
if (initializer.EGID == EGID.Empty) | |||
return ref array[index]; | |||
return ref CompRefCache<T>._default; | |||
return ref initializer.GetOrCreate<T>(); | |||
} | |||
public bool Exists => exists; | |||
@@ -51,10 +71,10 @@ namespace TechbloxModdingAPI | |||
/// <summary> | |||
/// Creates an instance of a struct T that can be referenced. | |||
/// </summary> | |||
/// <typeparam name="T">The struct type to cache</typeparam> | |||
private struct CompRefCache<T> where T : unmanaged | |||
/// <typeparam name="TR">The struct type to cache</typeparam> | |||
private struct CompRefCache<TR> where TR : unmanaged | |||
{ | |||
public static T _default; | |||
public static TR Default; | |||
} | |||
} | |||
} |