- // ==UserScript==
- // @name Mxobot Library
- // @namespace http://tampermonkey.net/<3nevin
- // @version 1.5
- // @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.gf.qytechs.cn/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;
- }
- }