Пример #1
0
        public static bool Copy(BlueprintData data, List <int> entityIds, int referenceId = 0)
        {
            PlanetFactory factory   = GameMain.data.localPlanet.factory;
            var           buildings = new Dictionary <int, EntityData>();
            var           belts     = new Dictionary <int, EntityData>();

            foreach (int id in entityIds)
            {
                EntityData entity      = factory.entityPool[id];
                ItemProto  entityProto = LDB.items.Select(entity.protoId);

                // ignore standalone inserters and miners
                if (entityProto.prefabDesc.isInserter || entityProto.prefabDesc.minerType != EMinerType.None)
                {
                    continue;
                }

                if (entityProto.prefabDesc.isBelt)
                {
                    belts.Add(entity.id, entity);
                }
                else if ((entity.pos.magnitude - GameMain.localPlanet.realRadius - 0.2f) < 0.5f) // ignore multilevel buildings (for now)
                {
                    buildings.Add(entity.id, entity);
                }
            }

            if (buildings.Count == 0 && belts.Count == 0)
            {
                return(false);
            }


            if (buildings.TryGetValue(referenceId, out EntityData globalReference))
            {
                CopyBuilding(data, globalReference, globalReference);
                buildings.Remove(referenceId);
            }
            else
            {
                globalReference = buildings.Count > 0 ? buildings.First().Value : belts.First().Value;
            }

            foreach (EntityData building in buildings.Values)
            {
                CopyBuilding(data, building, globalReference);
            }
            foreach (EntityData belt in belts.Values)
            {
                CopyBelt(data, belt, globalReference);
            }

            return(true);
        }
Пример #2
0
        public static void PrepareNew()
        {
            if (!hasData)
            {
                return;
            }

            hasData      = false;
            previousData = data;
            data         = new BlueprintData();
            pastedEntities.Clear();
            GC.Collect();

            UpdateUIText();
        }
Пример #3
0
        public static void Restore(BlueprintData newData = null)
        {
            if (hasData)
            {
                BlueprintData temp = data;
                data         = newData ?? previousData;
                previousData = temp;
            }
            else
            {
                hasData = true;
                data    = newData ?? previousData;
            }

            pastedEntities.Clear();
            GC.Collect();
            UpdateUIText();
            EnterBuildModeAfterBp();
        }
Пример #4
0
        public static void PasteInsertersOnly(BlueprintData data, Vector3 targetPos, float yaw, int pasteIndex = 0, bool connectToPasted = false)
        {
            PlayerAction_Build actionBuild = GameMain.data.mainPlayer.controller.actionBuild;
            var backupMechaBuildArea       = actionBuild.player.mecha.buildArea;

            actionBuild.player.mecha.buildArea = 10000f;

            Vector2 targetSpr = targetPos.ToSpherical();

            ConcurrentQueue <InserterCopy> inserterQueue = new ConcurrentQueue <InserterCopy>(data.copiedInserters);

            Util.Parallelize((threadIndex) =>
            {
                while (inserterQueue.TryDequeue(out InserterCopy inserter))
                {
                    var pastedEntity = ConcurrentPasteInserter(threadIndex, inserter, yaw, pasteIndex, connectToPasted);
                    BuildLogic.CheckBuildConditionsWorker(_abs[threadIndex], pastedEntity.buildPreview);
                }
            });

            actionBuild.player.mecha.buildArea = backupMechaBuildArea;
        }
