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.

274 lines
7.7KB

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