From 2179ba6386f96fe0a6f27491b7d668a3f0c2ed35 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Tue, 10 Nov 2020 19:28:36 +0100 Subject: [PATCH] Add support for setting and placing blueprints --- GamecraftModdingAPI/Blocks/BlueprintEngine.cs | 145 +++++++++++++++++- GamecraftModdingAPI/Blueprint.cs | 48 +++++- GamecraftModdingAPI/Utility/FullGameFields.cs | 9 ++ 3 files changed, 199 insertions(+), 3 deletions(-) diff --git a/GamecraftModdingAPI/Blocks/BlueprintEngine.cs b/GamecraftModdingAPI/Blocks/BlueprintEngine.cs index 7f0a616..f2f0bef 100644 --- a/GamecraftModdingAPI/Blocks/BlueprintEngine.cs +++ b/GamecraftModdingAPI/Blocks/BlueprintEngine.cs @@ -1,13 +1,22 @@ -using System.Reflection; +using System; +using System.Reflection; using Gamecraft.Blocks.BlockGroups; using Gamecraft.GUI.Blueprints; using GamecraftModdingAPI.Engines; +using GamecraftModdingAPI.Utility; using HarmonyLib; using RobocraftX.Blocks; +using RobocraftX.Common; +using RobocraftX.CR.MachineEditing.BoxSelect; +using RobocraftX.CR.MachineEditing.BoxSelect.ClipboardOperations; using Svelto.DataStructures; using Svelto.ECS; using Svelto.ECS.DataStructures; +using Svelto.ECS.EntityStructs; +using Svelto.ECS.Serialization; using Unity.Collections; +using Unity.Mathematics; +using UnityEngine; namespace GamecraftModdingAPI.Blocks { @@ -18,8 +27,20 @@ namespace GamecraftModdingAPI.Blocks private readonly NativeDynamicArray selectedBlocksInGroup = new NativeDynamicArray(); private readonly NativeHashSet removedConnections = new NativeHashSet(); + 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 NativeEntityRemove nativeRemove; private static MachineGraphConnectionEntityFactory connectionFactory; + private static IEntityFunctions entityFunctions; + private static ClipboardSerializationDataResourceManager clipboardManager; + private static IEntitySerialization entitySerialization; + private static IEntityFactory entityFactory; + private static FasterList globalBlockMap; public void Ready() { @@ -55,6 +76,109 @@ namespace GamecraftModdingAPI.Blocks }); } + public uint CreateBlueprint() + { + uint index = clipboardManager.AllocateSerializationData(); + return index; + } + + public void ReplaceBlueprint(uint playerID, uint blueprintID, Block[] selected, float3 pos, quaternion rot) + { + var blockIDs = new EGID[selected.Length]; + for (var i = 0; i < selected.Length; i++) + { + var block = selected[i]; + blockIDs[i] = block.Id; + } + + var serializationData = clipboardManager.GetSerializationData(blueprintID); + SelectionSerializationUtility.ClearClipboard(playerID, entitiesDB, entityFunctions, serializationData.blueprintData); + if (selected.Length == 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; + if (math.all(pos == default)) + pos = selected[0].Position; + if (math.all(rot.value == default)) + rot = Quaternion.Euler(selected[0].Rotation); + + clipboardManager.SetGhostSerialized(blueprintID, false); + SelectionSerializationUtility.CopySelectionToClipboard(playerID, entitiesDB, + serializationData.blueprintData, entitySerialization, entityFactory, blockIDs, + (uint) blockIDs.Length, pos, rot); + } + + 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, + entitiesDB, 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] = new Block(placedBlocks[i]); + return blocks; + } + + public void InitBlueprint(uint blueprintID) + { + clipboardManager.IncrementRefCount(blueprintID); + } + + public void DisposeBlueprint(uint blueprintID) + { + clipboardManager.DecrementRefCount(blueprintID); + } + public string Name { get; } = "GamecraftModdingAPIBlueprintGameEngine"; public bool isRemovable { get; } @@ -66,6 +190,7 @@ namespace GamecraftModdingAPI.Blocks { nativeRemove = entityFunctions.ToNativeRemove("GCAPI" + nameof(BlueprintEngine)); connectionFactory = machineGraphConnectionEntityFactory; + BlueprintEngine.entityFunctions = entityFunctions; } public static MethodBase TargetMethod() @@ -73,5 +198,23 @@ namespace GamecraftModdingAPI.Blocks return AccessTools.Constructor(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine")); } } + + [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.Constructor(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.SelectBlockEngine")); + } + } } } \ No newline at end of file diff --git a/GamecraftModdingAPI/Blueprint.cs b/GamecraftModdingAPI/Blueprint.cs index de6d65e..26876f6 100644 --- a/GamecraftModdingAPI/Blueprint.cs +++ b/GamecraftModdingAPI/Blueprint.cs @@ -1,13 +1,20 @@ -using Gamecraft.GUI.Blueprints; +using System; +using Unity.Mathematics; namespace GamecraftModdingAPI { /// /// Represents a blueprint in the inventory. When placed it becomes a block group. /// - public class Blueprint + public class Blueprint : IDisposable { public uint Id { get; } + + internal Blueprint(uint id) + { + Id = id; + BlockGroup._engine.InitBlueprint(id); + } /*public static void SelectBlueprint(Blueprint blueprint) { @@ -16,5 +23,42 @@ namespace GamecraftModdingAPI blueprintResourceId = blueprint.Id }); }*/ + + /// + /// Creates a new, empty blueprint. It will be deleted on disposal unless the game holds a reference to it. + /// + /// A blueprint that doesn't have any blocks + public static Blueprint Create() + { + return new Blueprint(BlockGroup._engine.CreateBlueprint()); + } + + /// + /// Set the blocks that the blueprint contains. + /// + /// The array of blocks to use + /// The anchor position of the blueprint + /// The rotation of the blueprint + public void SetStoredBlocks(Block[] blocks, float3 position = default, float3 rotation = default) + { + BlockGroup._engine.ReplaceBlueprint(Player.LocalPlayer.Id, Id, blocks, position, + quaternion.Euler(rotation)); + } + + /// + /// Places the blocks the blueprint contains at the specified position and rotation. + /// + /// The position of the blueprint + /// The rotation of the blueprint + /// An array of the placed blocks + public Block[] PlaceBlocks(float3 position, float3 rotation) + { + return BlockGroup._engine.PlaceBlueprintBlocks(Id, Player.LocalPlayer.Id, position, rotation); + } + + public void Dispose() + { + BlockGroup._engine.DisposeBlueprint(Id); + } } } \ No newline at end of file diff --git a/GamecraftModdingAPI/Utility/FullGameFields.cs b/GamecraftModdingAPI/Utility/FullGameFields.cs index 8fd8895..432103d 100644 --- a/GamecraftModdingAPI/Utility/FullGameFields.cs +++ b/GamecraftModdingAPI/Utility/FullGameFields.cs @@ -12,6 +12,7 @@ using RobocraftX.GUI; using RobocraftX.Multiplayer; using RobocraftX.Rendering; using Svelto.Context; +using Svelto.DataStructures; using Svelto.ECS; using Svelto.ECS.Schedulers.Unity; using UnityEngine; @@ -159,6 +160,14 @@ namespace GamecraftModdingAPI.Utility } } + public static FasterList _deserialisedBlockMap + { + get + { + return (FasterList) fgcr?.Field("_deserialisedBlockMap").GetValue(); + } + } + private static Traverse fgcr; public static void Init(FullGameCompositionRoot instance)