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.

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