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.

291 lines
9.5KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using HarmonyLib;
  5. using Gamecraft.ColourPalette;
  6. using Gamecraft.Wires;
  7. using RobocraftX.Blocks;
  8. using RobocraftX.Common;
  9. using RobocraftX.Physics;
  10. using RobocraftX.Rendering;
  11. using RobocraftX.Rendering.GPUI;
  12. using Svelto.DataStructures;
  13. using Svelto.ECS;
  14. using Svelto.ECS.EntityStructs;
  15. using Svelto.ECS.Experimental;
  16. using Svelto.ECS.Hybrid;
  17. using Techblox.BuildingDrone;
  18. using Techblox.ObjectIDBlockServer;
  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. //TODO: GetConnectedCubesUtility
  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(NativeApiExtensions), "QueryEntityOptional",
  86. new[] { typeof(EntitiesDB), typeof(EcsObjectBase), typeof(ExclusiveGroupStruct) }, new[] { type })
  87. .Invoke(null, new object[] { entitiesDB, block, null });
  88. var str = AccessTools.Property(opt.GetType(), "Value").GetValue(opt);
  89. return AccessTools.Field(str.GetType(), name).GetValue(str);
  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 prop = AccessTools.Property(opt.GetType(), "Value");
  96. var str = prop.GetValue(opt);
  97. AccessTools.Field(str.GetType(), name).SetValue(str, value);
  98. prop.SetValue(opt, str);
  99. }
  100. public void UpdateDisplayedBlock(EGID id)
  101. {
  102. if (!BlockExists(id)) return;
  103. var pos = entitiesDB.QueryEntity<PositionEntityStruct>(id);
  104. var rot = entitiesDB.QueryEntity<RotationEntityStruct>(id);
  105. var scale = entitiesDB.QueryEntity<ScalingEntityStruct>(id);
  106. var skew = entitiesDB.QueryEntity<SkewComponent>(id);
  107. entitiesDB.QueryEntity<RenderingDataStruct>(id).matrix =
  108. math.mul(float4x4.TRS(pos.position, rot.rotation, scale.scale), skew.skewMatrix);
  109. entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(id); // Signal a prefab change so it updates the render buffers
  110. }
  111. internal void UpdatePrefab(Block block, byte material, bool flipped)
  112. {
  113. var prefabAssetIDOpt = entitiesDB.QueryEntityOptional<PrefabAssetIDComponent>(block);
  114. uint prefabAssetID = prefabAssetIDOpt
  115. ? prefabAssetIDOpt.Get().prefabAssetID
  116. : uint.MaxValue;
  117. if (prefabAssetID == uint.MaxValue)
  118. {
  119. if (entitiesDB.QueryEntityOptional<BlockTagEntityStruct>(block)) //The block exists
  120. throw new BlockException("Prefab asset ID not found for block " + block); //Set by the game
  121. return;
  122. }
  123. uint prefabId =
  124. PrefabsID.GetOrAddPrefabID((ushort) prefabAssetID, material, 1, flipped);
  125. entitiesDB.QueryEntityOrDefault<GFXPrefabEntityStructGPUI>(block).prefabID = prefabId;
  126. if (block.Exists)
  127. {
  128. entitiesDB.PublishEntityChangeDelayed<CubeMaterialStruct>(block.Id);
  129. entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(block.Id);
  130. ref BuildingActionComponent local =
  131. ref entitiesDB.QueryEntity<BuildingActionComponent>(BuildingDroneUtility
  132. .GetLocalBuildingDrone(entitiesDB).ToEGID(entitiesDB));
  133. local.buildAction = BuildAction.ChangeMaterial;
  134. local.targetPosition = block.Position;
  135. this.entitiesDB.PublishEntityChangeDelayed<BuildingActionComponent>(local.ID);
  136. }
  137. //Phyiscs prefab: prefabAssetID, set on block creation from the CubeListData
  138. }
  139. public void UpdateBlockColor(EGID id)
  140. {
  141. entitiesDB.PublishEntityChange<ColourParameterEntityStruct>(id);
  142. }
  143. public bool BlockExists(EGID blockID)
  144. {
  145. return entitiesDB.Exists<BlockTagEntityStruct>(blockID);
  146. }
  147. public SimBody[] GetSimBodiesFromID(byte id)
  148. {
  149. var ret = new FasterList<SimBody>(4);
  150. var (oids, tags, count) = entitiesDB.QueryEntities<ObjectIdEntityStruct, BlockTagEntityStruct>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP);
  151. EGIDMapper<GridConnectionsEntityStruct>? connections = null;
  152. for (int i = 0; i < count; i++)
  153. {
  154. if (oids[i].objectId != id) continue;
  155. var tag = tags[i];
  156. if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise
  157. connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(tag.ID.groupID);
  158. var rid = connections.Value.Entity(tag.ID.entityID).machineRigidBodyId;
  159. foreach (var rb in ret)
  160. {
  161. if (rb.Id.entityID == rid)
  162. goto DUPLICATE; //Multiple Object Identifiers on one rigid body
  163. }
  164. ret.Add(new SimBody(rid));
  165. DUPLICATE: ;
  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.FindGroups<GridConnectionsEntityStruct>();
  225. groups = new QueryGroups(groups).Except(CommonExclusiveGroups.DISABLED_JOINTS_IN_SIM_GROUP).Evaluate().result;
  226. var set = new HashSet<Block>();
  227. foreach (var ((coll, tags, count), _) in entitiesDB.QueryEntities<GridConnectionsEntityStruct, BlockTagEntityStruct>(groups))
  228. {
  229. for (var index = 0; index < count; index++)
  230. {
  231. var conn = coll[index];
  232. if (conn.machineRigidBodyId == sbid)
  233. set.Add(Block.New(tags[index].ID));
  234. }
  235. }
  236. return set.ToArray();
  237. }
  238. public ObjectID[] GetObjectIDsFromID(byte id)
  239. {
  240. if (!entitiesDB.HasAny<ObjectIDTweakableComponent>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP))
  241. return Array.Empty<ObjectID>();
  242. var ret = new FasterList<ObjectID>(4);
  243. var (oids, tags, count) = entitiesDB.QueryEntities<ObjectIDTweakableComponent, BlockTagEntityStruct>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP);
  244. for (var index = 0; index < count; index++)
  245. {
  246. if (oids[index].objectIDToTrigger == id)
  247. ret.Add(new ObjectID(tags[index].ID));
  248. }
  249. return ret.ToArray();
  250. }
  251. }
  252. }