您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Library for Mxobot
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/461063/1370924/Library%20For%20Mxobot.js
// ==UserScript== // @name MXO LİBRARY // @namespace http://tampermonkey.net/<3nevin // @version 1.1 // @description LİBRARY FOR MXOBOT // @author ngixl // @match https://pixelplace.io/* // @icon https://www.google.com/s2/favicons?sz=64&domain=pixelplace.io // @grant unsafeWindow // @require http://greasyfork.icu/scripts/462620-mxobot-example-bot/code/MxoBot%20Example%20Bot.js /* globals unsafeWindow*/ /*jshint esversion: 11 */ Object.defineProperty(unsafeWindow, "console", { value: console, writable: false, }); class MMXOLoggerFactory { static TEMPLATE = "%c[MMXOCore] %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 = MMXOLoggerFactory.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 <= MMXOLoggerFactory.LEVEL_INFO) { this.dispatch( MMXOLoggerFactory.TEMPLATE, MMXOLoggerFactory.CSS_INFO, MMXOLoggerFactory.TEMPLATE_INFO, msg ); } } warning(msg) { if (this.LEVEL <= MMXOLoggerFactory.LEVEL_WARNING) { this.dispatch( MMXOLoggerFactory.TEMPLATE, MMXOLoggerFactory.CSS_WARNING, MMXOLoggerFactory.TEMPLATE_WARNING, msg ); } } error(msg) { if (this.LEVEL <= MMXOLoggerFactory.LEVEL_ERROR) { this.dispatch( MMXOLoggerFactory.TEMPLATE, MMXOLoggerFactory.CSS_ERROR, MMXOLoggerFactory.TEMPLATE_ERROR, msg ); throw Error(msg); } } } class MMXOImageConverter { 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.MXOBOT_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 = MMXOImageConverter.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 MMXOLogger = new MMXOLoggerFactory(); class MMXOPalette { 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 = MMXOPalette.PALETTE_LOAD_STATIC; MMXOLogger.warning( "MMXOPalette invoked without specifying the loading type." ); } MMXOLogger.info( "MMXOPalette loading with type: " + (type == MMXOPalette.PALETTE_LOAD_DYNAMIC ? "DYNAMIC" : "STATIC") ); if (type == MMXOPalette.PALETTE_LOAD_DYNAMIC) { const palette = document.getElementById("palette-buttons"); if (!palette) { MMXOLogger.error( "Palette requested to be loaded dynamically but HTML is not loaded yet." ); } this.colors = []; this.indexes = []; const palette_buttons = Array.from(palette.children); MMXOLogger.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(MMXOPalette.hexStrToHex(color.hex)); this.indexes.push(parseInt(color.index)); } } else { this.colors = MMXOPalette.STATIC_COLORS; this.indexes = MMXOPalette.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 { MMXOLogger.error("Argument is neither type of Array nor a number"); } } constructor(type) { this.colors = undefined; this.indexes = undefined; this.initalizePalette(type); } } class MMXOOriginalWebSocket extends WebSocket {} class MMXOWS { constructor(mmxoPalette, webSocket) { if (webSocket) { this.ws = webSocket; if (mmxoPalette) { this.mmxoMapCache = new MMXOMapCache(mmxoPalette, this.ws); this.mmxoMapCache.addPixelChangeListener(this); } } else { this.ws = undefined; var proxy = this; this.hook = class extends WebSocket { constructor(a, b) { super(a, b); MMXOLogger.info("MMXOWS has hooked the game WebSocket connection."); proxy.ws = this; proxy.mmxoMapCache.addPixelChangeListener(proxy); } }; if (typeof unsafeWindow !== undefined) { if (unsafeWindow.WebSocket != MMXOWS) { unsafeWindow.WebSocket = this.hook; } } this.mmxoMapCache = new MMXOMapCache(mmxoPalette, this); } } } var map_cache; class MMXOMapCache { init(mmxoPalette, mmxoWS) { 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 = () => { MMXOLogger.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 = mmxoPalette.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(mmxoPalette, mmxoWS) { this.init(mmxoPalette, mmxoWS); } getPixel(x, y) { var i = y * this.map_width + x; return this.cache[i]; } addPixelChangeListener(mmxoWS) { mmxoWS.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 MMXOImagePicker { static requestImageFromFileDialog(MMXOPalette) { 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) { MMXOLogger.info("Image loaded"); resolve(new MMXOImage(e.target.result, MMXOPalette)); }; if (input.files && input.files[0]) { reader.readAsDataURL(input.files[0]); } }); }); } static addClipboardListener(MMXOPalette, callback) { document.addEventListener("paste", function (paste_e) { var items = (paste_e.clipboardData || paste_e.originalEvent.clipboardData) .items; MMXOLogger.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) { MMXOLogger.info("Readed image from clipboard!"); callback(new MMXOImage(e.target.result, MMXOPalette)); }; reader.readAsDataURL(blob); } }); } } function MMXOWaitForElm(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 MMXOCreateWorker(code) { var blob = new Blob([code], { type: "text/javascript" }); var url = URL.createObjectURL(blob); var worker = new Worker(url); return worker; } class MMXOImage { constructor(x, palette) { this.MMXOPalette = 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) { MMXOLogger.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 ); MMXOLogger.info('Dithering loaded image!'); this.image_data = MMXOImageConverter.floydSteinberg(this.image_data, this.image.width, this.image.height, palette); }; } convertToTasks(sx, sy, mmxoWS) { if (typeof sx != "number" || typeof sy != "number") { MMXOLogger.error( "Tried to convert an image to tasks yet the starting coordinates are not a number." ); } if (!(mmxoWS instanceof MMXOWS)) { MMXOLogger.error( "MMXOImage.convertToTasks requires an MMXOWS 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.MMXOPalette.getIndex([r, g, b]); const c_color = mmxoWS.mmxoMapCache.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 MMXOEngine { 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(MMXOWS, timeout) { if (!MMXOWS || !timeout) { return; } this.tasks = []; this.MMXOWS = MMXOWS; this.intervalID = setInterval(() => { const task = this.tasks.shift(); if (!task) { return; } if (this.MMXOWS.mmxoMapCache.getPixel(task[0], task[1]) == task[2]) { return; } this.MMXOWS.ws.send(MMXOEngine.convertToTask(...task)); }, timeout); } } class MMXOEngineMultiBot extends MMXOEngine { 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) { MMXOLogger.warning( "Auth informations are not defined. (Maybe not logged in?)" ); return; } const boardId = parseInt(location.pathname.replace("/", "").split("-")[0]); const socket = new MMXOOriginalWebSocket( "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(); MMXOLogger.error(json); } }; socket.onclose = () => { MMXOLogger.info("User Disconnected"); }; const mmxoWS = new MMXOWS(undefined, socket); this.sockets.push(mmxoWS); } addAccountFromMMXOWS(mmxoWS) { this.sockets.push(mmxoWS); } constructor(timeout, mmxoPalette) { 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(MMXOEngine.convertToTask(...task)); setTimeout(this.interval, timeout / this.sockets.length); } interval = interval.bind(this); interval(); this.interval = interval; } } class MMXOProtect { constructor(core) { MMXOLogger.info("MMXOProtect has been opened."); this.core = core; this.nimage = undefined; this.coordinates = undefined; this.working = false; this.core.mmxoWS.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) { MMXOLogger.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 MMXOCore { 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") { MMXOLogger.warning("User is not logged in!"); } else { MMXOLogger.info("Logged in as " + json.user.name); } } constructor(options) { this.testAccountValidation(); this.palette = new MMXOPalette(MMXOPalette.PALETTE_LOAD_STATIC); // this.accountManager = new MMXOAccountManager(); this.mmxoWS = new MMXOWS(this.palette); //, this.accountManager); if (options.multibot) { this.engine = new MMXOEngineMultiBot(options.timeout); localStorage.mmxoAccounts = localStorage.mmxoAccounts || "[]"; const mmxoAccounts = JSON.parse(localStorage.mmxoAccounts); unsafeWindow.addThisAccount = function () { const session_account = MMXOEngineMultiBot.getAccountDetailsFromCookie( document.cookie ).join("_AND_"); if (session_account[0] && !mmxoAccounts.includes(session_account)) { mmxoAccounts.push(session_account); } localStorage.mmxoAccounts = JSON.stringify(mmxoAccounts); }; for (let account of mmxoAccounts) { const [authId, authToken, authKey] = account.split("_AND_"); if (!authId || !authToken || !authKey) { console.error(account); MMXOLogger.error("Local account is corrupted"); } this.engine.addAccountFromCookies(authId, authToken, authKey); } } else { this.engine = new MMXOEngine(this.mmxoWS, options.timeout); } this.picker = MMXOImagePicker; this.logger = MMXOLogger; } }