Greasy Fork is available in English.

Library For Mxobot

Library for Mxobot

Ce script ne doit pas être installé directement. C'est une librairie destinée à être incluse dans d'autres scripts avec la méta-directive // @require https://update.greasyfork.org/scripts/461063/1374966/Library%20For%20Mxobot.js

// ==UserScript==
// @name         Mxobot Library
// @namespace    http://tampermonkey.net/<3nevin
// @version      1.3
// @description  Library for Mxobot
// @author       @ngixl
// @match        https://pixelplace.io/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=pixelplace.io
// @grant        unsafeWindow
// @require      https://update.greasyfork.org/scripts/461063/1371348/Library%20For%20MxoBot.js
 
/* globals unsafeWindow*/
/*jshint esversion: 11 */
 
Object.defineProperty(unsafeWindow, "console", {
  value: console,
  writable: false,
});
 
class NevinLoggerFactory {
  static TEMPLATE = "%c[NevinCore] %s: %s";
  static CSS_INFO = "color:green";
  static CSS_WARNING = "color:yellow;";
  static CSS_ERROR = "color:red;font-weight: bold;";
  static TEMPLATE_INFO = "INFO";
  static TEMPLATE_WARNING = "WARNING";
  static TEMPLATE_ERROR = "ERROR";
  static LEVEL_INFO = 0;
  static LEVEL_WARNING = 1;
  static LEVEL_ERROR = 2;
  LEVEL = NevinLoggerFactory.LEVEL_INFO;
  constructor() {
    this.listeners = [];
    this.listeners.push(function (template, css, level, msg) {
      console.log(template, css, level, msg);
    });
  }
  dispatch(template, css, level, msg) {
    this.listeners.forEach((listener) => {
      listener(template, css, level, msg);
    });
  }
  info(msg) {
    if (this.LEVEL <= NevinLoggerFactory.LEVEL_INFO) {
      this.dispatch(
        NevinLoggerFactory.TEMPLATE,
        NevinLoggerFactory.CSS_INFO,
        NevinLoggerFactory.TEMPLATE_INFO,
        msg
      );
    }
  }
  warning(msg) {
    if (this.LEVEL <= NevinLoggerFactory.LEVEL_WARNING) {
      this.dispatch(
        NevinLoggerFactory.TEMPLATE,
        NevinLoggerFactory.CSS_WARNING,
        NevinLoggerFactory.TEMPLATE_WARNING,
        msg
      );
    }
  }
  error(msg) {
    if (this.LEVEL <= NevinLoggerFactory.LEVEL_ERROR) {
      this.dispatch(
        NevinLoggerFactory.TEMPLATE,
        NevinLoggerFactory.CSS_ERROR,
        NevinLoggerFactory.TEMPLATE_ERROR,
        msg
      );
      throw Error(msg);
    }
  }
}
 