Пример #5
0
        public static BlueprintData Import(string input, out HashSet <int> incompatibleIds)
        {
            string unzipped;
            var    name = "";

            incompatibleIds = new HashSet <int>();

            try
            {
                List <string> segments   = input.Split(':').ToList();
                var           base64Data = segments.Last();

                if (segments.Count > 1)
                {
                    segments.RemoveAt(segments.Count - 1);
                    name = String.Join(":", segments.ToArray());
                }
                unzipped = Unzip(Convert.FromBase64String(base64Data));
            }
            catch (Exception e)
            {
                Console.WriteLine("Error while unzipping string: " + e.ToString());
                return(null);
            }


            BlueprintData deserialized = null;

            try
            {
                fsSerializer serializer = new fsSerializer();

                fsData data = fsJsonParser.Parse(unzipped);

                if (data.AsDictionary.ContainsKey("version"))
                {
                    int intVer = (int)data.AsDictionary["version"].AsInt64;
                    data.AsDictionary["$version"] = new fsData(intVer.ToString());
                }

                string version = data.AsDictionary["$version"].AsString;

                if (version.Equals("1"))
                {
                    serializer.AddConverter(new Vector3Converter_V1());
                }
                else
                {
                    serializer.AddConverter(new Vector3Converter());
                    serializer.AddConverter(new Vector2Converter());
                    serializer.AddConverter(new QuaternionConverter());
                }

                serializer.TryDeserialize <BlueprintData>(data, ref deserialized).AssertSuccessWithoutWarnings();
            }
            catch (Exception e)
            {
                Console.WriteLine("Error while trying to deserialise: " + e.ToString());
                return(null);
            }

            foreach (var building in deserialized.copiedBuildings)
            {
                building.itemProto = LDB.items.Select((int)building.protoId);
                if (building.itemProto == null)
                {
                    incompatibleIds.Add(building.protoId);
                }
            }
            foreach (var belt in deserialized.copiedBelts)
            {
                belt.itemProto = LDB.items.Select((int)belt.protoId);
                if (belt.itemProto == null)
                {
                    incompatibleIds.Add(belt.protoId);
                }
            }
            foreach (var inserter in deserialized.copiedInserters)
            {
                inserter.itemProto = LDB.items.Select((int)inserter.protoId);
                if (inserter.itemProto == null)
                {
                    incompatibleIds.Add(inserter.protoId);
                }
            }


            if (incompatibleIds.Count > 0)
            {
                return(null);
            }

            deserialized.name = name;
            return(deserialized);
        }
Пример #6
0
        public static BeltCopy CopyBelt(BlueprintData data, EntityData sourceEntity, EntityData referenceEntity)
        {
            PlanetFactory factory = GameMain.data.localPlanet.factory;

            ItemProto sourceEntityProto = LDB.items.Select(sourceEntity.protoId);

            if (!sourceEntityProto.prefabDesc.isBelt)
            {
                return(null);
            }

            BeltComponent belt         = factory.cargoTraffic.beltPool[sourceEntity.beltId];
            Vector2       sourceSprPos = sourceEntity.pos.ToSpherical();

            BeltCopy copiedBelt = new BeltCopy()
            {
                originalId   = sourceEntity.id,
                protoId      = sourceEntityProto.ID,
                itemProto    = sourceEntityProto,
                altitude     = Mathf.RoundToInt(2 * (sourceEntity.pos.magnitude - GameMain.localPlanet.realRadius - 0.2f) / 1.3333333f),
                backInputId  = factory.cargoTraffic.beltPool[belt.backInputId].entityId,
                leftInputId  = factory.cargoTraffic.beltPool[belt.leftInputId].entityId,
                rightInputId = factory.cargoTraffic.beltPool[belt.rightInputId].entityId,
                outputId     = factory.cargoTraffic.beltPool[belt.outputId].entityId,
            };

            factory.ReadObjectConn(sourceEntity.id, 0, out bool isOutput, out int otherId, out int otherSlot);
            if (otherId > 0 && factory.entityPool[otherId].beltId == 0)
            {
                copiedBelt.connectedBuildingId       = otherId;
                copiedBelt.connectedBuildingIsOutput = isOutput;
                copiedBelt.connectedBuildingSlot     = otherSlot;
            }

            factory.ReadObjectConn(sourceEntity.id, 1, out isOutput, out otherId, out otherSlot);
            if (otherId > 0 && factory.entityPool[otherId].beltId == 0)
            {
                copiedBelt.connectedBuildingId       = otherId;
                copiedBelt.connectedBuildingIsOutput = isOutput;
                copiedBelt.connectedBuildingSlot     = otherSlot;
            }

            if (sourceEntity.id == referenceEntity.id)
            {
                data.referencePos = sourceSprPos;
            }
            else
            {
                copiedBelt.originalSegmentCount = sourceSprPos.GetSegmentsCount();
                copiedBelt.cursorRelativePos    = (sourceSprPos - data.referencePos).Clamp();
            }

            data.copiedBelts.Add(copiedBelt);

            factory.ReadObjectConn(sourceEntity.id, 4, out _, out otherId, out _);

            if (otherId != 0)
            {
                // only copy belt to belt inserter if both belts are part fo the blueprint
                factory.ReadObjectConn(otherId, 0, out _, out int endId, out _);
                factory.ReadObjectConn(otherId, 1, out _, out int startId, out _);

                int idToFind = sourceEntity.id == endId ? startId : endId;

                if (data.copiedBelts.FindIndex(x => x.originalId == idToFind) != -1)
                {
                    EntityData inserterEntity = factory.entityPool[otherId];
                    CopyInserter(data, inserterEntity, sourceEntity);
                }
            }

            return(copiedBelt);
        }
