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.

286 lines
8.5KB

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