class NevinImageConverter {
    static getClosestColor(r, g, b, palette) {
        let closestColor = {r: 0, g: 0, b: 0};
        let closestDistance = Number.MAX_VALUE;
        for (let i = 0;i < palette.colors.length; i++) {
            let bigint = palette.colors[i];
            let p_r = (bigint >> 16) & 255;
            let p_g = (bigint >> 8) & 255;
            let p_b = bigint & 255;
            let distance = (r - p_r)**2 + (g - p_g)**2 + (b - p_b)**2;
            if (distance < closestDistance) {
                closestColor = {r: p_r, g: p_g, b: p_b};
                closestDistance = distance;
            }
        }
        return closestColor;
    }
    static floydSteinberg(img_data, w, h, palette) {
        if (unsafeWindow.BOT_DO_NOT_DITHER === true) {
            return img_data;
        }
        let dithered = new Uint8ClampedArray(img_data.data);
        let error_matrix = new Float32Array(w * h * 4);
        for (let y = 0; y < h; y++) {
            for (let x = 0;x < w; x++) {
                let i = (y * w + x) * 4;
                let r = img_data.data[i] + error_matrix[i];
                let g = img_data.data[i + 1] + error_matrix[i + 1];
                let b = img_data.data[i + 2] + error_matrix[i + 2];
                let closest = NevinImageConverter.getClosestColor(r, g, b, palette);
                dithered[i] = closest.r;
                dithered[i + 1] = closest.g;
                dithered[i + 2] = closest.b;
                dithered[i + 3] = img_data.data[i + 3];
                let err_r = r - closest.r;
                let err_g = g - closest.g;
                let err_b = b - closest.b;
                if (x + 1 < w) {
                    error_matrix[i + 4] += err_r * 7 / 16;
                    error_matrix[i + 5] += err_g * 7 / 16;
                    error_matrix[i + 6] += err_b * 7 / 16;
                }
                if (y + 1 < h) {
                    if (x > 0) {
                        error_matrix[i + 4 * w - 4] += err_r * 3 / 16;
                        error_matrix[i + 4 * w - 3] += err_g * 3 / 16;
                        error_matrix[i + 4 * w - 2] += err_b * 3 / 16;
                    }
                    error_matrix[i + 4 * w] += err_r * 5 / 16;
                    error_matrix[i + 4 * w + 1] += err_g * 5 / 16;
                    error_matrix[i + 4 * w + 2] += err_b * 5 / 16;
                    if (x + 1 < w) {
                        error_matrix[i + 4 * w + 4] += err_r * 1 / 16;
                        error_matrix[i + 4 * w + 5] += err_g * 1 / 16;
                        error_matrix[i + 4 * w + 6] += err_b * 1 / 16;
                    }
                }
            }
        }
        const dithered_img_data = new ImageData(dithered, w, h);
        return dithered_img_data;
    }
}
 
const NevinLogger = new NevinLoggerFactory();
 
class NevinPalette {
  static PALETTE_LOAD_STATIC = 0;
  static PALETTE_LOAD_DYNAMIC = 1;
  static hexStrToHex(hex_str) {
    return parseInt(hex_str.slice(1), 16);
  }
  static STATIC_COLORS = [
    16777215, 12895428, 8947848, 5592405, 2236962, 0, 13880, 26112, 1799168,
    4681808, 2273612, 179713, 5366041, 9756740, 10025880, 16514907, 15063296,
    15121932, 15045888, 16740352, 16726276, 15007744, 13510969, 16728426,
    10420224, 7012352, 16741727, 10512962, 6503455, 10048269, 12275456,
    16762015, 16768972, 16754641, 13594340, 8201933, 15468780, 8519808, 3342455,
    132963, 5308671, 234, 281599, 23457, 6652879, 3586815, 33735, 54237,
    4587464, 11921646,
  ];
  static STATIC_INDEX = [
    0, 1, 2, 3, 4, 5, 39, 6, 49, 40, 7, 8, 9, 10, 41, 11, 12, 13, 14, 42, 21,
    20, 43, 44, 19, 18, 23, 15, 17, 16, 22, 24, 25, 26, 27, 45, 28, 29, 46, 31,
    30, 32, 33, 47, 34, 35, 36, 37, 38, 48,
  ];
  initalizePalette(type) {
    if (type == undefined) {
      type = NevinPalette.PALETTE_LOAD_STATIC;
      NevinLogger.warning(
        "NevinPalette invoked without specifying the loading type."
      );
    }
    NevinLogger.info(
      "NevinPalette loading with type: " +
        (type == NevinPalette.PALETTE_LOAD_DYNAMIC ? "DYNAMIC" : "STATIC")
    );
    if (type == NevinPalette.PALETTE_LOAD_DYNAMIC) {
      const palette = document.getElementById("palette-buttons");
      if (!palette) {
        NevinLogger.error(
          "Palette requested to be loaded dynamically but HTML is not loaded yet."
        );
      }
      this.colors = [];
      this.indexes = [];
      const palette_buttons = Array.from(palette.children);
      NevinLogger.info("Dynamic loading found these DOM elements:");
      console.log(palette_buttons);
      for (const palette_button of palette_buttons) {
        const color = {
          hex: palette_button.getAttribute("title"),
          index: palette_button.getAttribute("data-id"),
        };
 
        this.colors.push(NevinPalette.hexStrToHex(color.hex));
        this.indexes.push(parseInt(color.index));
      }
    } else {
      this.colors = NevinPalette.STATIC_COLORS;
      this.indexes = NevinPalette.STATIC_INDEX;
    }
  }
  getIndex(x) {
    if (x instanceof Array) {
      const [r, g, b] = x;
      const hex = (r << 16) | (g << 8) | b;
      return this.indexes[this.colors.indexOf(hex)] ?? -1;
    } else if (typeof x == "number") {
      return this.indexes[this.colors.indexOf(x)] ?? -1;
    } else {
      NevinLogger.error("Argument is neither type of Array nor a number");
    }
  }
  constructor(type) {
    this.colors = undefined;
    this.indexes = undefined;
    this.initalizePalette(type);
  }
}
 
