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.

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