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.

300 lines
9.0KB

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