// ==UserScript==
// @name Cubic Engine
// @version 1.3
// @description Enhance your Experience
// @namespace drawaria.modded.fullspec
// @homepage https://drawaria.online/profile/?uid=63196790-c7da-11ec-8266-c399f90709b7
// @author ≺ᴄᴜʙᴇ³≻
// @match https://drawaria.online/
// @match https://drawaria.online/room/*
// @icon https://drawaria.online/avatar/cache/e53693c0-18b1-11ec-b633-b7649fa52d3f.jpg
// @grant none
// @license GNU GPLv3
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
const EL = (sel) => document.querySelector(sel);
const ELL = (sel) => document.querySelectorAll(sel);
const settings = (function enableSettingsContext(prefix) {
const namespace = prefix;
const settings = {
config: {},
save: function () {
localStorage.setItem(namespace, JSON.stringify(this.config));
},
load: function () {
this.config = JSON.parse(localStorage.getItem(namespace)) || {};
},
};
window.addEventListener('load', () => {
settings.load();
});
window.addEventListener(
'beforeunload',
() => {
settings.save();
},
false
);
return settings;
})('Engine');
let drawing_active = false;
let previewCanvas = document.createElement('canvas');
let originalCanvas = document.getElementById('canvas');
var data;
let cw = previewCanvas.width;
let ch = previewCanvas.height;
let executionLine = [];
window.myRoom = {};
window.sockets = [];
const originalSend = WebSocket.prototype.send;
WebSocket.prototype.send = function (...args) {
if (window.sockets.indexOf(this) === -1) {
window.sockets.push(this);
if (window.sockets.indexOf(this) === 0) {
this.addEventListener('message', (event) => {
// console.debug(event)
let message = String(event.data);
if (message.startsWith('42')) {
let payload = JSON.parse(message.slice(2));
if (payload[0] == 'bc_uc_freedrawsession_changedroom') {
window.myRoom.players = payload[3];
}
if (payload[0] == 'mc_roomplayerschange') {
window.myRoom.players = payload[3];
}
} else if (message.startsWith('41')) {
// this.send(40)
} else if (message.startsWith('430')) {
let configs = JSON.parse(message.slice(3))[0];
window.myRoom.players = configs.players;
window.myRoom.id = configs.roomid;
}
});
}
}
return originalSend.call(this, ...args);
};
function addBoxIcons() {
let boxicons = document.createElement('link');
boxicons.href = 'https://unpkg.com/[email protected]/css/boxicons.min.css';
boxicons.rel = 'stylesheet';
document.head.appendChild(boxicons);
}
function CreateStylesheet() {
let container = document.createElement('style');
container.innerHTML =
'input[type="number"] { text-align: center; -webkit-appearance: none; -moz-appearance: textfield; } ' +
'.hidden { display: none; } ' +
'.cheat-row { display:flex; width:100%; } ' +
'.cheat-row > * { width:100%; } ' +
'.cheat-border { width: 100%; text-align: center; line-height: inherit; margin: 1px; border: 1px solid coral; } ';
document.head.appendChild(container);
}
function Engine() {
let CheatContainer = document.createElement('div');
CheatContainer.id = 'Engine-Cheatcontainer';
CheatContainer.classList.toggle('hidden');
document.getElementById('chatbox_messages').after(CheatContainer);
function CreateToggleButton(Cheatcontainer) {
let target = document.getElementById('chatbox_textinput');
let btncontainer = document.createElement('div');
btncontainer.id = 'togglecheats';
btncontainer.className = 'input-group-append';
let togglebtn;
togglebtn = document.createElement('button');
togglebtn.className = 'btn btn-outline-secondary';
togglebtn.innerHTML = '<i class="bx bx-code-alt"></i>';
togglebtn.addEventListener('click', (e) => {
e.preventDefault();
togglebtn.classList.toggle('active');
togglebtn.classList.contains('active')
? Cheatcontainer.classList.remove('hidden')
: Cheatcontainer.classList.add('hidden');
});
btncontainer.appendChild(togglebtn);
target.after(btncontainer);
}
CreateToggleButton(CheatContainer);
function ImageLoader(CheatContainer) {
let container = document.createElement('div');
let row = document.createElement('div');
row.className = 'cheat-row';
let IPutImage = document.createElement('input');
IPutImage.type = 'file';
IPutImage.id = 'IPutImage';
IPutImage.className = 'cheat-border';
function readImage() {
if (!this.files || !this.files[0]) return;
const FR = new FileReader();
FR.addEventListener('load', (evt) => {
// get base64 string of image and apply to image src
let base64 = evt.target.result;
// load the image
loadImage(base64);
});
// read Image
FR.readAsDataURL(this.files[0]);
}
IPutImage.addEventListener('change', readImage);
row.appendChild(IPutImage);
container.appendChild(row);
CheatContainer.appendChild(container);
}
ImageLoader(CheatContainer);
function BotControl(CheatContainer) {
let container = document.createElement('div');
container.innerHTML =
"<div class='cheat-row'><i class='bx bx-user-plus cheat-border' id='botJoin'><span>Join</span></i><i class='bx bx-user-minus cheat-border' id='botLeave'><span>Leave</span></i><i class='bx bxs-eraser cheat-border' id='canvasClear'><span>Clear</span></i></div>";
CheatContainer.appendChild(container);
document.getElementById('botJoin').addEventListener('mousedown', (e) => {
window['___BOT'].room.join(EL('#invurl').value);
});
document.getElementById('botLeave').addEventListener('mousedown', (e) => {
window['___BOT'].conn.socket.close();
});
document.getElementById('canvasClear').addEventListener('mousedown', (e) => {
window['___BOT'].action.DrawLine(50, 50, 50, 50, 2000);
});
}
BotControl(CheatContainer);
function DrawingControls(CheatContainer) {
let container = document.createElement('div');
container.innerHTML = [
'<div class="cheat-row"><input type="number" id="engine_imagesize" min="1" max="10" value="4" title="Image Size. 1 = big. 10 = small"><input type="number" id="engine_brushsize" min="2" max="20" value="4" title="Your Brush Size"><input type="number" id="engine_pixelsize" min="2" max="20" value="2" title="Distance between Pixels\nBest use half of brushsize"><input type="number" id="engine_offset_x" min="0" max="100" value="0" title="Distance left"><input type="number" id="engine_offset_y" min="0" max="100" value="0" title="Distance top"></div>',
'<div class="cheat-row"><i class="bx bx-play-circle cheat-border" id="botStartDrawing"><span>Start</span></i><i class="bx bx-stop-circle cheat-border" id="botStopDrawing"><span>Stop</span></i></div>',
].join('');
CheatContainer.appendChild(container);
document.getElementById('botStopDrawing').addEventListener('mousedown', (e) => {
drawing_active = false;
});
document.getElementById('botStartDrawing').addEventListener('mousedown', (e) => {
let size = document.getElementById('engine_imagesize').value;
let modifier = document.getElementById('engine_pixelsize').value;
let thickness = document.getElementById('engine_brushsize').value;
let offset = {
x: document.getElementById('engine_offset_x').value,
y: document.getElementById('engine_offset_y').value,
};
drawImage(size, modifier, thickness, offset);
execute(window['___BOT'].conn.socket);
});
}
DrawingControls(CheatContainer);
}
function loadImage(url) {
// load the image
var img = new Image();
img.addEventListener('load', () => {
previewCanvas.width = originalCanvas.width;
previewCanvas.height = originalCanvas.height;
cw = previewCanvas.width;
ch = previewCanvas.height;
var ctx = previewCanvas.getContext('2d');
// center and resize image
let modifier = 1;
if (img.width > previewCanvas.width) {
modifier = previewCanvas.width / img.width;
} else {
modifier = previewCanvas.height / img.height;
}
// draw the image
// (this time to grab the image's pixel data
ctx.drawImage(img, 0, 0, img.width * modifier, img.height * modifier);
// grab the image's pixel data
var imgData = ctx.getImageData(0, 0, previewCanvas.width, previewCanvas.height);
data = imgData.data;
// clear the canvas to draw the glow
ctx.clearRect(0, 0, previewCanvas.width, previewCanvas.height);
console.debug('ready');
});
img.crossOrigin = 'anonymous';
img.src = url;
}
function drawImage(size, modifier = 1, thickness = 5, offset = { x: 0, y: 0 }, ignorcolors = []) {
executionLine = [];
for (let y = 0; y < ch; y += size * modifier) {
let start = [0, y];
for (let x = 0; x < ch; x += size * modifier) {
let end = [x, y];
let index = (y * cw + x) * 4;
let a = data[index + 3];
if (a > 20) {
end = [x, y];
// Is not Transparent
let r = data[index + 0],
g = data[index + 1],
b = data[index + 2];
let color = `rgb(${r},${g},${b})`;
if (!ignorcolors.includes(color)) {
if (x < cw - 1) {
let n_r = data[index + size * modifier * 4 + 4],
n_g = data[index + size * modifier * 4 + 5],
n_b = data[index + size * modifier * 4 + 6];
let samecolor = true;
// check if the next pixel is same color as the last
if ((r != n_r && g != n_g && b != n_b) || data[index + 7] < 20) {
samecolor = false;
}
if (!samecolor) {
executionLine.push({
pos1: recalc(start, size, offset),
pos2: recalc(end, size, offset),
color: color,
thickness: thickness,
});
start = [x, y];
}
} else {
executionLine.push({
pos1: recalc(start, size, offset),
pos2: recalc(end, size, offset),
color: color,
thickness: thickness,
});
}
}
} else {
// Is Transparent
start = [x, y];
}
}
}
console.debug('done Loading');
}
async function execute(socket) {
drawing_active = true;
for (let i = 0; i < executionLine.length; i++) {
if (!drawing_active) return;
let currentLine = executionLine[i];
let p1 = currentLine.pos1,
p2 = currentLine.pos2,
color = currentLine.color,
thickness = currentLine.thickness;
drawcmd(socket, p1, p2, color, thickness);
await delay(10);
}
function drawcmd(s, start, end, color, thickness) {
s.send(`42["drawcmd",0,[${start[0]},${start[1]},${end[0]},${end[1]},false,${0 - thickness},"${color}",0,0,{}]]`);
}
}
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function recalc(value, size, offset) {
return [(value[0] / (cw * size) + offset.x / 100).toFixed(4), (value[1] / (ch * size) + offset.y / 100).toFixed(4)];
}
function nullify(value = null) {
return value == null ? null : String().concat('"', value, '"');
}
class Player {
constructor(name = undefined) {
this.name = name;
this.sid1 = null;
this.uid = '';
this.wt = '';
this.conn = new Connection(this);
this.room = new Room(this.conn);
this.action = new Actions(this.conn);
}
annonymize(name) {
this.name = name;
this.uid = undefined;
this.wt = undefined;
}
}
class Connection {
constructor(player) {
this.player = player;
}
onopen(event) {
// console.debug(event)
this.Heartbeat(25000);
}
onclose(event) {
// console.debug(event)
}
onerror(event) {
// console.debug(event)
}
onmessage(event) {
// console.debug(event)
let message = String(event.data);
if (message.startsWith('42')) {
this.onbroadcast(message.slice(2));
} else if (message.startsWith('40')) {
this.onrequest();
} else if (message.startsWith('41')) {
this.player.room.join(this.player.room.id);
} else if (message.startsWith('430')) {
let configs = JSON.parse(message.slice(3))[0];
this.player.room.players = configs.players;
this.player.room.id = configs.roomid;
}
}
onbroadcast(payload) {
payload = JSON.parse(payload);
if (payload[0] == 'bc_uc_freedrawsession_changedroom') {
this.player.room.players = payload[3];
this.player.room.id = payload[4];
}
if (payload[0] == 'mc_roomplayerschange') {
this.player.room.players = payload[3];
}
}
onrequest() {}
open(url) {
this.socket = new WebSocket(url);
this.socket.onopen = this.onopen.bind(this);
this.socket.onclose = this.onclose.bind(this);
this.socket.onerror = this.onerror.bind(this);
this.socket.onmessage = this.onmessage.bind(this);
}
close(code, reason) {
this.socket.close(code, reason);
}
Heartbeat(interval) {
let timeout = setTimeout(() => {
if (this.socket.readyState == this.socket.OPEN) {
this.socket.send(2);
this.Heartbeat(interval);
}
}, interval);
}
serverconnect(server, room) {
if (this.socket == undefined || this.socket.readyState != this.socket.OPEN) {
this.open(server);
} else {
this.socket.send(41);
this.socket.send(40);
}
this.onrequest = () => {
this.socket.send(room);
};
}
}
class Room {
constructor(conn) {
this.conn = conn;
this.id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
this.players = [];
}
join(invitelink) {
let gamemode = 2;
let server = '';
if (invitelink == null) {
this.id = null;
server = 'sv3.';
} else {
// extract roomid
this.id = invitelink.startsWith('http') ? invitelink.split('/').pop() : invitelink;
// craft serverurl
if (invitelink.endsWith('.3')) {
server = 'sv3.';
gamemode = 2;
} else if (invitelink.endsWith('.2')) {
server = 'sv2.';
gamemode = 2;
} else {
server = '';
gamemode = 1;
}
}
let serverurl = `wss://${server}drawaria.online/socket.io/?sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`;
let player = this.conn.player;
// craft response for joining of desired room
let connectstring = `420["startplay","${player.name}",${gamemode},"en",${nullify(
this.id
)},null,[null,"https://drawaria.online/",1000,1000,[${nullify(player.sid1)},${nullify(player.uid)},${nullify(
player.wt
)}],null]]`;
this.conn.serverconnect(serverurl, connectstring);
}
next() {
if (this.conn.socket.readyState != this.conn.socket.OPEN) {
this.join(null);
} else {
this.conn.socket.send('42["pgswtichroom"]');
}
}
}
class Actions {
constructor(conn) {
this.conn = conn;
}
SendMessage(message) {
this.conn.socket.send(`42["chatmsg","${message}"]`);
}
SendGesture(gestureid) {
this.conn.socket.send(`42["sendgesture",${gestureid}]`);
}
SendToken(playerid, tokenid) {
this.conn.socket.send(`42["clientcmd",2,[${playerid},${tokenid}]]`);
}
SendToggleAllow(playerid) {
this.conn.socket.send(`42["pgdrawvote",${playerid},0]`);
}
SendVote() {
this.conn.socket.send('42["sendvote"]');
}
SetStatus(statusid, value) {
this.conn.socket.send(`42["clientcmd",3,[${statusid},${value}]]`);
}
AvatarSpawn() {
this.conn.socket.send('42["clientcmd",101]');
}
AvatarMove(x, y) {
this.conn.socket.send(`42["clientcmd",103,[${String(x * 100) + String(y * 100).padStart(4, '0')},false]]`);
}
AvatarChange() {
this.conn.socket.send('42["clientcmd",115]');
}
DrawLine(bx = 50, by = 50, ex = 50, ey = 50, thickness = 50, color = '#FFFFFF', algo = 0) {
bx = bx / 100;
by = by / 100;
ex = ex / 100;
ey = ey / 100;
/* algo = 101/201 */
this.conn.socket.send(
`42["drawcmd",0,[${bx},${by},${ex},${ey},true,${0 - thickness},"${color}",0,0,{"2":${algo},"3":0.5,"4":0.5}]]`
);
this.conn.socket.send(
`42["drawcmd",0,[${bx},${by},${ex},${ey},false,${0 - thickness},"${color}",0,0,{"2":${algo},"3":0.5,"4":0.5}]]`
);
}
}
if (!document.getElementById('Engine-Cheatcontainer')) {
window['___BOT'] = new Player('Your Best and Only Friend');
window['___ENGINE'] = { loadImage, drawImage };
addBoxIcons();
CreateStylesheet();
Engine();
}
})();