class NevinOriginalWebSocket extends WebSocket {}
 
class NevinWS {
  constructor(nevinPalette, webSocket) {
    if (webSocket) {
      this.ws = webSocket;
      if (nevinPalette) {
        this.nevinMapCache = new NevinMapCache(nevinPalette, this.ws);
        this.nevinMapCache.addPixelChangeListener(this);
      }
    } else {
      this.ws = undefined;
      var proxy = this;
      this.hook = class extends WebSocket {
        constructor(a, b) {
          super(a, b);
          NevinLogger.info("NevinWS has hooked the game WebSocket connection.");
          proxy.ws = this;
          proxy.nevinMapCache.addPixelChangeListener(proxy);
        }
      };
      if (typeof unsafeWindow !== undefined) {
        if (unsafeWindow.WebSocket != NevinWS) {
          unsafeWindow.WebSocket = this.hook;
        }
      }
      this.nevinMapCache = new NevinMapCache(nevinPalette, this);
    }
  }
}
 
var map_cache;
class NevinMapCache {
  init(nevinPalette, nevinWS) {
    var canvas_id = parseInt(location.pathname.replace("/", "").split("-")[0]);
    var url = `https://pixelplace.io/canvas/${canvas_id}.png?a=${
      Math.floor(Math.random() * 1e9) + 1e9
    }`;
    var canvas_image = new Image();
    var spare_canvas = document.createElement("canvas");
    this.before_poll = [];
    this.cache = map_cache;
    if (this.cache) return;
    spare_canvas.ctx = spare_canvas.getContext("2d");
    canvas_image.onload = () => {
      NevinLogger.info("Map loaded");
      this.map_width = canvas_image.naturalWidth;
      this.map_height = canvas_image.naturalHeight;
      spare_canvas.width = this.map_width;
      spare_canvas.height = this.map_height;
      spare_canvas.ctx.drawImage(
        canvas_image,
        0,
        0,
        this.map_width,
        this.map_height
      );
      var data = spare_canvas.ctx.getImageData(
        0,
        0,
        this.map_width,
        this.map_height
      ).data;
      this.cache = new Int8Array(this.map_width * this.map_height);
      for (let i = 0; i < data.length; i += 4) {
        // slice is slower in custom arrays such as Int8Array
        var r = data[i];
        var g = data[i + 1];
        var b = data[i + 2];
        const i_color = nevinPalette.getIndex([r, g, b]);
        this.cache[i >> 2] = i_color;
      }
      for (let packet of this.before_poll) {
        this.cache[packet[0]] = packet[1];
      }
      this.before_poll = undefined;
    };
    canvas_image.src = url;
  }
  constructor(nevinPalette, nevinWS) {
    this.init(nevinPalette, nevinWS);
  }
  getPixel(x, y) {
    var i = y * this.map_width + x;
    return this.cache[i];
  }
  addPixelChangeListener(nevinWS) {
    nevinWS.ws.addEventListener("message", (e) => {
      var data = e.data;
      if (!data.startsWith('42["p",')) {
        return;
      }
      var packets = JSON.parse(data.replace("42", ""))[1];
      for (let packet of packets) {
        var [x, y, color] = packet;
        var i = this.map_width * y + x;
        if (this.cache) {
          this.cache[i] = color;
        } else {
          this.before_poll.push([i, color]);
        }
      }
    });
  }
}
 
