void Weld(ICollection <IMySlimBlock> Blocks, int ticks = 1)
        {
            UnbuiltBlocks.Clear();
            if (Blocks.Count == 0)
            {
                return;
            }
            float SpeedRatio   = (VanillaToolConstants.WelderSpeed / Blocks.Count) * ticks * TermModule.SpeedMultiplier;
            float BoneFixSpeed = VanillaToolConstants.WelderBoneRepairSpeed * ticks;
            Dictionary <IMySlimBlock, int> UniqueBlocks = new Dictionary <IMySlimBlock, int>();

            if (!TermModule.DistanceMode)
            {
                UniqueBlocks = Blocks.CollapseDuplicates();
            }
            else
            {
                IMySlimBlock Block = Blocks.OrderByDescending(x => Vector3D.DistanceSquared(x.GetPosition(), Tool.GetPosition())).ToList().First();
                UniqueBlocks.Add(Block, 1);
                SpeedRatio = VanillaToolConstants.WelderSpeed * ticks * TermModule.SpeedMultiplier;
            }
            HashSet <IMySlimBlock> unbuilt     = new HashSet <IMySlimBlock>();
            List <IMySlimBlock>    CanBuild    = new List <IMySlimBlock>();
            List <IMySlimBlock>    CannotBuild = new List <IMySlimBlock>();

            if (MyAPIGateway.Session.CreativeMode)
            {
                foreach (IMySlimBlock Block in UniqueBlocks.Keys)
                {
                    float blockRatio = SpeedRatio * UniqueBlocks.GetData(Block);
                    Block.MoveItemsToConstructionStockpile(ToolCargo);
                    Block.IncreaseMountLevel(blockRatio, Welder.OwnerId, ToolCargo, BoneFixSpeed, Welder.HelpOthers);
                }
                return;
            }

            foreach (IMySlimBlock Block in UniqueBlocks.Keys)
            {
                if (Block.CanContinueBuild(ToolCargo))
                {
                    CanBuild.Add(Block);
                }
                else
                {
                    CannotBuild.Add(Block);
                }
            }

            SessionCore.DebugWrite(Tool.CustomName, $"Can build {CanBuild.Count} blocks", WriteOnlyIfDebug: true);
            SessionCore.DebugWrite(Tool.CustomName, $"Can't build {CannotBuild.Count} blocks", WriteOnlyIfDebug: true);

            bool Pull = false;

            var Components = Reduce(CannotBuild.ReadMissingComponents());
            Dictionary <string, int> Pulled = new Dictionary <string, int>();

            lock (GridInventoryModule.InventoryLock)
            {
                if (Welder.UseConveyorSystem)
                {
                    GridInventoryModule.PullIn(Welder, Components, "Component");
                }
                Pull = Pulled.Count > 0;
            }
            SessionCore.DebugWrite(Tool.CustomName, $"Pulled {Pulled.Values.Sum()} components", WriteOnlyIfDebug: true);

            System.Diagnostics.Stopwatch WelderWatch = new System.Diagnostics.Stopwatch();
            WelderWatch.Start();
            foreach (IMySlimBlock Block in UniqueBlocks.Keys)
            {
                if (!Pull && !CanBuild.Contains(Block))
                {
                    unbuilt.Add(Block);
                    continue;
                }
                float blockRatio = SpeedRatio * UniqueBlocks.GetData(Block);
                if (!Weld(Block, blockRatio, BoneFixSpeed))
                {
                    unbuilt.Add(Block);
                }
            }
            WelderWatch.Stop();
            WelderWatch.Report(Tool.CustomName, "Welding", UseAsync: true);

            System.Diagnostics.Stopwatch ReadMissingWatch = new System.Diagnostics.Stopwatch();
            ReadMissingWatch.Start();
            if (unbuilt.Count > 0)
            {
                Dictionary <string, int> Missing = new Dictionary <string, int>();
                unbuilt.ReadMissingComponents(Missing);
                Missing = Reduce(Missing);
                if (Missing.Count > 0)
                {
                    UnbuiltBlocks.UnionWith(unbuilt);
                }
            }
            ComplainUnbuilt();
            ReadMissingWatch.Stop();
            ReadMissingWatch.Report(Tool.CustomName, "ReadMissing", true);
            SessionCore.DebugAsync(Tool.CustomName, $"Unbuilt: {unbuilt.Count} blocks", WriteOnlyIfDebug: true);
        }
        void Place(ICollection <IMySlimBlock> _Blocks)
        {
            try
            {
                if (_Blocks.Count == 0)
                {
                    return;
                }
                HashSet <IMySlimBlock> Blocks  = new HashSet <IMySlimBlock>(_Blocks);
                HashSet <IMySlimBlock> unbuilt = new HashSet <IMySlimBlock>();

                var Projector = ((Blocks.First().CubeGrid as MyCubeGrid).Projector as IMyProjector);

                if (MyAPIGateway.Session.CreativeMode)
                {
                    foreach (IMySlimBlock Block in Blocks)
                    {
                        Projector.Build(Block, Tool.OwnerId, Tool.EntityId, false);
                    }
                    return;
                }

                Dictionary <string, int> ToPull = new Dictionary <string, int>();
                if (Welder.UseConveyorSystem)
                {
                    foreach (IMySlimBlock Block in Blocks)
                    {
                        var FirstItem = ((MyCubeBlockDefinition)Block.BlockDefinition).Components[0].Definition.Id;
                        if (!ToPull.ContainsKey(FirstItem.SubtypeName))
                        {
                            ToPull.Add(FirstItem.SubtypeName, 1);
                        }
                        else
                        {
                            ToPull[FirstItem.SubtypeName] += 1;
                        }
                    }

                    lock (GridInventoryModule.InventoryLock)
                    {
                        GridInventoryModule.PullIn(Welder, Reduce(ToPull), "Component");
                    }
                }

                foreach (IMySlimBlock Block in Blocks)
                {
                    var FirstItem = ((MyCubeBlockDefinition)Block.BlockDefinition).Components[0].Definition.Id;
                    if (ToolCargo.GetItemAmount(FirstItem) >= 1)
                    {
                        Projector.Build(Block, Tool.OwnerId, Tool.EntityId, false);
                        ToolCargo.RemoveItemsOfType(1, FirstItem);
                    }
                    else
                    {
                        unbuilt.Add(Block);
                    }
                }
                SessionCore.DebugAsync(Tool.CustomName, $"Place failed for {unbuilt.Count} blocks: can't pull first component", WriteOnlyIfDebug: true);
                UnbuiltBlocks.UnionWith(unbuilt);
                ComplainUnbuilt();
            }
            catch (Exception Scrap)
            {
                SessionCore.LogError($"{Tool.CustomName}.Place()", Scrap);
            }
        }