public async Task Run()
        {
            if (options.Canvas == CanvasType.Moon)
            {
                throw new Exception("Moon canvas is not designed for bots");
            }

            if (options.SessionName != null)
            {
                logger.LogTechState("Loading session...");
                SessionManager sessionManager = new SessionManager(proxySettings);
                session = sessionManager.GetSession(options.SessionName);
                logger.LogTechInfo("Session loaded");
            }

            logger.LogTechState("Connecting to API...");
            PixelPlanetHttpApi api = new PixelPlanetHttpApi
            {
                ProxySettings = proxySettings,
                Session       = session
            };
            UserModel user = await api.GetMeAsync(finishToken);

            logger.LogTechInfo("Successfully connected");
            if (options.SessionName != null)
            {
                if (user.Name != null)
                {
                    logger.LogTechInfo($"Session is alive; user \"{user.Name}\"");
                }
                else
                {
                    throw new SessionExpiredException();
                }
            }

            canvas = user.Canvases[options.Canvas];

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

            palette           = new Palette(canvas.Colors, canvas.Is3D);
            colorNameResolver = new ColorNameResolver(options.Canvas);

            await LoadImage();

            logger.LogTechState("Calculating pixel placing order...");
            CalculateOrder();
            logger.LogTechInfo("Pixel placing order is calculated");

            InitCache();

            mapUpdatedResetEvent = new ManualResetEvent(false);
            cache.OnMapUpdated  += (o, e) =>
            {
                logger.LogDebug("cache.OnMapUpdated event fired");
                mapUpdatedResetEvent.Set();
            };
            if (options.DefenseMode)
            {
                gotGriefed             = new AutoResetEvent(false);
                cache.OnMapUpdated    += (o, e) => gotGriefed.Set();
                waitingGriefLockObject = new object();
            }
            Thread statsThread = new Thread(StatsCollectionThreadBody);

            statsThread.Start();
            do
            {
                try
                {
                    using (WebsocketWrapper wrapper = new WebsocketWrapper(logger, false, proxySettings, session, options.Canvas))
                    {
                        wrapper.OnConnectionLost += (o, e) => mapUpdatedResetEvent.Reset();
                        cache.Wrapper             = wrapper;
                        cache.DownloadChunks();
                        wrapper.OnMapChanged += LogMapChanged;
                        ClearPlaced();
                        bool wasChanged;
                        do
                        {
                            repeatingFails = false;
                            wasChanged     = await PerformBuildingCycle(wrapper);

                            if (!wasChanged && options.DefenseMode)
                            {
                                logger.Log("Image is intact, waiting...", MessageGroup.Info);
                                lock (waitingGriefLockObject)
                                {
                                    logger.LogDebug("Run(): acquiring grief waiting lock");
                                    gotGriefed.Reset();
                                    gotGriefed.WaitOne();
                                }
                                logger.LogDebug("Run(): got griefed");
                                await Task.Delay(ThreadSafeRandom.Next(500, 3000), finishToken);
                            }
                        } while (options.DefenseMode || wasChanged);
                        logger.Log("Building is finished", MessageGroup.Info);
                    }
                    return;
                }
                catch (Exception ex)
                {
                    logger.LogError($"Unhandled exception: {ex.GetBaseException().Message}");
                    logger.LogDebug(ex.ToString());
                    int delay = repeatingFails ? 30 : 10;
                    repeatingFails = true;
                    logger.LogTechState($"Reconnecting in {delay} seconds...");
                    Thread.Sleep(TimeSpan.FromSeconds(delay));
                    continue;
                }
            } while (true);
        }
Example #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);
                }
            }
        }