class NevinImagePicker {
  static requestImageFromFileDialog(NevinPalette) {
    return new Promise((resolve) => {
      const input = document.createElement("input");
      input.type = "file";
      input.accept = "image/*";
      input.click();
      input.addEventListener("change", function () {
        const reader = new FileReader();
        reader.onload = function (e) {
          NevinLogger.info("Image loaded");
          resolve(new NevinImage(e.target.result, NevinPalette));
        };
        if (input.files && input.files[0]) {
          reader.readAsDataURL(input.files[0]);
        }
      });
    });
  }
  static addClipboardListener(NevinPalette, callback) {
    document.addEventListener("paste", function (paste_e) {
      var items = (paste_e.clipboardData || paste_e.originalEvent.clipboardData)
        .items;
      NevinLogger.info(
        "Recieved data from clipboard: " + JSON.stringify(items)
      );
      var blob = null;
      for (var i = 0; i < items.length; i++) {
        if (items[i].type.indexOf("image") === 0) {
          blob = items[i].getAsFile();
        }
      }
      if (blob !== null) {
        var reader = new FileReader();
        reader.onload = function (e) {
          NevinLogger.info("Readed image from clipboard!");
          callback(new NevinImage(e.target.result, NevinPalette));
        };
        reader.readAsDataURL(blob);
      }
    });
  }
}
 
function NevinWaitForElm(selector) {
  return new Promise((resolve) => {
    if (document.querySelector(selector)) {
      return resolve(document.querySelector(selector));
    }
 
    const observer = new MutationObserver((mutations) => {
      if (document.querySelector(selector)) {
        resolve(document.querySelector(selector));
        observer.disconnect();
      }
    });
 
    observer.observe(document.body, {
      childList: true,
      subtree: true,
    });
  });
}
 
function NevinCreateWorker(code) {
  var blob = new Blob([code], { type: "text/javascript" });
 
  var url = URL.createObjectURL(blob);
 
  var worker = new Worker(url);
  return worker;
}
 
class NevinImage {
  constructor(x, palette) {
    this.NevinPalette = palette;
    this.image = undefined;
    this.image_canvas = document.createElement("canvas");
    this.image_context = this.image_canvas.getContext("2d");
    if (x instanceof Image) {
      this.image = x;
    } else if (typeof x == "string") {
      this.image = new Image();
      this.image.src = x;
    }
    if (this.image == undefined) {
      NevinLogger.error("Argument is neither type of Image nor a string");
    }
    this.image_context.mozImageSmoothingEnabled = false;
    this.image.onload = () => {
      this.image_canvas.width = this.image.width;
      this.image_canvas.height = this.image.height;
      this.image_context.drawImage(this.image, 0, 0);
      this.image_data = this.image_context.getImageData(
        0,
        0,
        this.image_canvas.width,
        this.image_canvas.height
      );
      NevinLogger.info('Dithering loaded image!');
      this.image_data = NevinImageConverter.floydSteinberg(this.image_data, this.image.width, this.image.height, palette);
    };
  }
  convertToTasks(sx, sy, nevinWS) {
    if (typeof sx != "number" || typeof sy != "number") {
      NevinLogger.error(
        "Tried to convert an image to tasks yet the starting coordinates are not a number."
      );
    }
    if (!(nevinWS instanceof NevinWS)) {
      NevinLogger.error(
        "NevinImage.convertToTasks requires an NevinWS in new versions. Please update your code."
      );
    }
    var _tasks = [];
    for (let i = 0; i < this.image_data.data.length; i += 4) {
      var [r, g, b, a] = this.image_data.data.slice(i, i + 4);
      if (a == 0) {
        continue;
      }
      var x = (i / 4) % this.image_data.width;
      var y = Math.floor(i / 4 / this.image_data.width);
      var colorIndex = this.NevinPalette.getIndex([r, g, b]);
      const c_color = nevinWS.nevinMapCache.getPixel(sx + x, sy + y);
      if (colorIndex == -1) {
        console.log([r, g, b]);
      }
      if (c_color == colorIndex || c_color == -1 || colorIndex == -1) {
        continue;
      }
      _tasks.push([sx + x, sy + y, colorIndex]);
    }
    return _tasks;
  }
}
 