Пример #7
0
        public static InserterCopy CopyInserter(BlueprintData data, EntityData sourceEntity, EntityData referenceEntity)
        {
            PlanetFactory      factory     = GameMain.data.localPlanet.factory;
            PlayerAction_Build actionBuild = GameMain.data.mainPlayer.controller.actionBuild;

            if (sourceEntity.inserterId == 0)
            {
                return(null);
            }

            InserterComponent inserter = factory.factorySystem.inserterPool[sourceEntity.inserterId];

            if (data.copiedInserters.FindIndex(x => x.originalId == inserter.entityId) != -1)
            {
                return(null);
            }

            int pickTarget   = inserter.pickTarget;
            int insertTarget = inserter.insertTarget;

            ItemProto itemProto = LDB.items.Select(sourceEntity.protoId);

            bool incoming = insertTarget == referenceEntity.id;
            int  otherId  = incoming ? pickTarget : insertTarget;


            Vector2 referenceSprPos = referenceEntity.pos.ToSpherical();
            Vector2 sourceSprPos    = sourceEntity.pos.ToSpherical();
            Vector2 sourceSprPos2   = inserter.pos2.ToSpherical();

            // The belt or other building this inserter is attached to
            Vector2   otherSprPos;
            ItemProto otherProto;

            if (otherId > 0)
            {
                otherProto  = LDB.items.Select(factory.entityPool[otherId].protoId);
                otherSprPos = factory.entityPool[otherId].pos.ToSpherical();
            }
            else if (otherId < 0)
            {
                otherProto  = LDB.items.Select(factory.prebuildPool[-otherId].protoId);
                otherSprPos = factory.prebuildPool[-otherId].pos.ToSpherical();
            }
            else
            {
                otherSprPos = sourceSprPos2;
                otherProto  = null;
            }

            bool otherIsBelt = otherProto == null || otherProto.prefabDesc.isBelt;


            // Cache info for this inserter
            InserterCopy copiedInserter = new InserterCopy
            {
                itemProto  = itemProto,
                protoId    = itemProto.ID,
                originalId = inserter.entityId,

                pickTarget   = pickTarget,
                insertTarget = insertTarget,

                referenceBuildingId = referenceEntity.id,

                incoming = incoming,

                // rotations + deltas relative to the source building's rotation
                rot       = Quaternion.Inverse(referenceEntity.rot) * sourceEntity.rot,
                rot2      = Quaternion.Inverse(referenceEntity.rot) * inserter.rot2,
                posDelta  = sourceSprPos - referenceSprPos,  // Delta from copied building to inserter pos
                pos2Delta = sourceSprPos2 - referenceSprPos, // Delta from copied building to inserter pos2

                posDeltaCount  = sourceSprPos.GetSegmentsCount(),
                pos2DeltaCount = sourceSprPos2.GetSegmentsCount(),

                otherPosDelta      = otherSprPos - referenceSprPos,
                otherPosDeltaCount = otherSprPos.GetSegmentsCount(),

                // not important?
                pickOffset   = inserter.pickOffset,
                insertOffset = inserter.insertOffset,

                filterId = inserter.filter,

                startSlot = -1,
                endSlot   = -1,

                otherIsBelt = otherIsBelt
            };

            InserterPoses.CalculatePose(actionBuild, pickTarget, insertTarget);

            if (actionBuild.posePairs.Count > 0)
            {
                float minDistance = 1000f;
                for (int j = 0; j < actionBuild.posePairs.Count; ++j)
                {
                    var   posePair      = actionBuild.posePairs[j];
                    float startDistance = Vector3.Distance(posePair.startPose.position, sourceEntity.pos);
                    float endDistance   = Vector3.Distance(posePair.endPose.position, inserter.pos2);
                    float poseDistance  = startDistance + endDistance;

                    if (poseDistance < minDistance)
                    {
                        minDistance = poseDistance;
                        copiedInserter.startSlot = posePair.startSlot;
                        copiedInserter.endSlot   = posePair.endSlot;

                        copiedInserter.pickOffset   = (short)posePair.startOffset;
                        copiedInserter.insertOffset = (short)posePair.endOffset;
                    }
                }
            }


            /*        factory.ReadObjectConn(sourceEntity.id, 1, out bool isOutput, out int connectedId, out int connectedSlot);
             *
             *          if (connectedId != 0)
             *          {
             *              copiedInserter.startSlot = connectedSlot;
             *          }
             *
             *
             *          factory.ReadObjectConn(sourceEntity.id, 0, out _, out connectedId, out connectedSlot);
             *          if (connectedId != 0)
             *          {
             *              copiedInserter.endSlot = connectedSlot;
             *          }
             */

            data.copiedInserters.Add(copiedInserter);

            return(copiedInserter);
        }
