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.

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