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.

281 lines
8.8KB

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