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.

285 lines
8.4KB

  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. }
  94. internal void UpdatePrefab(Block block, byte material, bool flipped)
  95. {
  96. var prefabAssetIDOpt = entitiesDB.QueryEntityOptional<PrefabAssetIDComponent>(block);
  97. uint prefabAssetID = prefabAssetIDOpt
  98. ? prefabAssetIDOpt.Get().prefabAssetID
  99. : uint.MaxValue;
  100. if (prefabAssetID == uint.MaxValue)
  101. {
  102. if (entitiesDB.QueryEntityOptional<BlockTagEntityStruct>(block)) //The block exists
  103. throw new BlockException("Prefab asset ID not found for block " + block); //Set by the game
  104. return;
  105. }
  106. uint prefabId =
  107. PrefabsID.GetOrCreatePrefabID((ushort) prefabAssetID, material, 1, flipped);
  108. entitiesDB.QueryEntityOrDefault<GFXPrefabEntityStructGPUI>(block).prefabID = prefabId;
  109. if (block.Exists)
  110. {
  111. entitiesDB.PublishEntityChange<CubeMaterialStruct>(block.Id);
  112. entitiesDB.PublishEntityChange<GFXPrefabEntityStructGPUI>(block.Id);
  113. ref BuildingActionComponent local =
  114. ref entitiesDB.QueryEntity<BuildingActionComponent>(BuildingDroneUtility
  115. .GetLocalBuildingDrone(entitiesDB).ToEGID(entitiesDB));
  116. local.buildAction = BuildAction.ChangeMaterial;
  117. local.targetPosition = block.Position;
  118. this.entitiesDB.PublishEntityChange<BuildingActionComponent>(local.ID);
  119. }
  120. //Phyiscs prefab: prefabAssetID, set on block creation from the CubeListData
  121. }
  122. public bool BlockExists(EGID blockID)
  123. {
  124. return entitiesDB.Exists<BlockTagEntityStruct>(blockID);
  125. }
  126. public SimBody[] GetSimBodiesFromID(byte id)
  127. {
  128. var ret = new FasterList<SimBody>(4);
  129. var oide = entitiesDB.QueryEntities<ObjectIdEntityStruct>();
  130. EGIDMapper<GridConnectionsEntityStruct>? connections = null;
  131. foreach (var ((oids, count), _) in oide)
  132. {
  133. for (int i = 0; i < count; i++)
  134. {
  135. ref ObjectIdEntityStruct oid = ref oids[i];
  136. if (oid.objectId != id) continue;
  137. if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise
  138. connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(oid.ID.groupID);
  139. var rid = connections.Value.Entity(oid.ID.entityID).machineRigidBodyId;
  140. foreach (var rb in ret)
  141. {
  142. if (rb.Id.entityID == rid)
  143. goto DUPLICATE; //Multiple Object Identifiers on one rigid body
  144. }
  145. ret.Add(new SimBody(rid));
  146. DUPLICATE: ;
  147. }
  148. }
  149. return ret.ToArray();
  150. }
  151. public SimBody[] GetConnectedSimBodies(uint id)
  152. {
  153. var joints = entitiesDB.QueryEntities<JointEntityStruct>(MachineSimulationGroups.JOINTS_GROUP).ToBuffer();
  154. var list = new FasterList<SimBody>(4);
  155. for (int i = 0; i < joints.count; i++)
  156. {
  157. ref var joint = ref joints.buffer[i];
  158. if (joint.isBroken) continue;
  159. if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB));
  160. else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA));
  161. }
  162. return list.ToArray();
  163. }
  164. public SimBody[] GetClusterBodies(uint cid)
  165. {
  166. var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
  167. var bodies = new HashSet<uint>();
  168. foreach (var (coll, _) in groups)
  169. {
  170. var array = coll.ToBuffer().buffer;
  171. for (var index = 0; index < array.capacity; index++)
  172. {
  173. var conn = array[index];
  174. if (conn.clusterId == cid)
  175. bodies.Add(conn.machineRigidBodyId);
  176. }
  177. }
  178. return bodies.Select(id => new SimBody(id, cid)).ToArray();
  179. }
  180. public EGID? FindBlockEGID(uint id)
  181. {
  182. var groups = entitiesDB.FindGroups<BlockTagEntityStruct>();
  183. foreach (ExclusiveGroupStruct group in groups)
  184. {
  185. if (entitiesDB.Exists<BlockTagEntityStruct>(id, group))
  186. return new EGID(id, group);
  187. }
  188. return null;
  189. }
  190. public Cluster GetCluster(uint sbid)
  191. {
  192. var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
  193. foreach (var (coll, _) in groups)
  194. {
  195. var array = coll.ToBuffer().buffer;
  196. for (var index = 0; index < array.capacity; index++)
  197. {
  198. var conn = array[index];
  199. //Static blocks don't have a cluster ID but the cluster destruction manager should have one
  200. if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue)
  201. return new Cluster(conn.clusterId);
  202. }
  203. }
  204. return null;
  205. }
  206. public Block[] GetBodyBlocks(uint sbid)
  207. {
  208. var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
  209. var set = new HashSet<Block>();
  210. foreach (var (coll, _) in groups)
  211. {
  212. var array = coll.ToBuffer().buffer;
  213. for (var index = 0; index < array.capacity; index++)
  214. {
  215. var conn = array[index];
  216. if (conn.machineRigidBodyId == sbid)
  217. set.Add(Block.New(conn.ID));
  218. }
  219. }
  220. return set.ToArray();
  221. }
  222. #if DEBUG
  223. public EntitiesDB GetEntitiesDB()
  224. {
  225. return entitiesDB;
  226. }
  227. #endif
  228. [HarmonyPatch]
  229. public static class RenderingPatch
  230. {
  231. private static ComputeRenderingEntitiesMatricesEngine Engine;
  232. public static void Postfix(ComputeRenderingEntitiesMatricesEngine __instance)
  233. {
  234. Engine = __instance;
  235. }
  236. public static MethodBase TargetMethod()
  237. {
  238. return typeof(ComputeRenderingEntitiesMatricesEngine).GetConstructors()[0];
  239. }
  240. public static void UpdateBlocks()
  241. {
  242. var data = new RenderingDataStruct();
  243. Engine.Add(ref data, new EGID(0, CommonExclusiveGroups.BUTTON_BLOCK_GROUP));
  244. }
  245. }
  246. }
  247. }