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.

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