private static int MoveItem(IScriptRootData root, IItemsSource S, string N, IStructureData targetStructure, int count, int?maxLimit)
        {
            var target = targetStructure?.GetCurrent()?.GetDevice <Eleon.Modding.IContainer>(N);

            if (target == null)
            {
                Log($"TargetNoFound: {S.Id} #{S.Count} => {N}", LogLevel.Debug);
                return(count);
            }

            if (!targetStructure.ContainerSource.TryGetValue(N, out var targetData))
            {
                Log($"TargetDataNoFound: {S.Id} #{S.Count} => {N}", LogLevel.Debug);
                return(count);
            }

            var tryMoveCount = maxLimit.HasValue
                ? Math.Max(0, Math.Min(count, maxLimit.Value - target.GetTotalItems(S.Id)))
                : count;

            using var locked = WeakCreateDeviceLock(root, root.GetCurrentPlayfield(), targetStructure.GetCurrent(), targetData.Position);
            if (!locked.Success)
            {
                Log($"DeviceIsLocked (Target): {S.Id} #{S.Count} => {targetData.CustomName}", LogLevel.Debug);
                return(count);
            }

            return(maxLimit.HasValue
                ? target.AddItems(S.Id, tryMoveCount) + (count - tryMoveCount)
                : target.AddItems(S.Id, tryMoveCount));
        }
        static void DeconstructPart(TextWriter output, IScriptRootData root, IStructure S, DeconstructData deconstructData, IContainer target, VectorInt3 targetPos, string N)
        {
            IDeviceLock locked = null;

            var startTime       = DateTime.Now;
            var maxMilliSeconds = EmpyrionScripting.Configuration.Current.InGameScriptsIntervallMS;

            try
            {
                for (; deconstructData.Y >= deconstructData.MinPos.y; deconstructData.Y--)
                {
                    for (; deconstructData.X <= deconstructData.MaxPos.x; deconstructData.X++)
                    {
                        for (; deconstructData.Z <= deconstructData.MaxPos.z; deconstructData.Z++)
                        {
                            deconstructData.CheckedBlocks++;

                            var block = S.GetBlock(deconstructData.X, 128 + deconstructData.Y, deconstructData.Z);
                            if (block != null)
                            {
                                block.Get(out var blockType, out _, out _, out _);
                                if (blockType > 0 && blockType != PlayerCoreType)
                                {
                                    locked = locked ?? CreateDeviceLock(EmpyrionScripting.ModApi?.Playfield, root.E.S.GetCurrent(), targetPos);
                                    if (!locked.Success)
                                    {
                                        deconstructData.CheckedBlocks--;
                                        output.WriteLine($"Container '{N}' is locked");
                                        return;
                                    }

                                    if (target.AddItems(blockType, 1) > 0)
                                    {
                                        deconstructData.CheckedBlocks--;
                                        output.WriteLine($"Container '{N}' is full");
                                        return;
                                    }
                                    block.Set(0);
                                    deconstructData.RemovedBlocks++;

                                    if (deconstructData.RemovedBlocks > 100 && deconstructData.RemovedBlocks % 100 == 0 && (DateTime.Now - startTime).TotalMilliseconds > maxMilliSeconds)
                                    {
                                        return;
                                    }
                                }
                            }
                        }
                        deconstructData.Z = deconstructData.MinPos.z;
                    }
                    deconstructData.X = deconstructData.MinPos.x;
                }
            }
            finally
            {
                locked?.Dispose();
            }
        }
 public void Add(IScriptRootData data)
 {
     if (WaitForExec.TryAdd(data.ScriptId, data))
     {
         ExecQueue.Enqueue(data);
     }
     else
     {
         lock (ExecQueue) WaitForExec.AddOrUpdate(data.ScriptId, data, (i, d) => data);
     }
 }
        public static IList <string> Scroll(IScriptRootData root, string content, int lines, int delay, int step)
        {
            var textlines   = content.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            var outputLines = new List <string>(textlines);
            var overlapp    = textlines.Length - lines;

            if (overlapp > 0)
            {
                var skip = root.CycleCounter / delay * step % textlines.Length;
                outputLines = textlines.Skip(skip).Take(lines).ToList();
                outputLines.AddRange(textlines.Take(skip + lines - textlines.Length));
            }

            return(outputLines);
        }
        public bool ExecNext()
        {
            var             found = false;
            IScriptRootData data  = null;

            lock (ExecQueue) found = ExecQueue.TryDequeue(out data);
            if (!found)
            {
                return(false);
            }

            if (!ThreadPool.QueueUserWorkItem(ExecScript, data))
            {
                Log($"EmpyrionScripting Mod: ExecNext NorThreadPoolFree {data.ScriptId}", LogLevel.Debug);
                return(false);
            }
            return(true);
        }
        public void Add(IScriptRootData data)
        {
            if (ScriptNeedsMainThread.TryGetValue(data.ScriptId, out var needMainThread))
            {
                data.ScriptNeedsMainThread = needMainThread;
            }
            else
            {
                ScriptNeedsMainThread.TryAdd(data.ScriptId, false);
            }

            if (WaitForExec.TryAdd(data.ScriptId, data))
            {
                ExecQueue.Enqueue(data.ScriptId);
            }
            else
            {
                WaitForExec.AddOrUpdate(data.ScriptId, data, (scriptId, oldData) => data);
            }
        }
        private static void AddTargetsAndDisplayType(IScriptRootData data, string targets)
        {
            if (targets.StartsWith("["))
            {
                var typeEnd = targets.IndexOf(']');
                if (typeEnd > 0)
                {
                    var s           = targets.Substring(1, typeEnd - 1);
                    var appendAtEnd = s.EndsWith("+");
                    int.TryParse(appendAtEnd ? s.Substring(0, s.Length - 1) : s.Substring(1), out int Lines);
                    data.DisplayType = new DisplayOutputConfiguration()
                    {
                        AppendAtEnd = appendAtEnd, Lines = Lines
                    };

                    targets = targets.Substring(typeEnd + 1);
                }
            }

            data.LcdTargets.AddRange(data.E.S.AllCustomDeviceNames.GetUniqueNames(targets).Where(N => !N.StartsWith(ScriptKeyword)));
        }
        public WeakDeviceLock(IScriptRootData root, IPlayfield playfield, IStructure structure, VectorInt3 position)
        {
            try
            {
                if (!root.ScriptWithinMainThread)
                {
                    root.ScriptNeedsMainThread = true;
                    return;
                }

                if (!root.DeviceLockAllowed || root.TimeLimitReached)
                {
                    return;
                }

                Success = !playfield.IsStructureDeviceLocked(structure.Id, position);
            }
            catch (Exception error)
            {
                throw new Exception($"WeakDeviceLock:failed on Playfield:{playfield?.Name} at Id:{structure.Id} on {position} with: {error}");
            }
        }
        public DeviceLock(IScriptRootData root, IPlayfield playfield, IStructure structure, VectorInt3 position)
        {
            var witherror = false;

            try
            {
                if (!root.ScriptWithinMainThread)
                {
                    root.ScriptNeedsMainThread = true;
                    return;
                }

                if (!root.DeviceLockAllowed || root.TimeLimitReached)
                {
                    return;
                }

                if (playfield.IsStructureDeviceLocked(structure.Id, position))
                {
                    return;
                }

                var lockkey = $"{structure.Id}#{position.x}#{position.y}#{position.z}";

                var e = new AutoResetEvent(false);
                playfield.LockStructureDevice(structure.Id, position, true, (id, pos, success) =>
                {
                    if (id != structure.Id || pos != position)
                    {
                        return;
                    }

                    if (witherror)
                    {
                        Log($"Lock:Callback:Error {playfield.Name} {structure.Id} {position}", LogLevel.Debug);
                        playfield.LockStructureDevice(structure.Id, position, false, null);
                    }
                    else
                    {
                        Success = success;
                        e.Set();
                    }
                });
                witherror = !e.WaitOne(10000);
                if (witherror)
                {
                    Log($"Lock:WaitOne:Error {playfield.Name} {structure.Id} {position}", LogLevel.Debug);
                }

                if (Success)
                {
                    unlockAction = () =>
                    {
                        e.Reset();
                        playfield.LockStructureDevice(structure.Id, position, false, (id, pos, s) => {
                            if (id != structure.Id || pos != position)
                            {
                                return;
                            }
                            e.Set();
                        });
                        if (!e.WaitOne(10000))
                        {
                            Log($"Unlock:Timeout {playfield.Name} {structure.Id} {position}", LogLevel.Debug);
                        }
                    }
                }
                ;
            }
            catch (Exception error)
            {
                witherror = true;
                throw new Exception($"DeviceLock:failed on Playfield:{playfield?.Name} at Id:{structure.Id} on {position} with: {error}");
            }
        }
 public CsScriptFunctions(IScriptRootData root)
 {
     ScriptRoot = root;
 }
 public ConsoleMock(IScriptRootData root)
 {
     Root = root;
 }
        static void ProcessBlockPart(TextWriter output, IScriptRootData root, IStructure S, ProcessBlockData processBlockData,
                                     IContainer target, VectorInt3 targetPos, string N, int replaceId, Tuple <int, int>[] list,
                                     Func <IContainer, int, bool> processBlock)
        {
            IDeviceLock locked = null;

            try
            {
                for (; processBlockData.Y >= processBlockData.MinPos.y; processBlockData.Y--)
                {
                    for (; processBlockData.X <= processBlockData.MaxPos.x; processBlockData.X++)
                    {
                        for (; processBlockData.Z <= processBlockData.MaxPos.z; processBlockData.Z++)
                        {
                            processBlockData.CheckedBlocks++;

                            var block = S.GetBlock(processBlockData.X, 128 + processBlockData.Y, processBlockData.Z);
                            if (block != null)
                            {
                                block.Get(out var blockType, out _, out _, out _);

                                if (list != null &&
                                    list.Length > 0 &&
                                    !list.Any(L => L.Item1 <= blockType && L.Item2 >= blockType))
                                {
                                    blockType = 0;
                                }

                                if (blockType > 0 && blockType != PlayerCoreType)
                                {
                                    if (EmpyrionScripting.Configuration.Current?.DeconstructBlockSubstitution != null &&
                                        EmpyrionScripting.Configuration.Current.DeconstructBlockSubstitution.TryGetValue(blockType, out var substituteTo))
                                    {
                                        blockType = substituteTo;
                                    }

                                    if (blockType > 0 && N != null)
                                    {
                                        locked = locked ?? WeakCreateDeviceLock(root, root.GetCurrentPlayfield(), root.E.S.GetCurrent(), targetPos);
                                        if (!locked.Success)
                                        {
                                            processBlockData.CheckedBlocks--;
                                            output.WriteLine($"Container '{N}' is locked");
                                            return;
                                        }

                                        if (processBlock(target, blockType))
                                        {
                                            processBlockData.CheckedBlocks--;
                                            output.WriteLine($"Container '{N}' is full");
                                            return;
                                        }
                                    }

                                    block.Set(replaceId);
                                    processBlockData.RemovedBlocks++;

                                    if (processBlockData.RemovedBlocks > 100 && processBlockData.RemovedBlocks % 100 == 0 && root.TimeLimitReached)
                                    {
                                        return;
                                    }
                                }
                            }
                        }
                        processBlockData.Z = processBlockData.MinPos.z;
                    }
                    processBlockData.X = processBlockData.MinPos.x;
                }
            }
            finally
            {
                locked?.Dispose();
            }
        }
        public static IList <IItemMoveInfo> Fill(IScriptRootData root, IItemsData item, IStructureData structure, StructureTankType type, int maxLimit)
        {
            if (!root.DeviceLockAllowed)
            {
                Log($"Fill: NoLockAllowed({root.ScriptId}): {root.CycleCounter} % {EmpyrionScripting.Configuration.Current.DeviceLockOnlyAllowedEveryXCycles}", LogLevel.Debug);
                return(ItemMoveInfo.Empty);
            }

            var specialTransfer = type switch
            {
                StructureTankType.Oxygen => structure.OxygenTank,
                StructureTankType.Fuel => structure.FuelTank,
                StructureTankType.Pentaxid => structure.PentaxidTank,
                _ => null,
            };

            if (specialTransfer == null || !specialTransfer.AllowedItem(item.Id))
            {
                return(ItemMoveInfo.Empty);
            }

            Log($"Fill Total: #{item.Source.Count}", LogLevel.Debug);

            var moveInfos = new List <IItemMoveInfo>();

            lock (moveLock) item.Source
                .ForEach(S => {
                    using var locked = WeakCreateDeviceLock(root, root.GetCurrentPlayfield(), S.E?.S.GetCurrent(), S.Position);
                    if (!locked.Success)
                    {
                        Log($"DeviceIsLocked (Source): {S.Id} #{S.Count} => {S.CustomName}", LogLevel.Debug);
                        return;
                    }

                    var count = specialTransfer.ItemsNeededForFill(S.Id, maxLimit);
                    if (count > 0)
                    {
                        count -= S.Container.RemoveItems(S.Id, count);
                        Log($"Move(RemoveItems): {S.CustomName} {S.Id} #{S.Count}->{count}", LogLevel.Debug);
                    }

                    ItemMoveInfo currentMoveInfo = null;

                    if (count > 0)
                    {
                        var startCount = count;
                        count          = specialTransfer.AddItems(S.Id, count);
                        if (startCount != count)
                        {
                            moveInfos.Add(currentMoveInfo = new ItemMoveInfo()
                            {
                                Id           = S.Id,
                                Count        = startCount - count,
                                SourceE      = S.E,
                                Source       = S.CustomName,
                                DestinationE = structure.E,
                                Destination  = type.ToString(),
                            });
                        }
                    }
                    ;

                    if (count > 0)
                    {
                        count = S.Container.AddItems(S.Id, count);
                    }
                    if (count > 0 && currentMoveInfo != null)
                    {
                        root.GetPlayfieldScriptData().MoveLostItems.Enqueue(new ItemMoveInfo()
                        {
                            Id      = S.Id,
                            Count   = count,
                            SourceE = S.E,
                            Source  = S.CustomName,
                        });
                        currentMoveInfo.Error = $"{{fill}} error lost #{count} of item {S.Id} in container {S.CustomName} -> add to retry list";
                    }
                }, () => root.TimeLimitReached);

            return(moveInfos);
        }
        public static IList <IItemMoveInfo> Move(IScriptRootData root, IItemsData item, IStructureData structure, string namesSearch, int?maxLimit)
        {
            if (!root.DeviceLockAllowed)
            {
                Log($"Move: NoLockAllowed({root.ScriptId}): {root.CycleCounter} % {EmpyrionScripting.Configuration.Current.DeviceLockOnlyAllowedEveryXCycles}", LogLevel.Debug);
                return(ItemMoveInfo.Empty);
            }

            if (root.TimeLimitReached)
            {
                Log($"Move: TimeLimitReached({root.ScriptId})", LogLevel.Debug);
                return(ItemMoveInfo.Empty);
            }

            var uniqueNames = structure.AllCustomDeviceNames.GetUniqueNames(namesSearch);

            if (!uniqueNames.Any())
            {
                Log($"NoDevicesFound: {namesSearch}", LogLevel.Debug);
                return(ItemMoveInfo.Empty);
            }

            var moveInfos = new List <IItemMoveInfo>();

            lock (moveLock) item.Source
                .ForEach(S => {
                    using var locked = WeakCreateDeviceLock(root, root.GetCurrentPlayfield(), S.E?.S.GetCurrent(), S.Position);
                    if (!locked.Success)
                    {
                        Log($"DeviceIsLocked (Source): {S.Id} #{S.Count} => {S.CustomName}", LogLevel.Debug);
                        return;
                    }

                    var count = S.Count;
                    count    -= S.Container.RemoveItems(S.Id, count);
                    Log($"Move(RemoveItems): {S.CustomName} {S.Id} #{S.Count}->{count}", LogLevel.Debug);

                    ItemMoveInfo currentMoveInfo = null;

                    if (count > 0)
                    {
                        uniqueNames
                        .Where(N => N != S.CustomName)
                        .ForEach(N => {
                            var startCount = count;
                            count          = MoveItem(root, S, N, structure, count, maxLimit);
                            if (startCount != count)
                            {
                                var movedCount = startCount - count;
                                moveInfos.Add(currentMoveInfo = new ItemMoveInfo()
                                {
                                    Id           = S.Id,
                                    Count        = movedCount,
                                    SourceE      = S.E,
                                    Source       = S.CustomName,
                                    DestinationE = structure.E,
                                    Destination  = N,
                                });

                                Log($"Move(AddItems): {S.CustomName} {S.Id} #{S.Count}->{startCount - count}", LogLevel.Debug);

                                // Für diesen Scriptdurchlauf dieses Item aus der Verarbeitung nehmen
                                S.Count -= movedCount;
                            }
                            ;
                        }, () => root.TimeLimitReached);
                    }

                    if (count > 0)
                    {
                        var retoureCount = count;
                        count            = S.Container.AddItems(S.Id, retoureCount);
                        Log($"Move(retoure): {S.CustomName} {retoureCount} -> {count}", LogLevel.Debug);
                    }

                    if (count > 0)
                    {
                        root.GetPlayfieldScriptData().MoveLostItems.Enqueue(new ItemMoveInfo()
                        {
                            Id      = S.Id,
                            Count   = count,
                            SourceE = S.E,
                            Source  = S.CustomName,
                        });
                        currentMoveInfo.Error = $"{{move}} error lost #{count} of item {S.Id} in container {S.CustomName} -> add to retry list";
                    }
                }, () => root.TimeLimitReached);

            return(moveInfos);
        }