using System;
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using Gamecraft.ColourPalette;
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.EntityStructs;
using Svelto.ECS.Experimental;
using Svelto.ECS.Hybrid;
using Techblox.BuildingDrone;
using Techblox.ObjectIDBlockServer;
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 Array.Empty();
Stack cubeStack = new Stack();
FasterList cubes = new FasterList(10);
var coll = entitiesDB.QueryEntities();
foreach (var ((ecoll, count), _) in coll)
{
for(int i = 0; i < count; i++)
{
ecoll[i].isProcessed = false;
}
}
//TODO: GetConnectedCubesUtility
ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes,
(in GridConnectionsEntityStruct _) => 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
{
#if DEBUG
if (!typeof(BlockTagEntityStruct).IsAssignableFrom(typeof(T)) && block.Exists && block.InitData.Valid)
throw new ArgumentException("The block exists but the init data has not been removed!");
#endif
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);
}
internal object GetBlockInfo(Block block, Type type, string name)
{
var opt = AccessTools.Method(typeof(NativeApiExtensions), "QueryEntityOptional",
new[] { typeof(EntitiesDB), typeof(EcsObjectBase), typeof(ExclusiveGroupStruct) }, new[] { type })
.Invoke(null, new object[] { entitiesDB, block, null });
var str = AccessTools.Property(opt.GetType(), "Value").GetValue(opt);
return AccessTools.Field(str.GetType(), name).GetValue(str);
}
internal void SetBlockInfo(Block block, Type type, string name, object value)
{
var opt = AccessTools.Method(typeof(BlockEngine), "GetBlockInfoOptional", generics: new[] { type })
.Invoke(this, new object[] { block });
var prop = AccessTools.Property(opt.GetType(), "Value");
var str = prop.GetValue(opt);
AccessTools.Field(str.GetType(), name).SetValue(str, value);
prop.SetValue(opt, str);
}
public void UpdateDisplayedBlock(EGID id)
{
if (!BlockExists(id)) return;
var pos = entitiesDB.QueryEntity(id);
var rot = entitiesDB.QueryEntity(id);
var scale = entitiesDB.QueryEntity(id);
var skew = entitiesDB.QueryEntity(id);
entitiesDB.QueryEntity(id).matrix =
math.mul(float4x4.TRS(pos.position, rot.rotation, scale.scale), skew.skewMatrix);
entitiesDB.PublishEntityChangeDelayed(id); // Signal a prefab change so it updates the render buffers
}
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.GetOrAddPrefabID((ushort) prefabAssetID, material, 1, flipped);
entitiesDB.QueryEntityOrDefault(block).prefabID = prefabId;
if (block.Exists)
{
entitiesDB.PublishEntityChangeDelayed(block.Id);
entitiesDB.PublishEntityChangeDelayed(block.Id);
ref BuildingActionComponent local =
ref entitiesDB.QueryEntity(BuildingDroneUtility
.GetLocalBuildingDrone(entitiesDB).ToEGID(entitiesDB));
local.buildAction = BuildAction.ChangeMaterial;
local.targetPosition = block.Position;
this.entitiesDB.PublishEntityChangeDelayed(local.ID);
}
//Phyiscs prefab: prefabAssetID, set on block creation from the CubeListData
}
public void UpdateBlockColor(EGID id)
{
entitiesDB.PublishEntityChange(id);
}
public bool BlockExists(EGID blockID)
{
return entitiesDB.Exists(blockID);
}
public SimBody[] GetSimBodiesFromID(byte id)
{
var ret = new FasterList(4);
var (oids, tags, count) = entitiesDB.QueryEntities(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP);
EGIDMapper? connections = null;
for (int i = 0; i < count; i++)
{
if (oids[i].objectId != id) continue;
var tag = tags[i];
if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise
connections = entitiesDB.QueryMappedEntities(tag.ID.groupID);
var rid = connections.Value.Entity(tag.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, count) = entitiesDB.QueryEntities(MachineSimulationGroups.JOINTS_GROUP);
var list = new FasterList(4);
for (int i = 0; i < count; i++)
{
ref var joint = ref joints[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, count), _) in groups)
{
for (var index = 0; index < count; index++)
{
var conn = coll[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, count), _) in groups)
{
for (var index = 0; index < count; index++)
{
var conn = coll[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.FindGroups();
groups = new QueryGroups(groups).Except(CommonExclusiveGroups.DISABLED_JOINTS_IN_SIM_GROUP).Evaluate().result;
var set = new HashSet();
foreach (var ((coll, tags, count), _) in entitiesDB.QueryEntities(groups))
{
for (var index = 0; index < count; index++)
{
var conn = coll[index];
if (conn.machineRigidBodyId == sbid)
set.Add(Block.New(tags[index].ID));
}
}
return set.ToArray();
}
public ObjectID[] GetObjectIDsFromID(byte id)
{
if (!entitiesDB.HasAny(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP))
return Array.Empty();
var ret = new FasterList(4);
var (oids, tags, count) = entitiesDB.QueryEntities(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP);
for (var index = 0; index < count; index++)
{
if (oids[index].objectIDToTrigger == id)
ret.Add(new ObjectID(tags[index].ID));
}
return ret.ToArray();
}
}
}