public static byte[] GetRectangle(ChunkCache2D cache, short left, short top, short right, short bottom)
        {
            int rightExclusive  = right + 1;
            int bottomExclusive = bottom + 1;
            int width           = rightExclusive - left;
            int height          = bottomExclusive - top;

            byte[] bytes = new byte[height * width];

            unsafe
            {
                fixed(byte *byteArrPtr = &bytes[0])
                {
                    byte *byteArrStartPosition = byteArrPtr;

                    Parallel.ForEach(cache.CachedChunks, chunkEntry =>
                    {
                        fixed(byte *chunkPtr = &chunkEntry.Value[0, 0])
                        {
                            (byte chunkX, byte chunkY) = chunkEntry.Key;
                            short xChunkStart          = PixelMap.RelativeToAbsolute(chunkX, 0);
                            short yChunkStart          = PixelMap.RelativeToAbsolute(chunkY, 0);
                            short xChunkEndExclusive   = PixelMap.RelativeToAbsolute(chunkX + 1, 0);
                            short yChunkEndExclusive   = PixelMap.RelativeToAbsolute(chunkY + 1, 0);

                            short xBlockStart      = Math.Max(xChunkStart, left);
                            short yBlockStart      = Math.Max(yChunkStart, top);
                            int xBlockEndExclusive = Math.Min(xChunkEndExclusive, rightExclusive);
                            int yBlockEndExclusive = Math.Min(yChunkEndExclusive, bottomExclusive);

                            if (xBlockStart >= xBlockEndExclusive || yBlockStart >= yBlockEndExclusive)
                            {
                                return;
                            }

                            int xBlockStartChunkOffset        = xBlockStart - xChunkStart;
                            int yBlockStartChunkOffset        = yBlockStart - yChunkStart;
                            int yBlockEndExclusiveChunkOffset = yBlockEndExclusive - yChunkStart;
                            int blockWidth = xBlockEndExclusive - xBlockStart;

                            byte *outputCurrentPosition = byteArrStartPosition + xBlockStart - left + (yBlockStart - top) * width;
                            byte *inputCurrentPosition  = chunkPtr + xBlockStartChunkOffset + PixelMap.ChunkSize * yBlockStartChunkOffset;

                            for (int chunkLine = yBlockStartChunkOffset; chunkLine < yBlockEndExclusiveChunkOffset; chunkLine++)
                            {
                                Buffer.MemoryCopy(inputCurrentPosition, outputCurrentPosition, blockWidth, blockWidth);
                                inputCurrentPosition  += PixelMap.ChunkSize;
                                outputCurrentPosition += width;
                            }
                        }
                    });
                }
            }
            return(bytes);
        }
Пример #2
0
        private static async Task Main(string[] args)
        {
            try
            {
                if (!ParseArguments(args))
                {
                    return;
                }

                logger = new Logger(options?.LogFilePath, finishCTS.Token)
                {
                    ShowDebugLogs = options?.ShowDebugLogs ?? false
                };
                logger.LogDebug("Command line: " + Environment.CommandLine);

                logger.LogTechState("Connecting to API...");
                PixelPlanetHttpApi api = new PixelPlanetHttpApi
                {
                    ProxySettings = proxySettings
                };

                user = await api.GetMeAsync();

                logger.LogTechInfo("Successfully connected");

                CanvasModel canvas = user.Canvases[options.Canvas];

                if (canvas.Is3D)
                {
                    throw new Exception("3D canvas is not supported");
                }

                LoggerExtensions.MaxCoordXYLength = 1 + (int)Math.Log10(canvas.Size / 2);

                PixelMap.MapSize = canvas.Size;

                try
                {
                    if (options.LeftX < -(canvas.Size / 2) || options.RightX >= canvas.Size / 2)
                    {
                        throw new Exception("X");
                    }

                    if (options.TopY < -(canvas.Size / 2) || options.BottomY >= canvas.Size / 2)
                    {
                        throw new Exception("Y");
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception($"Entire rectangle should be inside the map (failed by {ex.Message})");
                }

                colorNameResolver = new ColorNameResolver(options.Canvas);

                if (checkUpdates || !options.DisableUpdates)
                {
                    if (UpdateChecker.IsStartingUpdate(logger, checkUpdates) || checkUpdates)
                    {
                        return;
                    }
                }

                cache = new ChunkCache2D(options.LeftX, options.TopY, options.RightX, options.BottomY, logger, options.Canvas);
                bool initialMapSavingStarted = false;
                saveThread = new Thread(SaveChangesThreadBody);
                saveThread.Start();
                if (string.IsNullOrWhiteSpace(options.FileName))
                {
                    options.FileName = string.Format("pixels_({0};{1})-({2};{3})_{4:yyyy.MM.dd_HH-mm}.bin", options.LeftX, options.TopY, options.RightX, options.BottomY, DateTime.Now);
                }
                Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(options.FileName)));
                do
                {
                    try
                    {
                        using (WebsocketWrapper wrapper = new WebsocketWrapper(logger, true, proxySettings, null, options.Canvas))
                        {
                            cache.Wrapper = wrapper;
                            if (!initialMapSavingStarted)
                            {
                                logger.LogDebug("Main(): initiating map saving");
                                initialMapSavingStarted = true;
                                lockingStreamTask       = Task.Run(SaveInitialMapState);
                            }
                            wrapper.OnMapChanged   += Wrapper_OnMapChanged;
                            stopListening           = wrapper.StopListening;
                            Console.CancelKeyPress += (o, e) =>
                            {
                                logger.LogDebug("Console.CancelKeyPress received");
                                e.Cancel = true;
                                wrapper.StopListening();
                            };
                            logger.LogInfo("Press Ctrl+C to stop");
                            wrapper.StartListening();
                            break;
                        }
                    }
                    catch (Exception ex)
                    {
                        logger.LogError($"Unhandled exception: {ex.Message}");
                        Thread.Sleep(1000);
                    }
                } while (true);
            }
            catch (Exception ex)
            {
                logger?.LogError($"Unhandled app level exception: {ex.Message}");
                logger?.LogDebug(ex.ToString());
            }
            finally
            {
                if (logger != null)
                {
                    logger.LogInfo("Exiting when everything is saved...");
                    logger.LogInfo($"Logs were saved to {logger.LogFilePath}");
                }
                finishCTS.Cancel();
                if (logger != null)
                {
                    Thread.Sleep(500);
                }
                finishCTS.Dispose();
                logger?.Dispose();
                Console.ForegroundColor = ConsoleColor.White;
                if (saveThread != null && !saveThread.Join(TimeSpan.FromMinutes(1)))
                {
                    Console.WriteLine("Save thread doesn't finish, aborting");
                    Environment.Exit(0);
                }
            }
        }