class NevinEngine {
  static convertToTask(x, y, colorIndex, packetType) {
    if (packetType == undefined) {
      packetType = 1;
    }
    return `42["p",${JSON.stringify([x, y, colorIndex, packetType])}]`;
  }
  putPixel(x, y, colorIndex) {
    this.tasks.push([x, y, colorIndex]);
  }
  putPixelWithPriority(x, y, colorIndex) {
    this.tasks.unshift([x, y, colorIndex]);
  }
  constructor(NevinWS, timeout) {
    if (!NevinWS || !timeout) {
      return;
    }
    this.tasks = [];
    this.NevinWS = NevinWS;
    this.intervalID = setInterval(() => {
      const task = this.tasks.shift();
      if (!task) {
        return;
      }
      if (this.NevinWS.nevinMapCache.getPixel(task[0], task[1]) == task[2]) {
        return;
      }
      this.NevinWS.ws.send(NevinEngine.convertToTask(...task));
    }, timeout);
  }
}
 
class NevinEngineMultiBot extends NevinEngine {
  static getAccountDetailsFromCookie(cookie) {
    const dict = cookie
      .split("; ")
      .map((a) => a.split("="))
      .reduce(function (b, a) {
        if (!["authKey", "authToken", "authId"].includes(a[0])) return b;
        b[a[0]] = a[1];
        return b;
      }, {});
    return [dict.authId, dict.authToken, dict.authKey];
  }
  addAccountFromCookies(authId, authToken, authKey) {
    if (!authId || !authToken || !authKey) {
      NevinLogger.warning(
        "Auth informations are not defined. (Maybe not logged in?)"
      );
      return;
    }
    const boardId = parseInt(location.pathname.replace("/", "").split("-")[0]);
    const socket = new NevinOriginalWebSocket(
      "wss://pixelplace.io/socket.io/?EIO=3&transport=websocket"
    );
    socket.headless = true;
    socket.onmessage = ({ data }) => {
      const [code, msg] = data.split(/(?<=^\d+)(?=[^\d])/);
      if (code == "40") {
        socket.send(
          "42" +
            JSON.stringify(["init", { authKey, authToken, authId, boardId }])
        );
      }
 
      const message = JSON.parse(msg || "[]");
      if (message.pingInterval)
        socket.ping = setInterval(() => socket.send("2"), message.pingInterval);
 
      if (!message.length) return arguments;
      const [event, json] = message;
      if (event == "throw.error") {
        socket.close();
        NevinLogger.error(json);
      }
    };
    socket.onclose = () => {
      NevinLogger.info("User Disconnected");
    };
    const nevinWS = new NevinWS(undefined, socket);
    this.sockets.push(nevinWS);
  }
  addAccountFromNevinWS(nevinWS) {
    this.sockets.push(nevinWS);
  }
  constructor(timeout, nevinPalette) {
    super();
    this.tasks = [];
    this.sockets = [];
    this.counter = 0;
    function interval() {
      if (this.sockets.length == 0) {
        setTimeout(interval, 100);
        return;
      }
      const task = this.tasks.shift();
      if (!task) {
        setTimeout(interval, timeout / this.sockets.length);
        return;
      }
      console.log(this);
      this.counter = (this.counter + 1) % this.sockets.length;
      this.sockets[this.counter]?.ws?.send(NevinEngine.convertToTask(...task));
      setTimeout(this.interval, timeout / this.sockets.length);
    }
    interval = interval.bind(this);
    interval();
    this.interval = interval;
  }
}
class NevinProtect {
  constructor(core) {
    NevinLogger.info("NevinProtect has been opened.");
    this.core = core;
    this.nimage = undefined;
    this.coordinates = undefined;
    this.working = false;
    this.core.nevinWS.ws.addEventListener(
      "message",
      function (e) {
        if (!this.working) return;
        if (!this.nimage) return;
        if (!this.coordinates) return;
        var data = e.data;
        if (!data.startsWith('42["p",')) {
          return;
        }
        var packets = JSON.parse(data.replace("42", ""))[1];
        for (let packet of packets) {
          var [x, y, color] = packet;
          var image_width = this.nimage.image.width;
          var image_height = this.nimage.image.height;
          var image_x = this.coordinates[0];
          var image_y = this.coordinates[1];
          var image_xmax = image_width + image_x;
          var image_ymax = image_height + image_y;
          if (!this.nimage) {
            continue;
          }
          if (
            x < image_x ||
            x >= image_xmax ||
            y < image_y ||
            y >= image_ymax
          ) {
            continue;
          }
          var img_data_index = 4 * (x - image_x + image_width * (y - image_y));
          var [r, g, b, a] = this.nimage.image_data.data.slice(
            img_data_index,
            img_data_index + 4
          );
          if (a == 0) continue;
          var image_color_i = this.core.palette.getIndex([r, g, b]);
          if (image_color_i == undefined) {
            NevinLogger.error(
              JSON.stringify([[r, g, b], image_color_i, img_data_index])
            );
          }
          if (image_color_i != color) {
            this.core.engine.putPixelWithPriority(x, y, image_color_i);
          }
        }
      }.bind(this)
    );
  }
  start() {
    this.working = true;
  }
  stop() {
    this.working = false;
  }
  load(nimage, coordinates) {
    this.nimage = nimage;
    this.coordinates = coordinates;
  }
}
 
