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.

404 lines
19KB

  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.Blocks.Ghost;
  9. using RobocraftX.Common;
  10. using RobocraftX.CR.MachineEditing.BoxSelect;
  11. using RobocraftX.CR.MachineEditing.BoxSelect.ClipboardOperations;
  12. using RobocraftX.Physics;
  13. using RobocraftX.Rendering;
  14. using RobocraftX.Rendering.GPUI;
  15. using Svelto.DataStructures;
  16. using Svelto.ECS;
  17. using Svelto.ECS.DataStructures;
  18. using Svelto.ECS.EntityStructs;
  19. using Svelto.ECS.Native;
  20. using Svelto.ECS.Serialization;
  21. using Techblox.Blocks.Connections;
  22. using TechbloxModdingAPI.Engines;
  23. using TechbloxModdingAPI.Utility;
  24. using TechbloxModdingAPI.Utility.ECS;
  25. using Unity.Collections;
  26. using Unity.Mathematics;
  27. using UnityEngine;
  28. using Allocator = Svelto.Common.Allocator;
  29. namespace TechbloxModdingAPI.Blocks.Engines
  30. {
  31. public class BlueprintEngine : IFactoryEngine
  32. {
  33. private readonly MethodInfo getBlocksFromGroup =
  34. AccessTools.Method("RobocraftX.CR.MachineEditing.PlaceBlockUtility:GetBlocksSharingBlockgroup");
  35. private NativeDynamicArray selectedBlocksInGroup;
  36. private NativeHashSet<ulong> removedConnections = new NativeHashSet<ulong>();
  37. private int addingToBlockGroup = -1;
  38. private static readonly Type PlaceBlueprintUtilityType =
  39. AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceBlueprintUtility");
  40. private static readonly FieldInfo LocalBlockMap =
  41. AccessTools.DeclaredField(PlaceBlueprintUtilityType, "_localBlockMap");
  42. private static readonly MethodInfo BuildBlock = AccessTools.Method(PlaceBlueprintUtilityType, "BuildBlock");
  43. private static readonly MethodInfo BuildWires = AccessTools.Method(PlaceBlueprintUtilityType, "BuildWires");
  44. private static readonly Type SerializeGhostBlueprintType =
  45. AccessTools.TypeByName("RobocraftX.CR.MachineEditing.BoxSelect.SerializeGhostChildrenOnAddEngine");
  46. private static readonly MethodInfo SerializeGhostBlueprint =
  47. AccessTools.Method(SerializeGhostBlueprintType, "SerializeClipboardGhostEntities");
  48. private static NativeEntityRemove nativeBlockRemove;
  49. private static NativeEntityRemove nativeConnectionRemove;
  50. private static MachineGraphConnectionEntityFactory connectionFactory;
  51. private static IEntityFunctions entityFunctions;
  52. private static ClipboardSerializationDataResourceManager clipboardManager;
  53. private static IEntitySerialization entitySerialization;
  54. private static IEntityFactory entityFactory;
  55. private static FasterList<EGID> globalBlockMap;
  56. private static object SerializeGhostBlueprintInstance;
  57. private static GhostChildEntityFactory BuildGhostBlueprintFactory;
  58. public void Ready()
  59. {
  60. selectedBlocksInGroup = NativeDynamicArray.Alloc<EGID>(Allocator.Persistent);
  61. }
  62. public EntitiesDB entitiesDB { get; set; }
  63. public void Dispose()
  64. {
  65. selectedBlocksInGroup.Dispose();
  66. }
  67. public Block[] GetBlocksFromGroup(EGID blockID, out float3 pos, out quaternion rot)
  68. {
  69. var blockPos = default(float3);
  70. var blockRot = default(quaternion);
  71. var parameters = new object[] {blockID, selectedBlocksInGroup, entitiesDB, blockPos, blockRot};
  72. getBlocksFromGroup.Invoke(null, parameters);
  73. pos = (float3) parameters[3];
  74. rot = (quaternion) parameters[4];
  75. int count = selectedBlocksInGroup.Count<EGID>();
  76. var ret = new Block[count];
  77. for (uint i = 0; i < count; i++)
  78. ret[i] = Block.New(selectedBlocksInGroup.Get<EGID>(i));
  79. selectedBlocksInGroup.FastClear();
  80. return ret;
  81. }
  82. public void RemoveBlockGroup(int id)
  83. {
  84. BlockGroupUtility.RemoveAllBlocksInBlockGroup(id, entitiesDB, removedConnections, nativeBlockRemove,
  85. nativeConnectionRemove, connectionFactory, default).Complete();
  86. }
  87. public int CreateBlockGroup(float3 position, quaternion rotation)
  88. {
  89. int nextFilterId = BlockGroupUtility.NextFilterId;
  90. Factory.BuildEntity<BlockGroupEntityDescriptor>((uint) nextFilterId,
  91. BlockGroupExclusiveGroups.BlockGroupEntityGroup).Init(new BlockGroupTransformEntityComponent
  92. {
  93. blockGroupGridRotation = rotation,
  94. blockGroupGridPosition = position
  95. });
  96. return nextFilterId;
  97. }
  98. public void AddBlockToGroup(EGID blockID, int groupID)
  99. {
  100. if (globalBlockMap == null)
  101. globalBlockMap = FullGameFields._deserialisedBlockMap;
  102. if (groupID != addingToBlockGroup)
  103. {
  104. Logging.MetaDebugLog("Changing current block group from " + addingToBlockGroup + " to " + groupID);
  105. addingToBlockGroup = groupID;
  106. globalBlockMap.Clear();
  107. }
  108. globalBlockMap.Add(blockID);
  109. }
  110. public void SelectBlueprint(uint resourceID)
  111. {
  112. if (resourceID == uint.MaxValue)
  113. BlueprintUtil.UnselectBlueprint(entitiesDB);
  114. else
  115. BlueprintUtil.SelectBlueprint(entitiesDB, resourceID, false, -1);
  116. }
  117. public uint CreateBlueprint()
  118. {
  119. uint index = clipboardManager.AllocateSerializationData();
  120. return index;
  121. }
  122. public void ReplaceBlueprint(uint playerID, uint blueprintID, ICollection<Block> selected, float3 pos, quaternion rot)
  123. {
  124. var blockIDs = new EGID[selected.Count];
  125. using (var enumerator = selected.GetEnumerator())
  126. {
  127. for (var i = 0; enumerator.MoveNext(); i++)
  128. {
  129. var block = enumerator.Current;
  130. blockIDs[i] = block.Id;
  131. }
  132. }
  133. var serializationData = clipboardManager.GetSerializationData(blueprintID);
  134. SelectionSerializationUtility.ClearClipboard(playerID, entitiesDB, entityFunctions, serializationData.blueprintData, -1);
  135. if (selected.Count == 0)
  136. return;
  137. //ref BlockGroupTransformEntityComponent groupTransform = ref EntityNativeDBExtensions.QueryEntity<BlockGroupTransformEntityComponent>(entitiesDb, (uint) local1.currentBlockGroup, BlockGroupExclusiveGroups.BlockGroupEntityGroup);
  138. //ref ColliderAabb collider = ref EntityNativeDBExtensions.QueryEntity<ColliderAabb>(entitiesDB, (uint) groupID, BlockGroupExclusiveGroups.BlockGroupEntityGroup);
  139. //float3 bottomOffset = PlaceBlockUtility.GetBottomOffset(collider);
  140. //var rootPosition = math.mul(groupTransform.blockGroupGridRotation, bottomOffset) + groupTransform.blockGroupGridPosition;
  141. //var rootRotation = groupTransform.blockGroupGridRotation;
  142. clipboardManager.SetGhostSerialized(blueprintID, false);
  143. SelectionSerializationUtility.CopySelectionToClipboard(playerID, entitiesDB,
  144. serializationData.blueprintData, entitySerialization, entityFactory, blockIDs,
  145. (uint) blockIDs.Length, pos, rot, -1);
  146. BuildGhostBlueprint(selected, pos, rot, playerID);
  147. SerializeGhostBlueprint.Invoke(SerializeGhostBlueprintInstance, new object[] {playerID, blueprintID});
  148. }
  149. private void BuildGhostBlueprint(ICollection<Block> blocks, float3 pos, quaternion rot, uint playerID)
  150. {
  151. GhostChildUtility.ClearGhostChildren(playerID, entitiesDB, entityFunctions);
  152. var bssesopt = entitiesDB.QueryEntityOptional<BoxSelectStateEntityStruct>(new EGID(playerID,
  153. BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup));
  154. if (!bssesopt)
  155. return;
  156. foreach (var block in blocks)
  157. {
  158. GhostChildUtility.BuildGhostChild(in playerID, block.Id, in pos, in rot, entitiesDB,
  159. BuildGhostBlueprintFactory, false, bssesopt.Get().buildingDroneReference,
  160. FullGameFields._managers.blockLabelResourceManager);
  161. }
  162. }
  163. public Block[] PlaceBlueprintBlocks(uint blueprintID, uint playerID, float3 pos, float3 rot)
  164. { //RobocraftX.CR.MachineEditing.PlaceBlueprintUtility.PlaceBlocksFromSerialisedData
  165. var serializationData = clipboardManager.GetSerializationData(blueprintID);
  166. var blueprintData = serializationData.blueprintData;
  167. blueprintData.dataPos = 0U;
  168. uint selectionSize;
  169. PositionEntityStruct selectionPosition;
  170. RotationEntityStruct selectionRotation;
  171. uint version;
  172. BoxSelectSerializationUtilities.ReadClipboardHeader(blueprintData, out selectionSize, out selectionPosition, out selectionRotation, out version);
  173. ((FasterList<EGID>) LocalBlockMap.GetValue(null)).Clear();
  174. if (version <= 1U)
  175. {
  176. uint groupsCount;
  177. BoxSelectSerializationUtilities.ReadBlockGroupData(blueprintData, out groupsCount);
  178. for (int index = 0; (long) index < (long) groupsCount; ++index)
  179. {
  180. int nextFilterId = BlockGroupUtility.NextFilterId;
  181. entitySerialization.DeserializeNewEntity(new EGID((uint) nextFilterId, BlockGroupExclusiveGroups.BlockGroupEntityGroup), blueprintData, 1);
  182. }
  183. }
  184. int nextFilterId1 = BlockGroupUtility.NextFilterId;
  185. entityFactory.BuildEntity<BlockGroupEntityDescriptor>(new EGID((uint) nextFilterId1,
  186. BlockGroupExclusiveGroups.BlockGroupEntityGroup)).Init(new BlockGroupTransformEntityComponent
  187. {
  188. blockGroupGridPosition = selectionPosition.position,
  189. blockGroupGridRotation = selectionRotation.rotation
  190. });
  191. var frot = Quaternion.Euler(rot);
  192. var grid = new GridRotationStruct {position = pos, rotation = frot};
  193. var poss = new PositionEntityStruct {position = pos};
  194. var rots = new RotationEntityStruct {rotation = frot};
  195. for (int index = 0; (long) index < (long) selectionSize; ++index)
  196. BuildBlock.Invoke(null,
  197. new object[]
  198. {
  199. playerID, grid, poss, rots, selectionPosition, selectionRotation, blueprintData,
  200. entitySerialization, nextFilterId1
  201. });
  202. /*
  203. uint playerId, in GridRotationStruct ghostParentGrid,
  204. in PositionEntityStruct ghostParentPosition, in RotationEntityStruct ghostParentRotation,
  205. in PositionEntityStruct selectionPosition, in RotationEntityStruct selectionRotation,
  206. ISerializationData serializationData, EntitiesDB entitiesDb,
  207. IEntitySerialization entitySerialization, int blockGroupId
  208. */
  209. if (globalBlockMap == null)
  210. globalBlockMap = FullGameFields._deserialisedBlockMap;
  211. var placedBlocks = (FasterList<EGID>) LocalBlockMap.GetValue(null);
  212. globalBlockMap.Clear();
  213. globalBlockMap.AddRange(placedBlocks);
  214. BuildWires.Invoke(null,
  215. new object[] {playerID, blueprintData, entitySerialization, entitiesDB, entityFactory});
  216. var blocks = new Block[placedBlocks.count];
  217. for (int i = 0; i < blocks.Length; i++)
  218. blocks[i] = Block.New(placedBlocks[i]);
  219. return blocks;
  220. }
  221. public void GetBlueprintInfo(uint blueprintID, out float3 pos, out quaternion rot, out uint selectionSize)
  222. {
  223. var serializationData = clipboardManager.GetSerializationData(blueprintID);
  224. var blueprintData = serializationData.blueprintData;
  225. blueprintData.dataPos = 0U;
  226. BoxSelectSerializationUtilities.ReadClipboardHeader(blueprintData, out selectionSize, out var posst,
  227. out var rotst, out _);
  228. blueprintData.dataPos = 0U; //Just to be sure, it gets reset when it's read anyway
  229. pos = posst.position;
  230. rot = rotst.rotation;
  231. }
  232. public void InitBlueprint(uint blueprintID)
  233. {
  234. clipboardManager.IncrementRefCount(blueprintID);
  235. }
  236. public void DisposeBlueprint(uint blueprintID)
  237. {
  238. clipboardManager.DecrementRefCount(blueprintID);
  239. }
  240. //GhostChildUtility.BuildGhostChild
  241. public Block BuildGhostChild()
  242. {
  243. var sourceId = new EGID(Player.LocalPlayer.Id, GHOST_BLOCKS_ENABLED.Group);
  244. var positionEntityStruct = entitiesDB.QueryEntity<PositionEntityStruct>(sourceId);
  245. var rotationEntityStruct = entitiesDB.QueryEntity<RotationEntityStruct>(sourceId);
  246. var scalingEntityStruct = entitiesDB.QueryEntity<ScalingEntityStruct>(sourceId);
  247. var dbStruct = entitiesDB.QueryEntity<DBEntityStruct>(sourceId);
  248. var colliderStruct = entitiesDB.QueryEntity<ColliderAabb>(sourceId);
  249. var colorStruct = entitiesDB.QueryEntity<ColourParameterEntityStruct>(sourceId);
  250. uint ghostChildBlockId = CommonExclusiveGroups.GetNewGhostChildBlockID();
  251. var ghostEntityReference = GhostBlockUtils.GetGhostEntityReference(sourceId.entityID, entitiesDB);
  252. var entityInitializer = BuildGhostBlueprintFactory.Build(
  253. new EGID(ghostChildBlockId, BoxSelectExclusiveGroups.GhostChildEntitiesExclusiveGroup), /*dbStruct.DBID*/ (uint)BlockIDs.Cube,
  254. FullGameFields._managers.blockLabelResourceManager);
  255. entityInitializer.Init(dbStruct);
  256. entityInitializer.Init(new GFXPrefabEntityStructGPUI(
  257. PrefabsID.GetOrAddPrefabID((ushort)entityInitializer.Get<PrefabAssetIDComponent>().prefabAssetID,
  258. entitiesDB.QueryEntity<CubeMaterialStruct>(sourceId).materialId, 7,
  259. FlippedBlockUtils.IsFlipped(in scalingEntityStruct.scale)), true));
  260. entityInitializer.Init(entitiesDB.QueryEntity<CubeMaterialStruct>(sourceId));
  261. entityInitializer.Init(new GhostParentEntityStruct
  262. {
  263. ghostBlockParentEntityReference = ghostEntityReference,
  264. ownerMustSerializeOnAdd = false
  265. });
  266. entityInitializer.Init(colorStruct);
  267. entityInitializer.Init(colliderStruct);
  268. entityInitializer.Init(new RigidBodyEntityStruct
  269. {
  270. position = positionEntityStruct.position,
  271. rotation = rotationEntityStruct.rotation
  272. });
  273. entityInitializer.Init(new ScalingEntityStruct
  274. {
  275. scale = scalingEntityStruct.scale
  276. });
  277. entityInitializer.Init(new LocalTransformEntityStruct
  278. {
  279. position = positionEntityStruct.position,
  280. rotation = rotationEntityStruct.rotation
  281. });
  282. entityInitializer.Init(new RotationEntityStruct
  283. {
  284. rotation = rotationEntityStruct.rotation
  285. });
  286. entityInitializer.Init(new PositionEntityStruct
  287. {
  288. position = positionEntityStruct.position
  289. });
  290. entityInitializer.Init(new SkewComponent
  291. {
  292. skewMatrix = entitiesDB.QueryEntity<SkewComponent>(sourceId).skewMatrix
  293. });
  294. entityInitializer.Init(new BlockPlacementInfoStruct
  295. {
  296. placedByBuildingDrone = entitiesDB
  297. .QueryEntityOptional<BoxSelectStateEntityStruct>(new EGID(Player.LocalPlayer.Id,
  298. BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)).Get().buildingDroneReference
  299. });
  300. entityInitializer.Init(new GridRotationStruct
  301. {
  302. position = float3.zero,
  303. rotation = quaternion.identity
  304. });
  305. var block = Block.New(entityInitializer.EGID);
  306. block.InitData = entityInitializer;
  307. return block;
  308. }
  309. public string Name { get; } = "TechbloxModdingAPIBlueprintGameEngine";
  310. public bool isRemovable { get; } = false;
  311. [HarmonyPatch]
  312. private static class RemoveEnginePatch
  313. {
  314. public static void Prefix(IEntityFunctions entityFunctions,
  315. MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory)
  316. {
  317. nativeBlockRemove = entityFunctions.ToNativeRemove<BlockEntityDescriptor>("TBAPI" + nameof(BlueprintEngine));
  318. nativeConnectionRemove = entityFunctions.ToNativeRemove<MachineConnectionEntityDescriptor>("TBAPI" + nameof(BlueprintEngine));
  319. connectionFactory = machineGraphConnectionEntityFactory;
  320. BlueprintEngine.entityFunctions = entityFunctions;
  321. }
  322. public static MethodBase TargetMethod()
  323. {
  324. return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine"))[0];
  325. }
  326. }
  327. [HarmonyPatch]
  328. private static class SelectEnginePatch
  329. {
  330. public static void Prefix(ClipboardSerializationDataResourceManager clipboardSerializationDataResourceManager,
  331. IEntitySerialization entitySerialization,
  332. IEntityFactory entityFactory)
  333. {
  334. clipboardManager = clipboardSerializationDataResourceManager;
  335. BlueprintEngine.entitySerialization = entitySerialization;
  336. BlueprintEngine.entityFactory = entityFactory;
  337. }
  338. public static MethodBase TargetMethod()
  339. {
  340. return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.SelectBlockEngine"))[0];
  341. }
  342. }
  343. [HarmonyPatch]
  344. private static class SerializeGhostBlueprintPatch
  345. {
  346. public static void Postfix(object __instance)
  347. {
  348. SerializeGhostBlueprintInstance = __instance;
  349. }
  350. public static MethodBase TargetMethod()
  351. {
  352. return AccessTools.GetDeclaredConstructors(SerializeGhostBlueprintType)[0];
  353. }
  354. }
  355. [HarmonyPatch]
  356. private static class BuildGhostBlueprintPatch
  357. {
  358. public static void Postfix(GhostChildEntityFactory ghostChildEntityFactory)
  359. {
  360. BuildGhostBlueprintFactory = ghostChildEntityFactory;
  361. }
  362. public static MethodBase TargetMethod()
  363. {
  364. return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.BuildGhostChildForMultiblockPickEngine"))[0];
  365. }
  366. }
  367. public IEntityFactory Factory { get; set; }
  368. }
  369. }