using System.Collections.Generic; using System.Linq; using System.Reflection; using HarmonyLib; using Gamecraft.ColourPalette; using Gamecraft.TimeRunning; using Gamecraft.Wires; using RobocraftX.Blocks; using RobocraftX.Common; using RobocraftX.Physics; using RobocraftX.Rendering; using RobocraftX.Rendering.GPUI; using Svelto.DataStructures; using Svelto.ECS; using Svelto.ECS.Hybrid; using Techblox.BuildingDrone; using Unity.Mathematics; using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Utility; namespace TechbloxModdingAPI.Blocks.Engines { /// /// Engine for executing general block actions /// public partial class BlockEngine : IApiEngine { public string Name { get; } = "TechbloxModdingAPIBlockGameEngine"; 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] = Block.New(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 OptionalRef GetBlockInfoOptional(Block block) where T : unmanaged, IEntityComponent { return entitiesDB.QueryEntityOptional(block); } public ref T GetBlockInfo(Block block) where T : unmanaged, IEntityComponent { return ref entitiesDB.QueryEntityOrDefault(block); } internal ref T GetBlockInfo(EcsObjectBase obj) where T : unmanaged, IEntityComponent { return ref entitiesDB.QueryEntityOrDefault(obj); } public ref T GetBlockInfoViewComponent(Block block) where T : struct, IEntityViewComponent { return ref entitiesDB.QueryEntityOrDefault(block); } public void UpdateDisplayedBlock(EGID id) { if (!BlockExists(id)) return; RenderingPatch.UpdateBlocks(); } internal void UpdatePrefab(Block block, byte material, bool flipped) { var prefabAssetIDOpt = entitiesDB.QueryEntityOptional(block); uint prefabAssetID = prefabAssetIDOpt ? prefabAssetIDOpt.Get().prefabAssetID : uint.MaxValue; if (prefabAssetID == uint.MaxValue) { if (entitiesDB.QueryEntityOptional(block)) //The block exists throw new BlockException("Prefab asset ID not found for block " + block); //Set by the game return; } uint prefabId = PrefabsID.GetOrCreatePrefabID((ushort) prefabAssetID, material, 1, flipped); entitiesDB.QueryEntityOrDefault(block).prefabID = prefabId; if (block.Exists) { entitiesDB.PublishEntityChange(block.Id); entitiesDB.PublishEntityChange(block.Id); ref BuildingActionComponent local = ref entitiesDB.QueryEntity(BuildingDroneUtility .GetLocalBuildingDrone(entitiesDB).ToEGID(entitiesDB)); local.buildAction = BuildAction.ChangeMaterial; local.targetPosition = block.Position; this.entitiesDB.PublishEntityChange(local.ID); } //Phyiscs prefab: prefabAssetID, set on block creation from the CubeListData } public bool BlockExists(EGID blockID) { return entitiesDB.Exists(blockID); } 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 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(Block.New(conn.ID)); } } return set.ToArray(); } #if DEBUG public EntitiesDB GetEntitiesDB() { return entitiesDB; } #endif [HarmonyPatch] public static class RenderingPatch { private static ComputeRenderingEntitiesMatricesEngine Engine; public static void Postfix(ComputeRenderingEntitiesMatricesEngine __instance) { Engine = __instance; } public static MethodBase TargetMethod() { return typeof(ComputeRenderingEntitiesMatricesEngine).GetConstructors()[0]; } public static void UpdateBlocks() { var data = new RenderingDataStruct(); Engine.Add(ref data, new EGID(0, CommonExclusiveGroups.BUTTON_BLOCK_GROUP)); } } } }