A stable modding interface between Techblox and mods https://mod.exmods.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

280 lines
8.1KB

  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using System.Reflection;
  4. using HarmonyLib;
  5. using Gamecraft.ColourPalette;
  6. using Gamecraft.TimeRunning;
  7. using Gamecraft.Wires;
  8. using RobocraftX.Blocks;
  9. using RobocraftX.Common;
  10. using RobocraftX.Physics;
  11. using RobocraftX.Rendering;
  12. using RobocraftX.Rendering.GPUI;
  13. using Svelto.DataStructures;
  14. using Svelto.ECS;
  15. using Svelto.ECS.EntityStructs;
  16. using Svelto.ECS.Hybrid;
  17. using Techblox.BuildingDrone;
  18. using Unity.Mathematics;
  19. using TechbloxModdingAPI.Engines;
  20. using TechbloxModdingAPI.Utility;
  21. namespace TechbloxModdingAPI.Blocks.Engines
  22. {
  23. /// <summary>
  24. /// Engine for executing general block actions
  25. /// </summary>
  26. public partial class BlockEngine : IApiEngine
  27. {
  28. public string Name { get; } = "TechbloxModdingAPIBlockGameEngine";
  29. public EntitiesDB entitiesDB { set; private get; }
  30. public bool isRemovable => false;
  31. public void Dispose()
  32. {
  33. }
  34. public void Ready()
  35. {
  36. }
  37. public Block[] GetConnectedBlocks(EGID blockID)
  38. {
  39. if (!BlockExists(blockID)) return new Block[0];
  40. Stack<EGID> cubeStack = new Stack<EGID>();
  41. FasterList<EGID> cubes = new FasterList<EGID>(10);
  42. var coll = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
  43. foreach (var (ecoll, _) in coll)
  44. {
  45. var ecollB = ecoll.ToBuffer();
  46. for(int i = 0; i < ecoll.count; i++)
  47. {
  48. ref var conn = ref ecollB.buffer[i];
  49. conn.isProcessed = false;
  50. }
  51. }
  52. ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes,
  53. (in GridConnectionsEntityStruct g) => { return false; });
  54. var ret = new Block[cubes.count];
  55. for (int i = 0; i < cubes.count; i++)
  56. ret[i] = Block.New(cubes[i]);
  57. return ret;
  58. }
  59. public float4 ConvertBlockColor(byte index) => index == byte.MaxValue
  60. ? new float4(-1f, -1f, -1f, -1f)
  61. : entitiesDB.QueryEntity<PaletteEntryEntityStruct>(index,
  62. CommonExclusiveGroups.COLOUR_PALETTE_GROUP).Colour;
  63. public OptionalRef<T> GetBlockInfoOptional<T>(Block block) where T : unmanaged, IEntityComponent
  64. {
  65. return entitiesDB.QueryEntityOptional<T>(block);
  66. }
  67. public ref T GetBlockInfo<T>(Block block) where T : unmanaged, IEntityComponent
  68. {
  69. return ref entitiesDB.QueryEntityOrDefault<T>(block);
  70. }
  71. internal ref T GetBlockInfo<T>(EcsObjectBase obj) where T : unmanaged, IEntityComponent
  72. {
  73. return ref entitiesDB.QueryEntityOrDefault<T>(obj);
  74. }
  75. public ref T GetBlockInfoViewComponent<T>(Block block) where T : struct, IEntityViewComponent
  76. {
  77. return ref entitiesDB.QueryEntityOrDefault<T>(block);
  78. }
  79. public void UpdateDisplayedBlock(EGID id)
  80. {
  81. if (!BlockExists(id)) return;
  82. var pos = entitiesDB.QueryEntity<PositionEntityStruct>(id);
  83. var rot = entitiesDB.QueryEntity<RotationEntityStruct>(id);
  84. var scale = entitiesDB.QueryEntity<ScalingEntityStruct>(id);
  85. var skew = entitiesDB.QueryEntity<SkewComponent>(id);
  86. entitiesDB.QueryEntity<RenderingDataStruct>(id).matrix =
  87. math.mul(float4x4.TRS(pos.position, rot.rotation, scale.scale), skew.skewMatrix);
  88. }
  89. internal void UpdatePrefab(Block block, byte material, bool flipped)
  90. {
  91. var prefabAssetIDOpt = entitiesDB.QueryEntityOptional<PrefabAssetIDComponent>(block);
  92. uint prefabAssetID = prefabAssetIDOpt
  93. ? prefabAssetIDOpt.Get().prefabAssetID
  94. : uint.MaxValue;
  95. if (prefabAssetID == uint.MaxValue)
  96. {
  97. if (entitiesDB.QueryEntityOptional<DBEntityStruct>(block)) //The block exists
  98. throw new BlockException("Prefab asset ID not found for block " + block); //Set by the game
  99. return;
  100. }
  101. uint prefabId =
  102. PrefabsID.GetOrCreatePrefabID((ushort) prefabAssetID, material, 1, flipped);
  103. entitiesDB.QueryEntityOrDefault<GFXPrefabEntityStructGPUI>(block).prefabID = prefabId;
  104. if (block.Exists)
  105. {
  106. entitiesDB.PublishEntityChange<CubeMaterialStruct>(block.Id);
  107. entitiesDB.PublishEntityChange<GFXPrefabEntityStructGPUI>(block.Id);
  108. ref BuildingActionComponent local =
  109. ref entitiesDB.QueryEntity<BuildingActionComponent>(BuildingDroneUtility
  110. .GetLocalBuildingDrone(entitiesDB).ToEGID(entitiesDB));
  111. local.buildAction = BuildAction.ChangeMaterial;
  112. local.targetPosition = block.Position;
  113. this.entitiesDB.PublishEntityChange<BuildingActionComponent>(local.ID);
  114. }
  115. //Phyiscs prefab: prefabAssetID, set on block creation from the CubeListData
  116. }
  117. public bool BlockExists(EGID blockID)
  118. {
  119. return entitiesDB.Exists<DBEntityStruct>(blockID);
  120. }
  121. public SimBody[] GetSimBodiesFromID(byte id)
  122. {
  123. var ret = new FasterList<SimBody>(4);
  124. var oide = entitiesDB.QueryEntities<ObjectIdEntityStruct>();
  125. EGIDMapper<GridConnectionsEntityStruct>? connections = null;
  126. foreach (var ((oids, count), _) in oide)
  127. {
  128. for (int i = 0; i < count; i++)
  129. {
  130. ref ObjectIdEntityStruct oid = ref oids[i];
  131. if (oid.objectId != id) continue;
  132. if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise
  133. connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(oid.ID.groupID);
  134. var rid = connections.Value.Entity(oid.ID.entityID).machineRigidBodyId;
  135. foreach (var rb in ret)
  136. {
  137. if (rb.Id.entityID == rid)
  138. goto DUPLICATE; //Multiple Object Identifiers on one rigid body
  139. }
  140. ret.Add(new SimBody(rid));
  141. DUPLICATE: ;
  142. }
  143. }
  144. return ret.ToArray();
  145. }
  146. public SimBody[] GetConnectedSimBodies(uint id)
  147. {
  148. var joints = entitiesDB.QueryEntities<JointEntityStruct>(MachineSimulationGroups.JOINTS_GROUP).ToBuffer();
  149. var list = new FasterList<SimBody>(4);
  150. for (int i = 0; i < joints.count; i++)
  151. {
  152. ref var joint = ref joints.buffer[i];
  153. if (joint.isBroken) continue;
  154. if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB));
  155. else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA));
  156. }
  157. return list.ToArray();
  158. }
  159. public SimBody[] GetClusterBodies(uint cid)
  160. {
  161. var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
  162. var bodies = new HashSet<uint>();
  163. foreach (var (coll, _) in groups)
  164. {
  165. var array = coll.ToBuffer().buffer;
  166. for (var index = 0; index < array.capacity; index++)
  167. {
  168. var conn = array[index];
  169. if (conn.clusterId == cid)
  170. bodies.Add(conn.machineRigidBodyId);
  171. }
  172. }
  173. return bodies.Select(id => new SimBody(id, cid)).ToArray();
  174. }
  175. public EGID? FindBlockEGID(uint id)
  176. {
  177. var groups = entitiesDB.FindGroups<DBEntityStruct>();
  178. foreach (ExclusiveGroupStruct group in groups)
  179. {
  180. if (entitiesDB.Exists<DBEntityStruct>(id, group))
  181. return new EGID(id, group);
  182. }
  183. return null;
  184. }
  185. public Cluster GetCluster(uint sbid)
  186. {
  187. var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
  188. foreach (var (coll, _) in groups)
  189. {
  190. var array = coll.ToBuffer().buffer;
  191. for (var index = 0; index < array.capacity; index++)
  192. {
  193. var conn = array[index];
  194. //Static blocks don't have a cluster ID but the cluster destruction manager should have one
  195. if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue)
  196. return new Cluster(conn.clusterId);
  197. }
  198. }
  199. return null;
  200. }
  201. public Block[] GetBodyBlocks(uint sbid)
  202. {
  203. var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
  204. var set = new HashSet<Block>();
  205. foreach (var (coll, _) in groups)
  206. {
  207. var array = coll.ToBuffer().buffer;
  208. for (var index = 0; index < array.capacity; index++)
  209. {
  210. var conn = array[index];
  211. if (conn.machineRigidBodyId == sbid)
  212. set.Add(Block.New(conn.ID));
  213. }
  214. }
  215. return set.ToArray();
  216. }
  217. #if DEBUG
  218. public EntitiesDB GetEntitiesDB()
  219. {
  220. return entitiesDB;
  221. }
  222. #endif
  223. [HarmonyPatch]
  224. public static class RenderingPatch
  225. {
  226. private static ComputeRenderingEntitiesMatricesEngine Engine;
  227. public static void Postfix(ComputeRenderingEntitiesMatricesEngine __instance)
  228. {
  229. Engine = __instance;
  230. }
  231. public static MethodBase TargetMethod()
  232. {
  233. return typeof(ComputeRenderingEntitiesMatricesEngine).GetConstructors()[0];
  234. }
  235. public static void UpdateBlocks()
  236. {
  237. var data = new RenderingDataStruct();
  238. Engine.Add(ref data, new EGID(0, CommonExclusiveGroups.BUTTON_BLOCK_GROUP));
  239. }
  240. }
  241. }
  242. }