Пример #8
0
        public static BuildingCopy CopyBuilding(BlueprintData data, EntityData sourceEntity, EntityData referenceEntity)
        {
            PlanetFactory factory = GameMain.data.localPlanet.factory;

            ItemProto sourceEntityProto = LDB.items.Select(sourceEntity.protoId);

            Vector3    sourcePos = sourceEntity.pos;
            Quaternion sourceRot = sourceEntity.rot;

            Quaternion zeroRot = Maths.SphericalRotation(sourcePos, 0f);
            float      yaw     = Vector3.SignedAngle(zeroRot.Forward(), sourceRot.Forward(), zeroRot.Up());


            BuildingCopy copiedBuilding = new BuildingCopy()
            {
                originalId = sourceEntity.id,
                protoId    = sourceEntityProto.ID,
                itemProto  = sourceEntityProto,
                modelIndex = sourceEntity.modelIndex,
            };

            var prefabDesc = BlueprintManager.GetPrefabDesc(copiedBuilding);

            if (sourceEntity.assemblerId > 0)
            {
                copiedBuilding.recipeId = factory.factorySystem.assemblerPool[sourceEntity.assemblerId].recipeId;
            }
            else if (sourceEntity.labId > 0)
            {
                LabComponent labComponent = factory.factorySystem.labPool[sourceEntity.labId];
                copiedBuilding.recipeId = ((!labComponent.researchMode) ? labComponent.recipeId : -1);
            }
            else if (sourceEntity.powerGenId > 0)
            {
                PowerGeneratorComponent powerGeneratorComponent = factory.powerSystem.genPool[sourceEntity.powerGenId];
                if (powerGeneratorComponent.gamma)
                {
                    copiedBuilding.recipeId = ((powerGeneratorComponent.productId <= 0) ? 0 : 1);
                }
            }
            else if (sourceEntity.powerExcId > 0)
            {
                copiedBuilding.recipeId = Mathf.RoundToInt(factory.powerSystem.excPool[sourceEntity.powerExcId].targetState);
            }
            else if (sourceEntity.ejectorId > 0)
            {
                copiedBuilding.recipeId = factory.factorySystem.ejectorPool[sourceEntity.ejectorId].orbitId;
            }
            else if (sourceEntity.stationId > 0)
            {
                StationComponent stationComponent = factory.transport.stationPool[sourceEntity.stationId];

                copiedBuilding.stationConfig = new StationConfig()
                {
                    workEnergyPerTick     = factory.powerSystem.consumerPool[stationComponent.pcId].workEnergyPerTick,
                    tripRangeDrones       = stationComponent.tripRangeDrones,
                    tripRangeShips        = stationComponent.tripRangeShips,
                    warpEnableDist        = stationComponent.warpEnableDist,
                    warperNecessary       = stationComponent.warperNecessary,
                    includeOrbitCollector = stationComponent.includeOrbitCollector,
                    deliveryDrones        = stationComponent.deliveryDrones,
                    deliveryShips         = stationComponent.deliveryShips
                };

                for (int i = 0; i < stationComponent.slots.Length; i++)
                {
                    if (stationComponent.slots[i].storageIdx != 0)
                    {
                        copiedBuilding.slotFilters.Add(new SlotFilter()
                        {
                            slotIndex  = i,
                            storageIdx = stationComponent.slots[i].storageIdx
                        });
                    }
                }

                for (int i = 0; i < stationComponent.storage.Length; i++)
                {
                    if (stationComponent.storage[i].itemId != 0)
                    {
                        copiedBuilding.stationSettings.Add(new StationSetting()
                        {
                            index       = i,
                            itemId      = stationComponent.storage[i].itemId,
                            max         = stationComponent.storage[i].max,
                            localLogic  = stationComponent.storage[i].localLogic,
                            remoteLogic = stationComponent.storage[i].remoteLogic
                        });
                    }
                }
            }
            else if (sourceEntity.splitterId > 0)
            {
                var splitterComponent = factory.cargoTraffic.splitterPool[sourceEntity.splitterId];
                copiedBuilding.splitterSettings = new SplitterSettings()
                {
                    inPriority  = splitterComponent.inPriority,
                    outPriority = splitterComponent.outPriority,
                    outFilter   = splitterComponent.outFilter
                };

                var slots = new List <int>(4)
                {
                    splitterComponent.beltA, splitterComponent.beltB, splitterComponent.beltC, splitterComponent.beltD
                };
                if (copiedBuilding.splitterSettings.inPriority)
                {
                    copiedBuilding.splitterSettings.inPrioritySlot = slots.IndexOf(splitterComponent.input0);
                }
                if (copiedBuilding.splitterSettings.outPriority)
                {
                    copiedBuilding.splitterSettings.outPrioritySlot = slots.IndexOf(splitterComponent.output0);
                }
                // TODO: find a way to restore splitter settings
                // SplitterComponent splitterComponent = factory.cargoTraffic.splitterPool[sourceEntity.splitterId];
            }
            else if (sourceEntity.storageId > 0)
            {
                copiedBuilding.recipeId = factory.factoryStorage.storagePool[sourceEntity.storageId].bans;
            }

            Vector2 sourceSprPos = sourcePos.ToSpherical();

            if (sourceEntity.id == referenceEntity.id)
            {
                data.referencePos = sourceSprPos;
                copiedBuilding.cursorRelativeYaw = yaw;
            }
            else
            {
                copiedBuilding.originalSegmentCount = sourceSprPos.GetSegmentsCount();
                copiedBuilding.cursorRelativePos    = (sourceSprPos - data.referencePos).Clamp();
                copiedBuilding.cursorRelativeYaw    = yaw;
            }

            data.copiedBuildings.Add(copiedBuilding);

            for (int i = 0; i < sourceEntityProto.prefabDesc.insertPoses.Length; i++)
            {
                factory.ReadObjectConn(sourceEntity.id, i, out bool _, out int otherObjId, out int _);

                if (otherObjId > 0)
                {
                    EntityData inserterEntity = factory.entityPool[otherObjId];
                    CopyInserter(data, inserterEntity, sourceEntity);
                }
            }

            if (sourceEntityProto.prefabDesc.multiLevel)
            {
                copiedBuilding.altitude = Mathf.RoundToInt((sourceEntity.pos.magnitude - GameMain.localPlanet.realRadius - 0.2f) / prefabDesc.lapJoint.magnitude);

                if (copiedBuilding.altitude > 0)
                {
                    copiedBuilding.connectedBuildingId = referenceEntity.id;
                }

                factory.ReadObjectConn(sourceEntity.id, 15, out bool _, out int otherObjId, out int _);

                if (otherObjId > 0)
                {
                    EntityData stackedEntity = factory.entityPool[otherObjId];
                    CopyBuilding(data, stackedEntity, sourceEntity);
                }
            }


            return(copiedBuilding);
        }