class NevinCore {
  async testAccountValidation() {
    const req = await fetch(
      "https://pixelplace.io/api/get-painting.php?id=7&connected=1"
    );
    const json = await req.json();
    if (json.user.name == "Guest") {
      NevinLogger.warning("User is not logged in!");
    } else {
      NevinLogger.info("Logged in as " + json.user.name);
    }
  }
  constructor(options) {
    this.testAccountValidation();
    this.palette = new NevinPalette(NevinPalette.PALETTE_LOAD_STATIC);
    //        this.accountManager = new NevinAccountManager();
    this.nevinWS = new NevinWS(this.palette); //, this.accountManager);
    if (options.multibot) {
      this.engine = new NevinEngineMultiBot(options.timeout);
      localStorage.nevinAccounts = localStorage.nevinAccounts || "[]";
      const nevinAccounts = JSON.parse(localStorage.nevinAccounts);
      unsafeWindow.addThisAccount = function () {
        const session_account = NevinEngineMultiBot.getAccountDetailsFromCookie(
          document.cookie
        ).join("_AND_");
        if (session_account[0] && !nevinAccounts.includes(session_account)) {
          nevinAccounts.push(session_account);
        }
        localStorage.nevinAccounts = JSON.stringify(nevinAccounts);
      };
      for (let account of nevinAccounts) {
        const [authId, authToken, authKey] = account.split("_AND_");
        if (!authId || !authToken || !authKey) {
          console.error(account);
          NevinLogger.error("Local account is corrupted");
        }
        this.engine.addAccountFromCookies(authId, authToken, authKey);
      }
    } else {
      this.engine = new NevinEngine(this.nevinWS, options.timeout);
    }
    this.picker = NevinImagePicker;
    this.logger = NevinLogger;
  }
}