using System; using System.Collections.Generic; using System.Reflection; using Gamecraft.Blocks.BlockGroups; using Gamecraft.GUI.Blueprints; using HarmonyLib; using RobocraftX.Blocks; using RobocraftX.Blocks.Ghost; using RobocraftX.Common; using RobocraftX.CR.MachineEditing.BoxSelect; using RobocraftX.CR.MachineEditing.BoxSelect.ClipboardOperations; using RobocraftX.Physics; using RobocraftX.Rendering; using RobocraftX.Rendering.GPUI; using Svelto.DataStructures; using Svelto.ECS; using Svelto.ECS.DataStructures; using Svelto.ECS.EntityStructs; using Svelto.ECS.Native; using Svelto.ECS.Serialization; using Techblox.Blocks.Connections; using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility.ECS; using Unity.Collections; using Unity.Mathematics; using UnityEngine; using Allocator = Svelto.Common.Allocator; namespace TechbloxModdingAPI.Blocks.Engines { public class BlueprintEngine : IFactoryEngine { private readonly MethodInfo getBlocksFromGroup = AccessTools.Method("RobocraftX.CR.MachineEditing.PlaceBlockUtility:GetBlocksSharingBlockgroup"); private NativeDynamicArray selectedBlocksInGroup; private NativeHashSet removedConnections = new NativeHashSet(); private int addingToBlockGroup = -1; private static readonly Type PlaceBlueprintUtilityType = AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceBlueprintUtility"); private static readonly FieldInfo LocalBlockMap = AccessTools.DeclaredField(PlaceBlueprintUtilityType, "_localBlockMap"); private static readonly MethodInfo BuildBlock = AccessTools.Method(PlaceBlueprintUtilityType, "BuildBlock"); private static readonly MethodInfo BuildWires = AccessTools.Method(PlaceBlueprintUtilityType, "BuildWires"); private static readonly Type SerializeGhostBlueprintType = AccessTools.TypeByName("RobocraftX.CR.MachineEditing.BoxSelect.SerializeGhostChildrenOnAddEngine"); private static readonly MethodInfo SerializeGhostBlueprint = AccessTools.Method(SerializeGhostBlueprintType, "SerializeClipboardGhostEntities"); private static NativeEntityRemove nativeBlockRemove; private static NativeEntityRemove nativeConnectionRemove; private static MachineGraphConnectionEntityFactory connectionFactory; private static IEntityFunctions entityFunctions; private static ClipboardSerializationDataResourceManager clipboardManager; private static IEntitySerialization entitySerialization; private static IEntityFactory entityFactory; private static FasterList globalBlockMap; private static object SerializeGhostBlueprintInstance; private static GhostChildEntityFactory BuildGhostBlueprintFactory; public void Ready() { selectedBlocksInGroup = NativeDynamicArray.Alloc(Allocator.Persistent); } public EntitiesDB entitiesDB { get; set; } public void Dispose() { selectedBlocksInGroup.Dispose(); } public Block[] GetBlocksFromGroup(EGID blockID, out float3 pos, out quaternion rot) { var blockPos = default(float3); var blockRot = default(quaternion); var parameters = new object[] {blockID, selectedBlocksInGroup, entitiesDB, blockPos, blockRot}; getBlocksFromGroup.Invoke(null, parameters); pos = (float3) parameters[3]; rot = (quaternion) parameters[4]; int count = selectedBlocksInGroup.Count(); var ret = new Block[count]; for (uint i = 0; i < count; i++) ret[i] = Block.New(selectedBlocksInGroup.Get(i)); selectedBlocksInGroup.FastClear(); return ret; } public void RemoveBlockGroup(int id) { BlockGroupUtility.RemoveAllBlocksInBlockGroup(id, entitiesDB, removedConnections, nativeBlockRemove, nativeConnectionRemove, connectionFactory, default).Complete(); } public int CreateBlockGroup(float3 position, quaternion rotation) { int nextFilterId = BlockGroupUtility.NextFilterId; Factory.BuildEntity((uint) nextFilterId, BlockGroupExclusiveGroups.BlockGroupEntityGroup).Init(new BlockGroupTransformEntityComponent { blockGroupGridRotation = rotation, blockGroupGridPosition = position }); return nextFilterId; } public void AddBlockToGroup(EGID blockID, int groupID) { if (globalBlockMap == null) globalBlockMap = FullGameFields._deserialisedBlockMap; if (groupID != addingToBlockGroup) { Logging.MetaDebugLog("Changing current block group from " + addingToBlockGroup + " to " + groupID); addingToBlockGroup = groupID; globalBlockMap.Clear(); } globalBlockMap.Add(blockID); } public void SelectBlueprint(uint resourceID) { if (resourceID == uint.MaxValue) BlueprintUtil.UnselectBlueprint(entitiesDB); else BlueprintUtil.SelectBlueprint(entitiesDB, resourceID, false, -1); } public uint CreateBlueprint() { uint index = clipboardManager.AllocateSerializationData(); return index; } public void ReplaceBlueprint(uint playerID, uint blueprintID, ICollection selected, float3 pos, quaternion rot) { var blockIDs = new EGID[selected.Count]; using (var enumerator = selected.GetEnumerator()) { for (var i = 0; enumerator.MoveNext(); i++) { var block = enumerator.Current; blockIDs[i] = block.Id; } } var serializationData = clipboardManager.GetSerializationData(blueprintID); SelectionSerializationUtility.ClearClipboard(playerID, entitiesDB, entityFunctions, serializationData.blueprintData, -1); if (selected.Count == 0) return; //ref BlockGroupTransformEntityComponent groupTransform = ref EntityNativeDBExtensions.QueryEntity(entitiesDb, (uint) local1.currentBlockGroup, BlockGroupExclusiveGroups.BlockGroupEntityGroup); //ref ColliderAabb collider = ref EntityNativeDBExtensions.QueryEntity(entitiesDB, (uint) groupID, BlockGroupExclusiveGroups.BlockGroupEntityGroup); //float3 bottomOffset = PlaceBlockUtility.GetBottomOffset(collider); //var rootPosition = math.mul(groupTransform.blockGroupGridRotation, bottomOffset) + groupTransform.blockGroupGridPosition; //var rootRotation = groupTransform.blockGroupGridRotation; clipboardManager.SetGhostSerialized(blueprintID, false); SelectionSerializationUtility.CopySelectionToClipboard(playerID, entitiesDB, serializationData.blueprintData, entitySerialization, entityFactory, blockIDs, (uint) blockIDs.Length, pos, rot, -1); BuildGhostBlueprint(selected, pos, rot, playerID); SerializeGhostBlueprint.Invoke(SerializeGhostBlueprintInstance, new object[] {playerID, blueprintID}); } private void BuildGhostBlueprint(ICollection blocks, float3 pos, quaternion rot, uint playerID) { GhostChildUtility.ClearGhostChildren(playerID, entitiesDB, entityFunctions); var bssesopt = entitiesDB.QueryEntityOptional(new EGID(playerID, BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)); if (!bssesopt) return; foreach (var block in blocks) { GhostChildUtility.BuildGhostChild(in playerID, block.Id, in pos, in rot, entitiesDB, BuildGhostBlueprintFactory, false, bssesopt.Get().buildingDroneReference, FullGameFields._managers.blockLabelResourceManager); } } public Block[] PlaceBlueprintBlocks(uint blueprintID, uint playerID, float3 pos, float3 rot) { //RobocraftX.CR.MachineEditing.PlaceBlueprintUtility.PlaceBlocksFromSerialisedData var serializationData = clipboardManager.GetSerializationData(blueprintID); var blueprintData = serializationData.blueprintData; blueprintData.dataPos = 0U; uint selectionSize; PositionEntityStruct selectionPosition; RotationEntityStruct selectionRotation; uint version; BoxSelectSerializationUtilities.ReadClipboardHeader(blueprintData, out selectionSize, out selectionPosition, out selectionRotation, out version); ((FasterList) LocalBlockMap.GetValue(null)).Clear(); if (version <= 1U) { uint groupsCount; BoxSelectSerializationUtilities.ReadBlockGroupData(blueprintData, out groupsCount); for (int index = 0; (long) index < (long) groupsCount; ++index) { int nextFilterId = BlockGroupUtility.NextFilterId; entitySerialization.DeserializeNewEntity(new EGID((uint) nextFilterId, BlockGroupExclusiveGroups.BlockGroupEntityGroup), blueprintData, 1); } } int nextFilterId1 = BlockGroupUtility.NextFilterId; entityFactory.BuildEntity(new EGID((uint) nextFilterId1, BlockGroupExclusiveGroups.BlockGroupEntityGroup)).Init(new BlockGroupTransformEntityComponent { blockGroupGridPosition = selectionPosition.position, blockGroupGridRotation = selectionRotation.rotation }); var frot = Quaternion.Euler(rot); var grid = new GridRotationStruct {position = pos, rotation = frot}; var poss = new PositionEntityStruct {position = pos}; var rots = new RotationEntityStruct {rotation = frot}; for (int index = 0; (long) index < (long) selectionSize; ++index) BuildBlock.Invoke(null, new object[] { playerID, grid, poss, rots, selectionPosition, selectionRotation, blueprintData, entitySerialization, nextFilterId1 }); /* uint playerId, in GridRotationStruct ghostParentGrid, in PositionEntityStruct ghostParentPosition, in RotationEntityStruct ghostParentRotation, in PositionEntityStruct selectionPosition, in RotationEntityStruct selectionRotation, ISerializationData serializationData, EntitiesDB entitiesDb, IEntitySerialization entitySerialization, int blockGroupId */ if (globalBlockMap == null) globalBlockMap = FullGameFields._deserialisedBlockMap; var placedBlocks = (FasterList) LocalBlockMap.GetValue(null); globalBlockMap.Clear(); globalBlockMap.AddRange(placedBlocks); BuildWires.Invoke(null, new object[] {playerID, blueprintData, entitySerialization, entitiesDB, entityFactory}); var blocks = new Block[placedBlocks.count]; for (int i = 0; i < blocks.Length; i++) blocks[i] = Block.New(placedBlocks[i]); return blocks; } public void GetBlueprintInfo(uint blueprintID, out float3 pos, out quaternion rot, out uint selectionSize) { var serializationData = clipboardManager.GetSerializationData(blueprintID); var blueprintData = serializationData.blueprintData; blueprintData.dataPos = 0U; BoxSelectSerializationUtilities.ReadClipboardHeader(blueprintData, out selectionSize, out var posst, out var rotst, out _); blueprintData.dataPos = 0U; //Just to be sure, it gets reset when it's read anyway pos = posst.position; rot = rotst.rotation; } public void InitBlueprint(uint blueprintID) { clipboardManager.IncrementRefCount(blueprintID); } public void DisposeBlueprint(uint blueprintID) { clipboardManager.DecrementRefCount(blueprintID); } //GhostChildUtility.BuildGhostChild public Block BuildGhostChild() { var sourceId = new EGID(Player.LocalPlayer.Id, GHOST_BLOCKS_ENABLED.Group); var positionEntityStruct = entitiesDB.QueryEntity(sourceId); var rotationEntityStruct = entitiesDB.QueryEntity(sourceId); var scalingEntityStruct = entitiesDB.QueryEntity(sourceId); var dbStruct = entitiesDB.QueryEntity(sourceId); var colliderStruct = entitiesDB.QueryEntity(sourceId); var colorStruct = entitiesDB.QueryEntity(sourceId); uint ghostChildBlockId = CommonExclusiveGroups.GetNewGhostChildBlockID(); var ghostEntityReference = GhostBlockUtils.GetGhostEntityReference(sourceId.entityID, entitiesDB); var entityInitializer = BuildGhostBlueprintFactory.Build( new EGID(ghostChildBlockId, BoxSelectExclusiveGroups.GhostChildEntitiesExclusiveGroup), /*dbStruct.DBID*/ (uint)BlockIDs.Cube, FullGameFields._managers.blockLabelResourceManager); entityInitializer.Init(dbStruct); entityInitializer.Init(new GFXPrefabEntityStructGPUI( PrefabsID.GetOrAddPrefabID((ushort)entityInitializer.Get().prefabAssetID, entitiesDB.QueryEntity(sourceId).materialId, 7, FlippedBlockUtils.IsFlipped(in scalingEntityStruct.scale)), true)); entityInitializer.Init(entitiesDB.QueryEntity(sourceId)); entityInitializer.Init(new GhostParentEntityStruct { ghostBlockParentEntityReference = ghostEntityReference, ownerMustSerializeOnAdd = false }); entityInitializer.Init(colorStruct); entityInitializer.Init(colliderStruct); entityInitializer.Init(new RigidBodyEntityStruct { position = positionEntityStruct.position, rotation = rotationEntityStruct.rotation }); entityInitializer.Init(new ScalingEntityStruct { scale = scalingEntityStruct.scale }); entityInitializer.Init(new LocalTransformEntityStruct { position = positionEntityStruct.position, rotation = rotationEntityStruct.rotation }); entityInitializer.Init(new RotationEntityStruct { rotation = rotationEntityStruct.rotation }); entityInitializer.Init(new PositionEntityStruct { position = positionEntityStruct.position }); entityInitializer.Init(new SkewComponent { skewMatrix = entitiesDB.QueryEntity(sourceId).skewMatrix }); entityInitializer.Init(new BlockPlacementInfoStruct { placedByBuildingDrone = entitiesDB .QueryEntityOptional(new EGID(Player.LocalPlayer.Id, BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)).Get().buildingDroneReference }); entityInitializer.Init(new GridRotationStruct { position = float3.zero, rotation = quaternion.identity }); var block = Block.New(entityInitializer.EGID); block.InitData = entityInitializer; return block; } public string Name { get; } = "TechbloxModdingAPIBlueprintGameEngine"; public bool isRemovable { get; } = false; [HarmonyPatch] private static class RemoveEnginePatch { public static void Prefix(IEntityFunctions entityFunctions, MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory) { nativeBlockRemove = entityFunctions.ToNativeRemove("TBAPI" + nameof(BlueprintEngine)); nativeConnectionRemove = entityFunctions.ToNativeRemove("TBAPI" + nameof(BlueprintEngine)); connectionFactory = machineGraphConnectionEntityFactory; BlueprintEngine.entityFunctions = entityFunctions; } public static MethodBase TargetMethod() { return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine"))[0]; } } [HarmonyPatch] private static class SelectEnginePatch { public static void Prefix(ClipboardSerializationDataResourceManager clipboardSerializationDataResourceManager, IEntitySerialization entitySerialization, IEntityFactory entityFactory) { clipboardManager = clipboardSerializationDataResourceManager; BlueprintEngine.entitySerialization = entitySerialization; BlueprintEngine.entityFactory = entityFactory; } public static MethodBase TargetMethod() { return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.SelectBlockEngine"))[0]; } } [HarmonyPatch] private static class SerializeGhostBlueprintPatch { public static void Postfix(object __instance) { SerializeGhostBlueprintInstance = __instance; } public static MethodBase TargetMethod() { return AccessTools.GetDeclaredConstructors(SerializeGhostBlueprintType)[0]; } } [HarmonyPatch] private static class BuildGhostBlueprintPatch { public static void Postfix(GhostChildEntityFactory ghostChildEntityFactory) { BuildGhostBlueprintFactory = ghostChildEntityFactory; } public static MethodBase TargetMethod() { return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.BuildGhostChildForMultiblockPickEngine"))[0]; } } public IEntityFactory Factory { get; set; } } }