Пример #9
0
        internal void Awake()
        {
            harmony             = new Harmony("com.brokenmass.plugin.DSP.MultiBuild" + CHANNEL);
            itemSpecificSpacing = Config.Bind <bool>("General", "itemSpecificSpacing", true, "If this option is set to true, the mod will remember the last spacing used for a specific building. Otherwise the spacing will be the same for all entities.");
            spacingStore[0]     = 0;

            //StartCoroutine(CheckForUpdates());

            try
            {
                foreach (var pluginInfo in BepInEx.Bootstrap.Chainloader.PluginInfos)
                {
                    if (BLACKLISTED_MODS.Contains(pluginInfo.Value.Metadata.GUID))
                    {
                        incompatiblePlugins.Add(" - " + pluginInfo.Value.Metadata.Name);
                    }
                }

                if (incompatiblePlugins.Count > 0)
                {
                    isValidInstallation = false;
                    harmony.PatchAll(typeof(IncompatibilityNotice));
                }


                if (isValidInstallation)
                {
                    harmony.PatchAll(typeof(MultiBuild));
                    harmony.PatchAll(typeof(BlueprintManager));
                    harmony.PatchAll(typeof(BuildLogic));
                    harmony.PatchAll(typeof(BlueprintCreator));
                    harmony.PatchAll(typeof(InserterPoses));

                    UIBlueprintGroup.onCreate  = () => BlueprintCreator.StartBpMode();
                    UIBlueprintGroup.onRestore = () =>
                    {
                        if (BlueprintCreator.bpMode)
                        {
                            BlueprintCreator.EndBpMode(true);
                        }
                        BlueprintManager.Restore();
                    };
                    UIBlueprintGroup.onImport = () =>
                    {
                        if (BlueprintCreator.bpMode)
                        {
                            BlueprintCreator.EndBpMode(true);
                        }
                        HashSet <int> incompatibleIds;
                        var           data = BlueprintData.Import(GUIUtility.systemCopyBuffer, out incompatibleIds);
                        if (data != null)
                        {
                            BlueprintManager.Restore(data);
                            UIRealtimeTip.Popup("Blueprint successfully imported from your clipboard", false);
                        }
                        else
                        {
                            string message = "Error while importing data from your clipboard";
                            if (incompatibleIds.Count > 0)
                            {
                                message += $" - Found {incompatibleIds.Count} incompatible entities.\nIds: [{incompatibleIds.Join(null, ", ")}]";
                            }
                            UIRealtimeTip.Popup(message, true);
                        }
                    };
                    UIBlueprintGroup.onExport = () =>
                    {
                        if (BlueprintCreator.bpMode)
                        {
                            BlueprintCreator.EndBpMode();
                        }
                        if (BlueprintManager.hasData)
                        {
                            GUIUtility.systemCopyBuffer = BlueprintManager.data.Export();
                            UIRealtimeTip.Popup("Blueprint successfully exported to your clipboard", false);
                        }
                        else
                        {
                            UIRealtimeTip.Popup("No blueprint data to export", true);
                        }
                    };
                }
            }
            catch (Exception e)
            {
                isValidInstallation = false;
                Console.WriteLine(e.ToString());
            }
        }
