private static void Main(string[] args) { try { if (args.Length == 1) { try { SaveFingerprint(Guid.Parse(args[0])); Console.WriteLine("Fingerprint is saved, now you can relauch bot with needed parameters"); } catch { Console.WriteLine("You should pass correct 128-bit fingerprint (GUID)"); } return; } ushort width, height; PlacingOrderMode order = PlacingOrderMode.Random; notificationMode = CaptchaNotificationMode.Browser; try { try { leftX = short.Parse(args[0]); topY = short.Parse(args[1]); fingerprint = GetFingerprint(); string logFilePath = null; if (args.Length > 3) { notificationMode = CaptchaNotificationMode.None; string upper = args[3].ToUpper(); if (upper.Contains('B')) { notificationMode |= CaptchaNotificationMode.Browser; } if (upper.Contains('S')) { notificationMode |= CaptchaNotificationMode.Sound; } if (args.Length > 4) { defendMode = args[4].ToUpper() == "Y"; if (args.Length > 5) { switch (args[5].ToUpper()) { case "R": order = PlacingOrderMode.FromRight; break; case "L": order = PlacingOrderMode.FromLeft; break; case "T": order = PlacingOrderMode.FromTop; break; case "B": order = PlacingOrderMode.FromBottom; break; } if (args.Length > 6) { try { File.OpenWrite(args[6]).Dispose(); logFilePath = args[6]; } catch { } } } } } logger = new Logger(finishCTS.Token, logFilePath); imagePixels = ImageProcessing.PixelColorsByUri(args[2], logger.LogLine); checked { width = (ushort)imagePixels.GetLength(0); height = (ushort)imagePixels.GetLength(1); short check; check = (short)(leftX + width); check = (short)(topY + height); } } catch (OverflowException) { throw new Exception("Entire image should be inside the map"); } catch (WebException) { throw new Exception("Cannot download image"); } catch (ArgumentException) { throw new Exception("Cannot convert image"); } catch (IOException) { throw new Exception("Fingerprint is not saved, pass it from browser as only parameter to app to save before usage"); } catch { throw new Exception("Parameters: <leftX> <topY> <imageURI> [notificationMode: N/B/S/BS = B] [defendMode: Y/N = N] [buildFrom L/R/T/B/RND = RND] [logFileName = none]"); } } catch (Exception ex) { Console.WriteLine(ex.Message); finishCTS.Cancel(); return; } IEnumerable <int> allY = Enumerable.Range(0, height); IEnumerable <int> allX = Enumerable.Range(0, width); Pixel[] nonEmptyPixels = allX. SelectMany(X => allY.Select(Y => (X: (short)(X + leftX), Y: (short)(Y + topY), C: imagePixels[X, Y]))). Where(xy => xy.C != PixelColor.None).ToArray(); switch (order) { case PlacingOrderMode.FromLeft: pixelsToBuild = nonEmptyPixels.OrderBy(xy => xy.Item1).ToList(); break; case PlacingOrderMode.FromRight: pixelsToBuild = nonEmptyPixels.OrderByDescending(xy => xy.Item1).ToList(); break; case PlacingOrderMode.FromTop: pixelsToBuild = nonEmptyPixels.OrderBy(xy => xy.Item2).ToList(); break; case PlacingOrderMode.FromBottom: pixelsToBuild = nonEmptyPixels.OrderByDescending(xy => xy.Item2).ToList(); break; default: Random rnd = new Random(); for (int i = 0; i < nonEmptyPixels.Length; i++) { int r = rnd.Next(i, nonEmptyPixels.Length); Pixel tmp = nonEmptyPixels[r]; nonEmptyPixels[r] = nonEmptyPixels[i]; nonEmptyPixels[i] = tmp; } pixelsToBuild = nonEmptyPixels; break; } cache = new ChunkCache(pixelsToBuild, logger.LogLine); mapDownloadedResetEvent = new ManualResetEvent(false); cache.OnMapDownloaded += (o, e) => mapDownloadedResetEvent.Set(); if (defendMode) { gotGriefed = new AutoResetEvent(false); cache.OnMapDownloaded += (o, e) => gotGriefed.Set(); waitingGriefLock = new object(); } statsThread = new Thread(StatsCollectionThreadBody); statsThread.Start(); do { try { using (InteractionWrapper wrapper = new InteractionWrapper(fingerprint, logger.LogLine)) { wrapper.OnPixelChanged += LogPixelChanged; wrapper.OnConnectionLost += (o, e) => mapDownloadedResetEvent.Reset(); cache.Wrapper = wrapper; cache.DownloadChunks(); placed.Clear(); bool wasChanged; do { wasChanged = false; repeatingFails = false; foreach (Pixel pixel in pixelsToBuild) { (short x, short y, PixelColor color) = pixel; PixelColor actualColor = cache.GetPixelColor(x, y); if (!IsCorrectPixelColor(actualColor, color)) { wasChanged = true; bool success; placed.Add(pixel); do { byte placingPixelFails = 0; mapDownloadedResetEvent.WaitOne(); success = wrapper.PlacePixel(x, y, color, out double cd, out double totalCd, out string error); if (success) { string prefix = cd == 4 ? "P" : "Rep"; logger.LogPixel($"{prefix}laced pixel:", MessageGroup.Pixel, x, y, color); Thread.Sleep(TimeSpan.FromSeconds(totalCd < 53 ? 1 : cd)); } else { if (cd == 0.0) { logger.LogLine("Please go to browser and place pixel, then return and press any key", MessageGroup.Captcha); if (notificationMode.HasFlag(CaptchaNotificationMode.Sound)) { Task.Run(() => { for (int j = 0; j < 7; j++) { Console.Beep(1000, 100); } }); } if (notificationMode.HasFlag(CaptchaNotificationMode.Browser)) { Process.Start($"{InteractionWrapper.BaseHttpAdress}/#{x},{y},30"); } Thread.Sleep(100); logger.ConsoleLoggingResetEvent.Reset(); while (Console.KeyAvailable) { Console.ReadKey(true); } Console.ReadKey(true); logger.ConsoleLoggingResetEvent.Set(); } else { logger.LogLine($"Failed to place pixel: {error}", MessageGroup.PixelFail); if (++placingPixelFails == 3) { throw new Exception("Cannot place pixel 3 times"); } } Thread.Sleep(TimeSpan.FromSeconds(cd)); } } while (!success); } } if (defendMode) { if (!wasChanged) { logger.LogLine("Image is intact, waiting...", MessageGroup.ImageDone); lock (waitingGriefLock) { gotGriefed.Reset(); gotGriefed.WaitOne(); } Thread.Sleep(new Random().Next(500, 3000)); } } }while (defendMode || wasChanged); logger.LogLine("Building is finished, exiting...", MessageGroup.ImageDone); return; } } catch (Exception ex) { logger.LogLine($"Unhandled exception: {ex.Message}", MessageGroup.Error); int delay = repeatingFails ? 30 : 10; repeatingFails = true; logger.LogLine($"Reconnecting in {delay} seconds...", MessageGroup.TechState); Thread.Sleep(TimeSpan.FromSeconds(delay)); continue; } } while (true); } finally { finishCTS.Cancel(); gotGriefed?.Dispose(); mapDownloadedResetEvent?.Dispose(); logger?.Dispose(); Thread.Sleep(1000); finishCTS.Dispose(); if (statsThread?.IsAlive ?? false) { statsThread.Interrupt(); //fallback, should never work } } }
static void Main(string[] args) { try { try { try { x1 = short.Parse(args[0]); y1 = short.Parse(args[1]); x2 = short.Parse(args[2]); y2 = short.Parse(args[3]); if (x1 > x2 || y1 > y2) { throw new Exception(); } try { File.Open(args[5], FileMode.Append, FileAccess.Write).Dispose(); logFilePath = args[5]; } catch { } } catch (OverflowException) { throw new Exception("Entire watched zone should be inside the map"); } catch { throw new Exception("Parameters: <leftX> <topY> <rightX> <bottomY> [logFilePath] ; all in range -32768..32767"); } } catch (Exception ex) { Console.WriteLine(ex.Message); finishCTS.Cancel(); return; } logger = new Logger(finishCTS.Token, logFilePath); string fingerprint = Guid.NewGuid().ToString("N"); cache = new ChunkCache(x1, y1, x2, y2, logger.LogLine); bool initialMapStateSaved = false; saveThread.Start(); filename = string.Format("pixels_({0};{1})-({2};{3})_{4:yyyy.MM.dd_HH-mm}.bin", x1, y1, x2, y2, DateTime.Now); do { try { using (InteractionWrapper wrapper = new InteractionWrapper(fingerprint, logger.LogLine, true)) { cache.Wrapper = wrapper; if (!initialMapStateSaved) { Task.Run(() => { initialMapStateSaved = true; cache.DownloadChunks(); using (FileStream fileStream = File.Open(filename, FileMode.Create, FileAccess.Write)) { using (BinaryWriter writer = new BinaryWriter(fileStream)) { writer.Write(x1); writer.Write(y1); writer.Write(x2); writer.Write(y2); writer.Write(DateTime.Now.ToBinary()); for (short y = y1; y <= y2; y++) { for (short x = x1; x <= x2; x++) { writer.Write((byte)cache.GetPixelColor(x, y)); } } } } logger.LogLine("Chunk data is saved to file", MessageGroup.TechInfo); lockingStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None); }); } wrapper.OnPixelChanged += (o, e) => { short x = PixelMap.ConvertToAbsolute(e.Chunk.Item1, e.Pixel.Item1); short y = PixelMap.ConvertToAbsolute(e.Chunk.Item2, e.Pixel.Item2); if (x <= x2 && x >= x1 && y <= y2 && y >= y1) { logger.LogPixel("Received pixel update:", MessageGroup.PixelInfo, x, y, e.Color); lock (listLockObj) { updates.Add((x, y, e.Color)); } } }; wrapper.StartListening(); } } catch (Exception ex) { logger.LogLine($"Unhandled exception: {ex.Message}", MessageGroup.Error); } } while (true); } finally { finishCTS.Cancel(); Thread.Sleep(1000); finishCTS.Dispose(); logger?.Dispose(); if (saveThread.IsAlive) { saveThread.Interrupt(); } } }
private static void MainWorkingBody() { using (WebsocketWrapper wrapper = new WebsocketWrapper(logger, false)) { wrapper.OnConnectionLost += (o, e) => mapUpdatedResetEvent.Reset(); cache.Wrapper = wrapper; logger.LogDebug("MainWorkingBody(): downloading chunks"); cache.DownloadChunks(); wrapper.OnPixelChanged += LogPixelChanged; placed.Clear(); bool wasChanged; do { logger.LogDebug("MainWorkingBody(): main body cycle started"); wasChanged = false; repeatingFails = false; foreach (Pixel pixel in pixelsToBuild) { mapUpdatedResetEvent.WaitOne(); (short x, short y, EarthPixelColor color) = pixel; EarthPixelColor actualColor = cache.GetPixelColor(x, y); if (!IsCorrectPixelColor(actualColor, color)) { logger.LogDebug($"MainWorkingBody(): {pixel} - wrong color ({actualColor})"); wasChanged = true; bool success; placed.Add(pixel); do { wrapper.WaitWebsocketConnected(); success = HttpWrapper.PlacePixel(x, y, color, out double cd, out double totalCd, out string error); if (success) { logger.LogPixel($"{(cd == 4 ? "P" : "Rep")}laced pixel:", DateTime.Now, MessageGroup.Pixel, x, y, color); Thread.Sleep(TimeSpan.FromSeconds(totalCd < 53 ? 1 : cd)); } else { logger.LogDebug($"MainWorkingBody(): pixel placing handled error {error}"); if (error == "captcha") { ProcessCaptcha(); } else { logger.Log($"Failed to place pixel: {error}", MessageGroup.PixelFail); } logger.LogDebug($"MainWorkingBody(): sleep {cd:F2} seconds"); Thread.Sleep(TimeSpan.FromSeconds(cd)); } } while (!success); } } if (options.DefenseMode) { if (!wasChanged) { logger.Log("Image is intact, waiting...", MessageGroup.Info); lock (waitingGriefLock) { logger.LogDebug("MainWorkingBody(): acquiring grief waiting lock"); gotGriefed.Reset(); gotGriefed.WaitOne(); } logger.LogDebug("MainWorkingBody(): got griefed"); Thread.Sleep(new Random().Next(500, 3000)); } } } while (options.DefenseMode || wasChanged); logger.Log("Building is finished", MessageGroup.Info); return; } }