using System; using System.Collections.Generic; using System.Linq; using Gamecraft.ColourPalette; using Gamecraft.TimeRunning; using Gamecraft.Wires; using RobocraftX.Blocks; using RobocraftX.Common; using RobocraftX.Physics; using RobocraftX.Rendering; using Svelto.ECS.EntityStructs; using Svelto.DataStructures; using Svelto.ECS; using Svelto.ECS.Hybrid; using Unity.Mathematics; using GamecraftModdingAPI.Engines; namespace GamecraftModdingAPI.Blocks { /// /// Engine for executing general block actions /// public partial 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(EGID blockID) { if (!BlockExists(blockID)) return new Block[0]; Stack cubeStack = new Stack(); FasterList cubes = new FasterList(10); var coll = entitiesDB.QueryEntities(); foreach (var (ecoll, _) in coll) { var ecollB = ecoll.ToBuffer(); for(int i = 0; i < ecoll.count; i++) { ref var conn = ref ecollB.buffer[i]; conn.isProcessed = false; } } ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes, (in GridConnectionsEntityStruct g) => { return false; }); var ret = new Block[cubes.count]; for (int i = 0; i < cubes.count; i++) ret[i] = new Block(cubes[i]); return ret; } public float4 ConvertBlockColor(byte index) => index == byte.MaxValue ? new float4(-1f, -1f, -1f, -1f) : entitiesDB.QueryEntity(index, CommonExclusiveGroups.COLOUR_PALETTE_GROUP).Colour; public ref T GetBlockInfo(EGID blockID) where T : unmanaged, IEntityComponent { if (entitiesDB.Exists(blockID)) return ref entitiesDB.QueryEntity(blockID); T[] structHolder = new T[1]; //Create something that can be referenced return ref structHolder[0]; //Gets a default value automatically } public ref T GetBlockInfoViewStruct(EGID blockID) where T : struct, INeedEGID, IEntityViewComponent { if (entitiesDB.Exists(blockID)) return ref entitiesDB.QueryEntity(blockID); T[] structHolder = new T[1]; //Create something that can be referenced return ref structHolder[0]; //Gets a default value automatically } public U GetBlockInfo(Block block, Func getter, U def = default) where T : unmanaged, IEntityComponent { if (entitiesDB.Exists(block.Id)) return getter(entitiesDB.QueryEntity(block.Id)); return GetBlockInitInfo(block, getter, def); } public U GetBlockInfoViewStruct(Block block, Func getter, U def = default) where T : struct, IEntityViewComponent { if (entitiesDB.Exists(block.Id)) return getter(entitiesDB.QueryEntity(block.Id)); return GetBlockInitInfo(block, getter, def); } private U GetBlockInitInfo(Block block, Func getter, U def) where T : struct, IEntityComponent { if (block.InitData.Group == null) return def; var initializer = new EntityInitializer(block.Id, block.InitData.Group, block.InitData.Reference); if (initializer.Has()) return getter(initializer.Get()); return def; } public delegate void Setter(ref T component, U value) where T : struct, IEntityComponent; public void SetBlockInfoViewStruct(Block block, Setter setter, U value) where T : struct, IEntityViewComponent { if (entitiesDB.Exists(block.Id)) setter(ref entitiesDB.QueryEntity(block.Id), value); else SetBlockInitInfo(block, setter, value); } public void SetBlockInfo(Block block, Setter setter, U value) where T : unmanaged, IEntityComponent { if (entitiesDB.Exists(block.Id)) setter(ref entitiesDB.QueryEntity(block.Id), value); else SetBlockInitInfo(block, setter, value); } private void SetBlockInitInfo(Block block, Setter setter, U value) where T : struct, IEntityComponent { if (block.InitData.Group != null) { var initializer = new EntityInitializer(block.Id, block.InitData.Group, block.InitData.Reference); T component = initializer.Has() ? initializer.Get() : default; ref T structRef = ref component; setter(ref structRef, value); initializer.Init(structRef); } } public void UpdateDisplayedBlock(EGID id) { if (!BlockExists(id)) return; var pos = entitiesDB.QueryEntity(id); var rot = entitiesDB.QueryEntity(id); var scale = entitiesDB.QueryEntity(id); entitiesDB.QueryEntity(id).matrix = float4x4.TRS(pos.position, rot.rotation, scale.scale); } public bool BlockExists(EGID blockID) { return entitiesDB.Exists(blockID); } public bool GetBlockInfoExists(Block block) where T : struct, IEntityComponent { if (entitiesDB.Exists(block.Id)) return true; if (block.InitData.Group == null) return false; var init = new EntityInitializer(block.Id, block.InitData.Group, block.InitData.Reference); return init.Has(); } public SimBody[] GetSimBodiesFromID(byte id) { var ret = new FasterList(4); var oide = entitiesDB.QueryEntities(); EGIDMapper? connections = null; foreach (var ((oids, count), _) in oide) { for (int i = 0; i < count; i++) { ref ObjectIdEntityStruct oid = ref oids[i]; if (oid.objectId != id) continue; if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise connections = entitiesDB.QueryMappedEntities(oid.ID.groupID); var rid = connections.Value.Entity(oid.ID.entityID).machineRigidBodyId; foreach (var rb in ret) { if (rb.Id.entityID == rid) goto DUPLICATE; //Multiple Object Identifiers on one rigid body } ret.Add(new SimBody(rid)); DUPLICATE: ; } } return ret.ToArray(); } public ObjectIdentifier[] GetObjectIDsFromID(byte id, bool sim) { var ret = new FasterList(4); var oide = entitiesDB.QueryEntities(); foreach (var ((oids, count), _) in oide) { for (int i = 0; i < count; i++) { ref ObjectIdEntityStruct oid = ref oids[i]; if (sim ? oid.simObjectId == id : oid.objectId == id) ret.Add(new ObjectIdentifier(oid.ID)); } } return ret.ToArray(); } public SimBody[] GetConnectedSimBodies(uint id) { var joints = entitiesDB.QueryEntities(MachineSimulationGroups.JOINTS_GROUP).ToBuffer(); var list = new FasterList(4); for (int i = 0; i < joints.count; i++) { ref var joint = ref joints.buffer[i]; if (joint.isBroken) continue; if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB)); else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA)); } return list.ToArray(); } public SimBody[] GetClusterBodies(uint cid) { var groups = entitiesDB.QueryEntities(); var bodies = new HashSet(); foreach (var (coll, _) in groups) { var array = coll.ToBuffer().buffer; for (var index = 0; index < array.capacity; index++) { var conn = array[index]; if (conn.clusterId == cid) bodies.Add(conn.machineRigidBodyId); } } return bodies.Select(id => new SimBody(id, cid)).ToArray(); } public EGID? FindBlockEGID(uint id) { var groups = entitiesDB.FindGroups(); foreach (ExclusiveGroupStruct group in groups) { if (entitiesDB.Exists(id, group)) return new EGID(id, group); } return null; } public Cluster GetCluster(uint sbid) { var groups = entitiesDB.QueryEntities(); foreach (var (coll, _) in groups) { var array = coll.ToBuffer().buffer; for (var index = 0; index < array.capacity; index++) { var conn = array[index]; //Static blocks don't have a cluster ID but the cluster destruction manager should have one if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue) return new Cluster(conn.clusterId); } } return null; } public Block[] GetBodyBlocks(uint sbid) { var groups = entitiesDB.QueryEntities(); var set = new HashSet(); foreach (var (coll, _) in groups) { var array = coll.ToBuffer().buffer; for (var index = 0; index < array.capacity; index++) { var conn = array[index]; if (conn.machineRigidBodyId == sbid) set.Add(new Block(conn.ID)); } } return set.ToArray(); } #if DEBUG public EntitiesDB GetEntitiesDB() { return entitiesDB; } #endif } }