Пример #10
0
        public static void Paste(BlueprintData data, Vector3 targetPos, float yaw, bool pasteInserters = true, int pasteIndex = 0)
        {
            PlayerAction_Build actionBuild = GameMain.data.mainPlayer.controller.actionBuild;
            var backupMechaBuildArea       = actionBuild.player.mecha.buildArea;

            actionBuild.player.mecha.buildArea = 10000f;

            Vector2 targetSpr = targetPos.ToSpherical();

            ConcurrentQueue <BuildingCopy> buildingsQueue = new ConcurrentQueue <BuildingCopy>(data.copiedBuildings);

            Util.Parallelize((int threadIndex) =>
            {
                while (buildingsQueue.TryDequeue(out BuildingCopy building))
                {
                    var pastedEntity = ConcurrentPasteBuilding(threadIndex, building, targetSpr, yaw, pasteIndex);

                    lock (actionBuild.nearcdLogic)
                    {
                        actionBuild.nearcdLogic.ActiveEntityBuildCollidersInArea(pastedEntity.pose.position, 5f);
                    }
                }
            });

            buildingsQueue = new ConcurrentQueue <BuildingCopy>(data.copiedBuildings);
            Util.Parallelize((int threadIndex) =>
            {
                while (buildingsQueue.TryDequeue(out BuildingCopy building))
                {
                    int pasteId      = PASTE_INDEX_MULTIPLIER * pasteIndex + building.originalId;
                    var pastedEntity = BlueprintManager.pastedEntities[pasteId];

                    ConcurrentConnectBuildings(threadIndex, pastedEntity);
                    BuildLogic.CheckBuildConditionsWorker(_abs[threadIndex], pastedEntity.buildPreview);
                }
            });

            ConcurrentQueue <BeltCopy> beltsQueue = new ConcurrentQueue <BeltCopy>(data.copiedBelts);

            Util.Parallelize((int threadIndex) =>
            {
                while (beltsQueue.TryDequeue(out BeltCopy belt))
                {
                    var pastedEntity = ConcurrentPasteBelt(threadIndex, belt, targetSpr, yaw, pasteIndex);
                }
            });

            // after creating the belt previews this restore the correct connection to other belts and buildings
            beltsQueue = new ConcurrentQueue <BeltCopy>(data.copiedBelts);
            Util.Parallelize((int threadIndex) =>
            {
                while (beltsQueue.TryDequeue(out BeltCopy belt))
                {
                    int pasteId      = PASTE_INDEX_MULTIPLIER * pasteIndex + belt.originalId;
                    var pastedEntity = BlueprintManager.pastedEntities[pasteId];

                    ConcurrentConnectBelt(threadIndex, pastedEntity);
                    BuildLogic.CheckBuildConditionsWorker(_abs[threadIndex], pastedEntity.buildPreview);
                }
            });

            if (pasteInserters)
            {
                ConcurrentQueue <InserterCopy> inserterQueue = new ConcurrentQueue <InserterCopy>(data.copiedInserters);
                Util.Parallelize((threadIndex) =>
                {
                    while (inserterQueue.TryDequeue(out InserterCopy inserter))
                    {
                        var pastedEntity = ConcurrentPasteInserter(threadIndex, inserter, yaw, pasteIndex);
                        BuildLogic.CheckBuildConditionsWorker(_abs[threadIndex], pastedEntity.buildPreview);
                    }
                });
            }

            actionBuild.player.mecha.buildArea = backupMechaBuildArea;
        }