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.

319 lines
14KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using Gamecraft.Blocks.BlockGroups;
  5. using Gamecraft.GUI.Blueprints;
  6. using HarmonyLib;
  7. using RobocraftX.Blocks;
  8. using RobocraftX.Common;
  9. using RobocraftX.CR.MachineEditing.BoxSelect;
  10. using RobocraftX.CR.MachineEditing.BoxSelect.ClipboardOperations;
  11. using Svelto.DataStructures;
  12. using Svelto.ECS;
  13. using Svelto.ECS.DataStructures;
  14. using Svelto.ECS.EntityStructs;
  15. using Svelto.ECS.Serialization;
  16. using TechbloxModdingAPI.Engines;
  17. using TechbloxModdingAPI.Utility;
  18. using Unity.Collections;
  19. using Unity.Mathematics;
  20. using UnityEngine;
  21. using Allocator = Svelto.Common.Allocator;
  22. namespace TechbloxModdingAPI.Blocks
  23. {
  24. public class BlueprintEngine : IFactoryEngine
  25. {
  26. private readonly MethodInfo getBlocksFromGroup =
  27. AccessTools.Method("RobocraftX.CR.MachineEditing.PlaceBlockUtility:GetBlocksSharingBlockgroup");
  28. private NativeDynamicArray selectedBlocksInGroup;
  29. private NativeHashSet<ulong> removedConnections = new NativeHashSet<ulong>();
  30. private int addingToBlockGroup = -1;
  31. private static readonly Type PlaceBlueprintUtilityType =
  32. AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceBlueprintUtility");
  33. private static readonly FieldInfo LocalBlockMap =
  34. AccessTools.DeclaredField(PlaceBlueprintUtilityType, "_localBlockMap");
  35. private static readonly MethodInfo BuildBlock = AccessTools.Method(PlaceBlueprintUtilityType, "BuildBlock");
  36. private static readonly MethodInfo BuildWires = AccessTools.Method(PlaceBlueprintUtilityType, "BuildWires");
  37. private static readonly Type SerializeGhostBlueprintType =
  38. AccessTools.TypeByName("RobocraftX.CR.MachineEditing.BoxSelect.SerializeGhostChildrenOnAddEngine");
  39. private static readonly MethodInfo SerializeGhostBlueprint =
  40. AccessTools.Method(SerializeGhostBlueprintType, "SerializeClipboardGhostEntities");
  41. private static NativeEntityRemove nativeRemove;
  42. private static MachineGraphConnectionEntityFactory connectionFactory;
  43. private static IEntityFunctions entityFunctions;
  44. private static ClipboardSerializationDataResourceManager clipboardManager;
  45. private static IEntitySerialization entitySerialization;
  46. private static IEntityFactory entityFactory;
  47. private static FasterList<EGID> globalBlockMap;
  48. private static object SerializeGhostBlueprintInstance;
  49. private static GhostChildEntityFactory BuildGhostBlueprintFactory;
  50. public void Ready()
  51. {
  52. selectedBlocksInGroup = NativeDynamicArray.Alloc<EGID>(Allocator.Persistent);
  53. }
  54. public EntitiesDB entitiesDB { get; set; }
  55. public void Dispose()
  56. {
  57. selectedBlocksInGroup.Dispose();
  58. }
  59. public Block[] GetBlocksFromGroup(EGID blockID, out float3 pos, out quaternion rot)
  60. {
  61. var blockPos = default(float3);
  62. var blockRot = default(quaternion);
  63. var parameters = new object[] {blockID, selectedBlocksInGroup, entitiesDB, blockPos, blockRot};
  64. getBlocksFromGroup.Invoke(null, parameters);
  65. pos = (float3) parameters[3];
  66. rot = (quaternion) parameters[4];
  67. int count = selectedBlocksInGroup.Count<EGID>();
  68. var ret = new Block[count];
  69. for (uint i = 0; i < count; i++)
  70. ret[i] = new Block(selectedBlocksInGroup.Get<EGID>(i));
  71. selectedBlocksInGroup.FastClear();
  72. return ret;
  73. }
  74. public void RemoveBlockGroup(int id)
  75. {
  76. BlockGroupUtility.RemoveAllBlocksInBlockGroup(id, entitiesDB, removedConnections, nativeRemove,
  77. connectionFactory, default).Complete();
  78. }
  79. public int CreateBlockGroup(float3 position, quaternion rotation)
  80. {
  81. int nextFilterId = BlockGroupUtility.NextFilterId;
  82. Factory.BuildEntity<BlockGroupEntityDescriptor>((uint) nextFilterId,
  83. BlockGroupExclusiveGroups.BlockGroupEntityGroup).Init(new BlockGroupTransformEntityComponent
  84. {
  85. blockGroupGridRotation = rotation,
  86. blockGroupGridPosition = position
  87. });
  88. return nextFilterId;
  89. }
  90. public void AddBlockToGroup(EGID blockID, int groupID)
  91. {
  92. if (globalBlockMap == null)
  93. globalBlockMap = FullGameFields._deserialisedBlockMap;
  94. if (groupID != addingToBlockGroup)
  95. {
  96. Logging.MetaDebugLog("Changing current block group from " + addingToBlockGroup + " to " + groupID);
  97. addingToBlockGroup = groupID;
  98. globalBlockMap.Clear();
  99. }
  100. globalBlockMap.Add(blockID);
  101. }
  102. public void SelectBlueprint(uint resourceID)
  103. {
  104. if (resourceID == uint.MaxValue)
  105. BlueprintUtil.UnselectBlueprint(entitiesDB);
  106. else
  107. BlueprintUtil.SelectBlueprint(entitiesDB, resourceID, false, -1);
  108. }
  109. public uint CreateBlueprint()
  110. {
  111. uint index = clipboardManager.AllocateSerializationData();
  112. return index;
  113. }
  114. public void ReplaceBlueprint(uint playerID, uint blueprintID, ICollection<Block> selected, float3 pos, quaternion rot)
  115. {
  116. var blockIDs = new EGID[selected.Count];
  117. using (var enumerator = selected.GetEnumerator())
  118. {
  119. for (var i = 0; enumerator.MoveNext(); i++)
  120. {
  121. var block = enumerator.Current;
  122. blockIDs[i] = block.Id;
  123. }
  124. }
  125. var serializationData = clipboardManager.GetSerializationData(blueprintID);
  126. SelectionSerializationUtility.ClearClipboard(playerID, entitiesDB, entityFunctions, serializationData.blueprintData, -1);
  127. if (selected.Count == 0)
  128. return;
  129. //ref BlockGroupTransformEntityComponent groupTransform = ref EntityNativeDBExtensions.QueryEntity<BlockGroupTransformEntityComponent>(entitiesDb, (uint) local1.currentBlockGroup, BlockGroupExclusiveGroups.BlockGroupEntityGroup);
  130. //ref ColliderAabb collider = ref EntityNativeDBExtensions.QueryEntity<ColliderAabb>(entitiesDB, (uint) groupID, BlockGroupExclusiveGroups.BlockGroupEntityGroup);
  131. //float3 bottomOffset = PlaceBlockUtility.GetBottomOffset(collider);
  132. //var rootPosition = math.mul(groupTransform.blockGroupGridRotation, bottomOffset) + groupTransform.blockGroupGridPosition;
  133. //var rootRotation = groupTransform.blockGroupGridRotation;
  134. clipboardManager.SetGhostSerialized(blueprintID, false);
  135. SelectionSerializationUtility.CopySelectionToClipboard(playerID, entitiesDB,
  136. serializationData.blueprintData, entitySerialization, entityFactory, blockIDs,
  137. (uint) blockIDs.Length, pos, rot, -1);
  138. BuildGhostBlueprint(selected, pos, rot, playerID);
  139. SerializeGhostBlueprint.Invoke(SerializeGhostBlueprintInstance, new object[] {playerID, blueprintID});
  140. }
  141. private void BuildGhostBlueprint(ICollection<Block> blocks, float3 pos, quaternion rot, uint playerID)
  142. {
  143. GhostChildUtility.ClearGhostChildren(playerID, entitiesDB, entityFunctions);
  144. foreach (var block in blocks)
  145. {
  146. GhostChildUtility.BuildGhostChild(in playerID, block.Id, in pos, in rot, entitiesDB,
  147. BuildGhostBlueprintFactory, false);
  148. }
  149. }
  150. public Block[] PlaceBlueprintBlocks(uint blueprintID, uint playerID, float3 pos, float3 rot)
  151. { //RobocraftX.CR.MachineEditing.PlaceBlueprintUtility.PlaceBlocksFromSerialisedData
  152. var serializationData = clipboardManager.GetSerializationData(blueprintID);
  153. var blueprintData = serializationData.blueprintData;
  154. blueprintData.dataPos = 0U;
  155. uint selectionSize;
  156. PositionEntityStruct selectionPosition;
  157. RotationEntityStruct selectionRotation;
  158. uint version;
  159. BoxSelectSerializationUtilities.ReadClipboardHeader(blueprintData, out selectionSize, out selectionPosition, out selectionRotation, out version);
  160. ((FasterList<EGID>) LocalBlockMap.GetValue(null)).Clear();
  161. if (version <= 1U)
  162. {
  163. uint groupsCount;
  164. BoxSelectSerializationUtilities.ReadBlockGroupData(blueprintData, out groupsCount);
  165. for (int index = 0; (long) index < (long) groupsCount; ++index)
  166. {
  167. int nextFilterId = BlockGroupUtility.NextFilterId;
  168. entitySerialization.DeserializeNewEntity(new EGID((uint) nextFilterId, BlockGroupExclusiveGroups.BlockGroupEntityGroup), blueprintData, 1);
  169. }
  170. }
  171. int nextFilterId1 = BlockGroupUtility.NextFilterId;
  172. entityFactory.BuildEntity<BlockGroupEntityDescriptor>(new EGID((uint) nextFilterId1,
  173. BlockGroupExclusiveGroups.BlockGroupEntityGroup)).Init(new BlockGroupTransformEntityComponent
  174. {
  175. blockGroupGridPosition = selectionPosition.position,
  176. blockGroupGridRotation = selectionRotation.rotation
  177. });
  178. var frot = Quaternion.Euler(rot);
  179. var grid = new GridRotationStruct {position = pos, rotation = frot};
  180. var poss = new PositionEntityStruct {position = pos};
  181. var rots = new RotationEntityStruct {rotation = frot};
  182. for (int index = 0; (long) index < (long) selectionSize; ++index)
  183. BuildBlock.Invoke(null,
  184. new object[]
  185. {
  186. playerID, grid, poss, rots, selectionPosition, selectionRotation, blueprintData,
  187. entitySerialization, nextFilterId1
  188. });
  189. /*
  190. uint playerId, in GridRotationStruct ghostParentGrid,
  191. in PositionEntityStruct ghostParentPosition, in RotationEntityStruct ghostParentRotation,
  192. in PositionEntityStruct selectionPosition, in RotationEntityStruct selectionRotation,
  193. ISerializationData serializationData, EntitiesDB entitiesDb,
  194. IEntitySerialization entitySerialization, int blockGroupId
  195. */
  196. if (globalBlockMap == null)
  197. globalBlockMap = FullGameFields._deserialisedBlockMap;
  198. var placedBlocks = (FasterList<EGID>) LocalBlockMap.GetValue(null);
  199. globalBlockMap.Clear();
  200. globalBlockMap.AddRange(placedBlocks);
  201. BuildWires.Invoke(null,
  202. new object[] {playerID, blueprintData, entitySerialization, entitiesDB, entityFactory});
  203. var blocks = new Block[placedBlocks.count];
  204. for (int i = 0; i < blocks.Length; i++)
  205. blocks[i] = new Block(placedBlocks[i]);
  206. return blocks;
  207. }
  208. public void GetBlueprintInfo(uint blueprintID, out float3 pos, out quaternion rot, out uint selectionSize)
  209. {
  210. var serializationData = clipboardManager.GetSerializationData(blueprintID);
  211. var blueprintData = serializationData.blueprintData;
  212. blueprintData.dataPos = 0U;
  213. BoxSelectSerializationUtilities.ReadClipboardHeader(blueprintData, out selectionSize, out var posst,
  214. out var rotst, out _);
  215. blueprintData.dataPos = 0U; //Just to be sure, it gets reset when it's read anyway
  216. pos = posst.position;
  217. rot = rotst.rotation;
  218. }
  219. public void InitBlueprint(uint blueprintID)
  220. {
  221. clipboardManager.IncrementRefCount(blueprintID);
  222. }
  223. public void DisposeBlueprint(uint blueprintID)
  224. {
  225. clipboardManager.DecrementRefCount(blueprintID);
  226. }
  227. public string Name { get; } = "TechbloxModdingAPIBlueprintGameEngine";
  228. public bool isRemovable { get; } = false;
  229. [HarmonyPatch]
  230. private static class RemoveEnginePatch
  231. {
  232. public static void Prefix(IEntityFunctions entityFunctions,
  233. MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory)
  234. {
  235. nativeRemove = entityFunctions.ToNativeRemove<BlockEntityDescriptor>("GCAPI" + nameof(BlueprintEngine));
  236. connectionFactory = machineGraphConnectionEntityFactory;
  237. BlueprintEngine.entityFunctions = entityFunctions;
  238. }
  239. public static MethodBase TargetMethod()
  240. {
  241. return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine"))[0];
  242. }
  243. }
  244. [HarmonyPatch]
  245. private static class SelectEnginePatch
  246. {
  247. public static void Prefix(ClipboardSerializationDataResourceManager clipboardSerializationDataResourceManager,
  248. IEntitySerialization entitySerialization,
  249. IEntityFactory entityFactory)
  250. {
  251. clipboardManager = clipboardSerializationDataResourceManager;
  252. BlueprintEngine.entitySerialization = entitySerialization;
  253. BlueprintEngine.entityFactory = entityFactory;
  254. }
  255. public static MethodBase TargetMethod()
  256. {
  257. return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.SelectBlockEngine"))[0];
  258. }
  259. }
  260. [HarmonyPatch]
  261. private static class SerializeGhostBlueprintPatch
  262. {
  263. public static void Postfix(object __instance)
  264. {
  265. SerializeGhostBlueprintInstance = __instance;
  266. }
  267. public static MethodBase TargetMethod()
  268. {
  269. return AccessTools.GetDeclaredConstructors(SerializeGhostBlueprintType)[0];
  270. }
  271. }
  272. [HarmonyPatch]
  273. private static class BuildGhostBlueprintPatch
  274. {
  275. public static void Postfix(GhostChildEntityFactory ghostChildEntityFactory)
  276. {
  277. BuildGhostBlueprintFactory = ghostChildEntityFactory;
  278. }
  279. public static MethodBase TargetMethod()
  280. {
  281. return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.BuildGhostChildForMultiblockPickEngine"))[0];
  282. }
  283. }
  284. public IEntityFactory Factory { get; set; }
  285. }
  286. }