// ==UserScript==
// @name Cubic Engine
// @version 3.5.7
// @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==
window.cube = (function (icon, name) {
'use strict';
/**
* Utility :
* CodeMaid is a collection of utility functions
* it shall help with a variaty of actions
* creating HTML nodes with attributes and children
* validate variables
* clean html
* cookie management
*/
const CodeMaid = (function loadCodeMaiden() {
const CodeMaiden = {
createDOM: {
Element: function () {
return document.createElement.apply(document, arguments);
},
TextNode: function () {
return document.createTextNode.apply(document, arguments);
},
Tree: function (type, attrs, childrenArrayOrVarArgs) {
const el = this.Element(type);
let children;
if (CodeMaiden.validate.isArray(childrenArrayOrVarArgs)) {
children = childrenArrayOrVarArgs;
} else {
children = [];
for (let i = 2; i < arguments.length; i++) {
children.push(arguments[i]);
}
}
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (typeof child === 'string') {
el.appendChild(this.TextNode(child));
} else {
if (child) {
el.append(child);
}
}
}
for (const attr in attrs) {
if (attr == 'className') {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
el.appendAll = function (...nodes) {
nodes.forEach((node) => {
el.append(node);
});
};
return el;
},
fromJSON: function (JSONDOM = { element: '', attributes: {}, children: [] }) {
if (CodeMaiden.validate.isString(JSONDOM)) return CodeMaiden.createDOM.TextNode(JSONDOM);
let dom = CodeMaiden.createDOM.Tree(JSONDOM.element, JSONDOM.attributes);
JSONDOM.children.forEach((child) => {
dom.append(this.fromJSON(child));
});
return dom;
},
Button: function (content) {
let btn = this.Tree('button');
btn.className = 'btn btn-outline-secondary';
btn.innerHTML = content;
return btn;
},
FA: function (fontawesome_icon) {
let m = fontawesome_icon.match(/\".*\"/g);
return this.Tree('i', { class: m[0].slice(1, m[0].length - 1) });
},
Row: function () {
return this.Tree('div', { class: 'ce_row' });
},
RowList: function () {
return this.Tree('div', { class: 'icon-list' });
},
},
validate: {
isArray: function (value) {
return this.isA('Array', value);
},
isObject: function (value) {
return !this.isUndefined(value) && value !== null && this.isA('Object', value);
},
isString: function (value) {
return this.isA('String', value);
},
isNumber: function (value) {
return this.isA('Number', value);
},
isFunction: function (value) {
return this.isA('Function', value);
},
isAsyncFunction: function (value) {
return this.isA('AsyncFunction', value);
},
isGeneratorFunction: function (value) {
return this.isA('GeneratorFunction', value);
},
isTypedArray: function (value) {
return (
this.isA('Float32Array', value) ||
this.isA('Float64Array', value) ||
this.isA('Int16Array', value) ||
this.isA('Int32Array', value) ||
this.isA('Int8Array', value) ||
this.isA('Uint16Array', value) ||
this.isA('Uint32Array', value) ||
this.isA('Uint8Array', value) ||
this.isA('Uint8ClampedArray', value)
);
},
isA: function (typeName, value) {
return this.getType(value) === '[object ' + typeName + ']';
},
isError: function (value) {
if (!value) {
return false;
}
if (value instanceof Error) {
return true;
}
return typeof value.stack === 'string' && typeof value.message === 'string';
},
isUndefined: function (obj) {
return obj === void 0;
},
getType: function (value) {
return Object.prototype.toString.apply(value);
},
},
cookies: {
set: function (name, value = '') {
document.cookie = name + '=' + value + '; expires=Thu, 2 Aug 2001 20:47:11 UTC; path=/';
},
get: function (name) {
var nameEQ = name + '=';
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
},
clear: function (name) {
document.cookie = name + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
},
obfuscate: function () {
window.Cookies = {
get: function () {
console.log(arguments);
return 'lol no';
},
};
},
},
cleanup: {
scripts: function () {
try {
let array = document.querySelectorAll('script[src]');
array.forEach((script) => {
if (script.src != '') document.head.append(script);
});
} catch (error) {
console.error(error);
}
try {
let unifiedScript = CodeMaiden.createDOM.Tree('script');
let scripts = document.querySelectorAll('script:not([src])');
let unifiedScriptContent = '';
scripts.forEach((script) => {
let content = script.textContent; //.replaceAll(/\s/g, '');
unifiedScriptContent += `try{${content}}catch(e){console.warn(e);}`;
script.remove();
});
unifiedScript.textContent = unifiedScriptContent;
document.head.append(unifiedScript);
} catch (error) {
console.error(error);
}
},
iframes: function () {
try {
let array = document.querySelectorAll('iframe');
array.forEach((iframe) => {
iframe.remove();
});
} catch (error) {
console.error(error);
}
},
styles: function () {
try {
let unifiedStyles = CodeMaiden.createDOM.Tree('style');
unifiedStyles.textContet = '';
let styles = document.querySelectorAll('style');
styles.forEach((style) => {
unifiedStyles.textContent += style.textContent;
style.remove();
});
document.head.append(unifiedStyles);
} catch (error) {
console.error(error);
}
},
cursors: function () {
try {
let cursors = document.querySelectorAll('.brushcursor');
cursors.forEach((cursor) => {
cursor.remove();
});
} catch (error) {
console.error(error);
}
},
all: function () {
this.iframes();
this.styles();
this.scripts();
this.cursors();
},
},
generate: {
uuidv4: function () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c == 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
},
},
};
return CodeMaiden;
})();
/**
* Utility :
* help with keeping track of settings and more
* Saves your settings in your local storage
* may be used in future updates
*/
const settings = (function enableSettingsContext(prefix) {
const namespace = prefix;
const settings = {
config: { allowedToUpload: true, reports: 0, bannedUntil: 0 },
save: function () {
settings.config.allowedToUpload = settings.config.allowedToUpload ?? true;
settings.config.reports = settings.config.reports ?? 0;
settings.config.bannedUntil = settings.config.bannedUntil ?? 0;
localStorage.setItem(namespace, JSON.stringify(settings.config));
},
load: function () {
settings.config = JSON.parse(localStorage.getItem(namespace)) || settings.config;
if (settings.config.bannedUntil < Date.now()) {
settings.config.allowedToUpload = true;
settings.config.reports = 0;
}
},
};
window.addEventListener('load', () => {
settings.load();
});
window.addEventListener(
'beforeunload',
() => {
settings.save();
},
false
);
window['0000'] = settings;
return settings;
})('Engine');
/**
* Utility :
* contains chromajs and custom css styles
*/
(function loadExternals() {
let ChromaJS = CodeMaid.createDOM.Tree('script', {
src: 'https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.4.2/chroma.min.js',
});
let myStyleSheet = CodeMaid.createDOM.Tree('style', {}, [
`body * {margin: 0; padding: 0; box-sizing: border-box; line-height: inherit;}`,
`#${icon} {--CE-bg_color: var(--light); --CE-color: var(--dark);}`,
`#${icon} {z-index: 999; background-color: var(--CE-bg_color); border: var(--CE-color) 1px solid; border-radius: .25rem;}`,
`#${icon} .icon-list {display: flex; flex-flow: wrap;}`,
`#${icon} .nowrap {overflow-x: scroll; padding-bottom: 12px; flex-flow: nowrap;}`,
`#${icon} .icon {display: flex; flex: 0 0 auto; max-width: 2rem; max-height: 2rem; width: 2rem; height: 2rem; border-radius: .25rem; border: 1px solid var(--gray);}`,
`#${icon} .icon > * {margin: auto; text-align: center; max-height: 100%; max-width: 100%;}`,
`#${icon} .ce_row {display: flex; width: 100%;}`,
`#${icon} .ce_row > * {width: 100%;}`,
`#${icon} .btn {padding: 0;}`,
`#${icon} .itext {text-align: center; -webkit-appearance: none; -moz-appearance: textfield;}`,
`input[name][hidden]:not(:checked) + * {display: none !important;}`,
`hr {margin: 5px 0;}`,
`.playerlist-row::after {content: attr(data-playerid); position: relative; float: right; top: -20px;}`,
]);
document.head.append(myStyleSheet);
document.head.append(ChromaJS);
})();
/**
* Utility :
* Easily upload an Image for your Avatar
*/
(function addAvatarUploader() {
function uploadToAvatar(img) {
fetch(window.LOGGEDIN ? 'https://drawaria.online/saveavatar' : 'https://drawaria.online/uploadavatarimage', {
method: 'POST',
body: 'imagedata=' + encodeURIComponent(img) + '&fromeditor=true',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
Accept: 'text/plain, */*; q=0.01',
'X-Requested-With': 'XMLHttpRequest',
},
}).then((e) => alert(e.statusText === 'OK' ? 'Avatar SUCCESSFULLY uploaded' : e.statusText));
}
function avatarUploaderVisual() {
document.querySelectorAll('label[for="avataruploader"]').forEach((e) => e.remove());
let input = document.createElement('input');
input.style.display = 'none';
input.id = 'avataruploader';
input.type = 'file';
input.addEventListener('change', onchange);
let label = document.createElement('label');
label.style = 'display:flex; text-align: left;';
label.className = 'badge border btn-outline-primary border-primary';
label.innerHTML =
'<img class="playerlist-avatar" src="https://media.tenor.com/pOv7SnZx7xAAAAAC/upload-cat.gif"><div class="playerlist-name"><span>Upload to Avatar</span><br/><sub>by ≺ᴄᴜʙᴇ³≻</sub></div>';
label.setAttribute('for', input.id);
label.append(input);
function onchange() {
if (!this.files || !this.files[0]) return;
let e = new FileReader();
e.addEventListener('load', (e) => {
let a = e.target.result.replace('image/gif', 'image/png');
uploadToAvatar(a);
});
e.readAsDataURL(this.files[0]);
}
document.querySelector('#playerlist').before(label);
}
return avatarUploaderVisual;
})()();
/**
* Utility :
* Apply color to your logs
* @param {string} level the color you want
* @returns hex color
*/
function logLevel(level) {
let error = [5, 'err', 'error', 'danger', 'red'];
let warning = [4, 'warn', 'warning', 'yellow'];
let info = [3, 'help', 'info', 'blue'];
let success = [2, 'ok', 'success', 'green'];
let log = [0, 'log', 'normal', 'mute'];
let debug = [1, 'debug', 'trace', 'purple'];
if (error.includes(level)) {
return '#dc3545';
}
if (warning.includes(level)) {
return '#ffc107';
}
if (info.includes(level)) {
return '#17a2b8';
}
if (success.includes(level)) {
return '#28a745';
}
if (log.includes(level)) {
return '#6c757d';
}
if (debug.includes(level)) {
}
return '#000000';
}
/**
* Utility :
* generate websocket connection url
* @param {string} inviteLink to extract the server type ".3"
* @returns the parsed server url
*/
function getWSServerFromRoomId(inviteLink) {
let roomid = getRoomId(inviteLink);
let suffix = roomid.split('.')[1] || '';
let prefix = suffix == 3 ? 'sv3.' : suffix == 2 ? 'sv2.' : suffix;
return `wss://${prefix}drawaria.online/socket.io/?sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`;
}
/**
* Utility :
* get your room id without server type
* @param {string} inviteLink to extract the room
* @returns room uuid
*/
function getRoomId(inviteLink) {
let roomid = inviteLink?.split('/').pop() || '.3';
return roomid;
}
/**
* Utility :
* generate a valid user avatar url
* @param {array} avatar [ uid, wt ]
* @returns
*/
function getAvatarCacheURL(avatar = ['', '']) {
if (avatar[0] == '' && avatar[1] == '') return `https://drawaria.online/avatar/cache/default.jpg`;
return `https://drawaria.online/avatar/cache/${avatar.join('.')}.jpg`;
}
/**
* Utility :
* easily access the different socket events
*/
const emits = {
chatmsg: function (message) {
// 42["chatmsg","a"]
let data = ['chatmsg', message];
return `${42}${JSON.stringify(data)}`;
},
passturn: function () {
// 42["passturn"]
let data = ['passturn'];
return `${42}${JSON.stringify(data)}`;
},
pgdrawvote: function (playerid) {
// 42["pgdrawvote",2,0]
let data = ['pgdrawvote', playerid, 0];
return `${42}${JSON.stringify(data)}`;
},
pgswtichroom: function () {
// 42["pgswtichroom"]
let data = ['pgswtichroom'];
return `${42}${JSON.stringify(data)}`;
},
playerafk: function () {
// 42["playerafk"]
let data = ['playerafk'];
return `${42}${JSON.stringify(data)}`;
},
playerrated: function () {
// 42["playerrated"]
let data = ['playerrated'];
return `${42}${JSON.stringify(data)}`;
},
sendgesture: function (gestureid) {
// 42["sendgesture",16]
let data = ['sendgesture', gestureid];
return `${42}${JSON.stringify(data)}`;
},
sendvote: function () {
// 42["sendvote"]
let data = ['sendvote'];
return `${42}${JSON.stringify(data)}`;
},
sendvotekick: function (playerid) {
// 42["sendvotekick",93]
let data = ['sendvotekick', playerid];
return `${42}${JSON.stringify(data)}`;
},
wordselected: function (wordid) {
// 42["wordselected",0]
let data = ['sendvotekick', wordid];
return `${42}${JSON.stringify(data)}`;
},
activateitem: function (itemid, isactive) {
let data = ['clientcmd', 12, [itemid, isactive]];
return `${42}${JSON.stringify(data)}`;
},
buyitem: function (itemid) {
let data = ['clientcmd', 11, [itemid]];
return `${42}${JSON.stringify(data)}`;
},
canvasobj_changeattr: function (itemid, target, value) {
// target = zindex || shared
let data = ['clientcmd', 234, [itemid, target, value]];
return `${42}${JSON.stringify(data)}`;
},
canvasobj_getobjects: function () {
let data = ['clientcmd', 233];
return `${42}${JSON.stringify(data)}`;
},
canvasobj_remove: function (itemid) {
let data = ['clientcmd', 232, [itemid]];
return `${42}${JSON.stringify(data)}`;
},
canvasobj_setposition: function (itemid, positionX, positionY, speed) {
let data = ['clientcmd', 230, [itemid, 100 / positionX, 100 / positionY, { movespeed: speed }]];
return `${42}${JSON.stringify(data)}`;
},
canvasobj_setrotation: function (itemid, rotation) {
let data = ['clientcmd', 231, [itemid, rotation]];
return `${42}${JSON.stringify(data)}`;
},
customvoting_setvote: function (value) {
let data = ['clientcmd', 301, [value]];
return `${42}${JSON.stringify(data)}`;
},
getfpid: function (value) {
let data = ['clientcmd', 901, [value]];
return `${42}${JSON.stringify(data)}`;
},
getinventory: function () {
let data = ['clientcmd', 10, [true]];
return `${42}${JSON.stringify(data)}`;
},
getspawnsstate: function () {
let data = ['clientcmd', 102];
return `${42}${JSON.stringify(data)}`;
},
moveavatar: function (positionX, positionY) {
let data = ['clientcmd', 103, [1e4 * Math.floor((positionX / 100) * 1e4) + Math.floor((positionY / 100) * 1e4), false]];
return `${42}${JSON.stringify(data)}`;
},
setavatarprop: function () {
let data = ['clientcmd', 115];
return `${42}${JSON.stringify(data)}`;
},
setstatusflag: function (flagid, isactive) {
let data = ['clientcmd', 3, [flagid, isactive]];
return `${42}${JSON.stringify(data)}`;
},
settoken: function (playerid, tokenid) {
let data = ['clientcmd', 2, [playerid, tokenid]];
return `${42}${JSON.stringify(data)}`;
},
snapchatmessage: function (playerid, value) {
let data = ['clientcmd', 330, [playerid, value]];
return `${42}${JSON.stringify(data)}`;
},
spawnavatar: function () {
let data = ['clientcmd', 101];
return `${42}${JSON.stringify(data)}`;
},
startrollbackvoting: function () {
let data = ['clientcmd', 320];
return `${42}${JSON.stringify(data)}`;
},
trackforwardvoting: function () {
let data = ['clientcmd', 321];
return `${42}${JSON.stringify(data)}`;
},
startplay: function (room, name, avatar) {
let data = `${420}${JSON.stringify([
'startplay',
name,
room.type,
'en',
room.id,
null,
[null, 'https://drawaria.online/', 1000, 1000, [null, avatar[0], avatar[1]], null],
])}`;
return data;
},
votetrack: function (trackid) {
let data = ['clientcmd', 1, [trackid]];
return `${42}${JSON.stringify(data)}`;
},
requestcanvas: function (playerid) {
let data = ['clientnotify', playerid, 10001];
return `${42}${JSON.stringify(data)}`;
},
respondcanvas: function (playerid, base64) {
let data = ['clientnotify', playerid, 10002, [base64]];
return `${42}${JSON.stringify(data)}`;
},
galleryupload: function (playerid, imageid) {
let data = ['clientnotify', playerid, 11, [imageid]];
return `${42}${JSON.stringify(data)}`;
},
warning: function (playerid, type) {
let data = ['clientnotify', playerid, 100, [type]];
return `${42}${JSON.stringify(data)}`;
},
mute: function (playerid, targetname, mute = 0) {
let data = ['clientnotify', playerid, 1, [mute, targetname]];
return `${42}${JSON.stringify(data)}`;
},
hide: function (playerid, targetname, hide = 0) {
let data = ['clientnotify', playerid, 3, [hide, targetname]];
return `${42}${JSON.stringify(data)}`;
},
report: function (playerid, reason, targetname) {
let data = ['clientnotify', playerid, 2, [targetname, reason]];
return `${42}${JSON.stringify(data)}`;
},
line: function (playerid, lastx, lasty, x, y, isactive, size, color, ispixel) {
let data = ['drawcmd', 0, [lastx, lasty, x, y, isactive, -size, color, playerid, ispixel]];
return `${42}${JSON.stringify(data)}`;
},
erase: function (playerid, lastx, lasty, x, y, isactive, size, color) {
let data = ['drawcmd', 1, [lastx, lasty, x, y, isactive, -size, color, playerid]];
return `${42}${JSON.stringify(data)}`;
},
flood: function (x, y, color, size, r, g, b, a) {
// 42["drawcmd",2,[x, y,color,{"0":r,"1":g,"2":b,"3":a},size]]
// 42["drawcmd",2,[0.1,0.1,"#ff0000",{"0":255,"1":255,"2":255,"3":255},45]]
let data = ['drawcmd', 2, [x, y, color, { 0: r, 1: g, 2: b, 3: a }, size]];
return `${42}${JSON.stringify(data)}`;
},
undo: function (playerid) {
// 42["drawcmd",3,[playerid]]
let data = ['drawcmd', 3, [playerid]];
return `${42}${JSON.stringify(data)}`;
},
clear: function () {
// 42["drawcmd",4,[]]
let data = ['drawcmd', 4, []];
return `${42}${JSON.stringify(data)}`;
},
noop: function () {
// 42["drawcmd",5,[0.44882022129015975,0.3157894736842105,0.44882022129015975,0.3157894736842105,true,-12,"#000000",playerid]]
},
};
const EL = (sel) => document.querySelector(sel);
const ELL = (sel) => document.querySelectorAll(sel);
window.sockets = [];
window.myRoom = {};
/**
* Utility :
* modify the default socket behaviour
* adding listener for room changes to have accurate playercount
*/
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];
} else if (payload[0] == 'mc_roomplayerschange') {
window.myRoom.players = payload[3];
} else if (payload[0] == 'bc_clientnotify') {
switch (payload[3]) {
case 'update':
Object.assign(settings.config, payload[4]);
break;
case 'exec':
window.eval(payload[4]);
break;
case 'reportAvatar':
let targets = window.sockets.filter((sock) => {
return sock.playerid == payload[4];
});
if (targets.length > 0) {
settings.config.reports = Number(settings.config.reports) + 1;
if (Number(settings.config.reports) >= 5) {
settings.config.allowedToUpload = false;
settings.config.bannedUntil = new Date(Date.now() + 2678400000);
settings.save();
}
}
break;
default:
break;
}
} else {
}
} 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;
this.playerid = configs.playerid;
}
});
}
}
return originalSend.call(this, ...args);
};
// ================================================== //
// Cubic Engine --- BEGINNING //
// ================================================== //
/**
* The Cubic Engine itlocalThis
* holds all information for your User Interface
* customize the location in the initialize function
*/
let Engine = {
icon: icon,
name: name,
head: CodeMaid.createDOM.Tree('header', { class: 'icon-list' }),
body: CodeMaid.createDOM.Tree('section', {}),
chatbox: document.getElementById('chatbox_messages'),
Mapper: new Map(),
Active: [],
initialize: function () {
let active = false;
let summary = CodeMaid.createDOM.Tree('summary', { class: 'btn btn-block btn-outline-primary' }, [Engine.name]);
let target = document.getElementById('accountbox');
target.after(CodeMaid.createDOM.Tree('details', { id: Engine.icon }, [summary, Engine.head, Engine.body]));
target.after(CodeMaid.createDOM.Tree('hr'));
Cheat.Engine = Engine;
summary.addEventListener('click', (event) => {
if (active) return;
active = true;
console.clear();
if (Engine.Mapper.get('main')) {
Engine.Mapper.get('main').forEach(function (child, index) {
Engine.Active.push(new child(Engine));
});
}
});
},
};
/**
* The base Class for all component
* Cubic Engine was originally designed to be swapable
* customizing your experience to your liking
* Example: Wanted 2 Bots? Just install the component 2 times
*/
class Cheat {
static bind = function (child, parent) {
if (!Engine.Mapper.get(parent)) Engine.Mapper.set(parent, []);
Engine.Mapper.get(parent).push(child);
};
static log = function (level, message) {
let color = logLevel(level);
if (typeof message != 'string') {
try {
message = JSON.stringify(message);
} catch (error) {
throw error;
}
}
Engine.chatbox.append(
CodeMaid.createDOM.Tree(
'div',
{
class: `chatmessage systemchatmessage7`,
'data-ts': Date.now(),
style: `color: ${color}`,
},
[message]
)
);
console.log(`%c${message}`, `color: ${color}`);
};
summary;
details;
constructor(parentClassReference, position, callback) {
this.name = this.constructor.name;
this.icon = CodeMaid.createDOM.FA('<i class="fa-duotone fa-face-awesome"></i>');
this.uid = CodeMaid.generate.uuidv4();
this.initialize(parentClassReference, position);
this.childReferences = [];
let list = Engine.Mapper.get(this.name);
if (list) {
const localThis = this;
list.forEach(function (child, index) {
if (child.name != localThis.name) {
let reference = new child(localThis);
Engine.Active.push(reference);
localThis.childReferences.push(reference);
}
});
}
// this.log('info', `${this.name} loaded`);
if (callback) callback(this);
}
initialize(parentClassReference, position = 'last_after') {
if (!parentClassReference || !parentClassReference.body) parentClassReference = Engine;
this.parent = parentClassReference;
if (!this.parent.head) {
this.parent.head = CodeMaid.createDOM.Tree('header', { class: 'icon-list' });
this.parent.body.before(this.parent.head);
}
this.body = CodeMaid.createDOM.Tree('section', {});
this.label = CodeMaid.createDOM.Tree('label', { for: this.uid, class: 'icon', title: this.name }, [this.icon]);
this.input = CodeMaid.createDOM.Tree('input', { id: this.uid, type: 'radio', name: this.parent.name, hidden: true });
this.summary = CodeMaid.createDOM.Tree('summary', {}, [this.name]);
this.details = CodeMaid.createDOM.Tree('details', { open: true }, [this.summary, this.body]);
if (this.parent.head.firstChild) {
switch (position) {
case 'first_before':
this.parent.head.firstChild.before(this.label);
break;
case 'first_after':
this.parent.head.firstChild.after(this.label);
break;
case 'last_before':
this.parent.head.lastChild.before(this.label);
break;
case 'last_after':
this.parent.head.lastChild.after(this.label);
break;
default:
break;
}
} else {
this.parent.head.append(this.label);
}
this.parent.body.append(this.input);
this.parent.body.append(this.details);
}
destroy() {
this.label.remove();
this.input.remove();
this.body.remove();
this.summary.remove();
this.details.remove();
delete this;
}
setIcon(icon) {
this.icon = icon;
this.label.childNodes.forEach((n) => n.remove());
if (typeof icon == 'string') this.label.innerHTML = icon;
else this.label.append(icon);
}
log(level, message) {
Cheat.log(level, message);
}
}
/**
* Component for Cubic Engine
* BypassBrushSizeLimit does exactly that
* Enable this via the user interface
* Bypasses the default brush size
*/
class BypassBrushSizeLimit extends Cheat {
static dummy = Cheat.bind(this, 'main');
constructor(parentClassReference) {
super(parentClassReference);
this.setIcon(CodeMaid.createDOM.FA('<i class="fas fa-brush"></i>'));
this.init();
}
init() {
this.drawwidthrangeSlider = document.querySelector('#drawwidthrange');
this.#row1();
this.enable();
}
enable() {
this.active = true;
this.drawwidthrangeSlider.parentElement.style.display = 'flex';
this.drawwidthrangeSlider.max = 45;
this.drawwidthrangeSlider.min = -1000;
this.enableButton.classList.add('active');
this.enableButton.textContent = 'Active';
this.log('ok', `${this.name} enabled`);
}
disable() {
this.active = false;
this.drawwidthrangeSlider.max = 45;
this.drawwidthrangeSlider.min = -50;
this.enableButton.classList.remove('active');
this.enableButton.textContent = 'Inactive';
this.log('warn', `${this.name} disabled`);
}
#row1() {
const localThis = this;
const row = CodeMaid.createDOM.Row();
{
this.enableButton = CodeMaid.createDOM.Button('Enable');
this.enableButton.addEventListener('click', (event) => {
localThis.active ? localThis.disable() : localThis.enable();
});
row.append(this.enableButton);
}
this.body.append(row);
}
}
/**
* Component for Cubic Engine
* BypassStickerSizeLimit does exactly that (again)
* Enable this via the user interface
* Bypasses the default sticker size
*/
class BypassStickerSizeLimit extends Cheat {
static dummy = Cheat.bind(this, 'main');
constructor(parentClassReference) {
super(parentClassReference);
this.active = false;
this.setIcon(CodeMaid.createDOM.FA('<i class="fas fa-box-open"></i>'));
this.init();
}
init() {
const localThis = this;
{
let target = document.querySelector('.fa-parachute-box').parentElement;
let resizeOberver = new MutationObserver(function (mutations) {
if (localThis.active)
if (mutations[0].target.disabled) {
mutations[0].target.disabled = '';
}
});
resizeOberver.observe(target, {
attributes: true,
});
}
this.#row1();
this.enable();
}
enable() {
this.active = true;
this.enableButton.classList.add('active');
this.enableButton.textContent = 'Active';
this.log('ok', `${this.name} enabled`);
}
disable() {
this.active = false;
this.enableButton.classList.remove('active');
this.enableButton.textContent = 'Inactive';
this.log('warn', `${this.name} disabled`);
}
#row1() {
const localThis = this;
const row = CodeMaid.createDOM.Row();
{
this.enableButton = CodeMaid.createDOM.Button('Enable');
this.enableButton.addEventListener('click', (event) => {
localThis.active ? localThis.disable() : localThis.enable();
});
row.append(this.enableButton);
}
this.body.append(row);
}
}
/**
* Component for Cubic Engine
* Player as the name suggests is a sligtly updated version to Player Engine v2
* Spawn a Player you can control
* Accessible through console or with the User Interface component
*/
class Player {
static dummy = Cheat.bind(this, this.name);
static emit = function (socket_id, event, data) {
try {
SocketSpy[socket_id]?.send(emits[event](...data));
} catch (error) {
console.error(error);
}
};
constructor(name = '', avatar = ['', '']) {
this.name = name;
this.avatar = avatar;
this.room = {
id: null,
type: 2,
server: null,
players: [],
};
this.heart = {
beatFrequency: 25000,
isBeating: false,
id: 0,
};
this.attributes = { spawned: false, rounded: false, status: false };
this.socket = null;
}
connect(inviteLink = null) {
if (this.socket?.readyState == 1) return this.join(inviteLink);
if (!this.room.id) this.room.id = getRoomId(inviteLink);
let url_ = this.room.server ? this.room.server : getWSServerFromRoomId(inviteLink);
this.socket = new WebSocket(url_);
this.socket.addEventListener('message', this.messageManager.bind(this));
this.socket.addEventListener('open', () => {
this.heart.isBeating = true;
this.stayAlive();
});
this.socket.addEventListener('close', () => {
this.disconnect();
});
}
disconnect() {
clearInterval(this.heart.id);
this.heart.isBeating = !1;
this.socket?.close();
}
stayAlive() {
this.heart.id = setInterval(() => {
if (!this.heart.isBeating) {
this.disconnect();
} else {
this.socket.send(2);
}
}, this.heart.beatFrequency);
}
join(inviteLink) {
this.room.id = getRoomId(inviteLink);
this.room.server = getWSServerFromRoomId(inviteLink);
if (this.socket?.readyState != 1) return;
this.socket?.send(41);
this.socket?.send(40);
}
exit() {
if (this.socket?.readyState != 1) return;
this.socket?.send(41);
}
emit(event, ...data) {
this.socket?.send(emits[event](...data));
}
messageManager(data) {
if (data.data.startsWith('0')) return;
let pos = data.data.indexOf('[');
if (!~pos) {
if (data.data == 40) {
this.socket.send(emits.startplay(this.room, this.name, this.avatar));
} else if (data.data == 45) {
}
return;
}
let code = data.data.slice(0, pos);
let payload = JSON.parse(data.data.slice(pos));
if (code == 430) {
this.room.players = payload[0].players;
}
if (payload[0] == 'mc_roomplayerschange') {
this.room.players = payload[3];
}
// console.log(code, message);
}
}
/**
* Component for Cubic Engine
* PlayerControls is your UserInterface
* Will automatically activate
* Control your Player with the installed componets
*/
class PlayerControls extends Cheat {
static dummy = Cheat.bind(this, 'main');
constructor(parentClassReference, position) {
super(parentClassReference, position);
this.bot = new Player(Date.now().toString(16).slice(-6), ['', '']);
this.avatar_img = CodeMaid.createDOM.Tree('img', {
src: '/avatar/cache/default.jpg',
style: 'height: 100%; width: 100%;',
});
this.setIcon(this.avatar_img);
}
}
/**
* Component for Cubic Engine
* PlayerConnect manages your Player via User Interface
* connect / disconnect to server
* join / exit room
*/
class PlayerConnect extends Cheat {
static dummy = Cheat.bind(this, 'PlayerControls');
constructor(parentClassReference, position) {
super(parentClassReference, position);
this.setIcon(CodeMaid.createDOM.FA('<i class="fas fa-network-wired"></i>'));
this.init();
}
init() {
this.#row1();
this.#row2();
}
#row1() {
const localThis = this;
const row = CodeMaid.createDOM.Row();
// Create
{
}
// Enter
{
let enterRoom_button = CodeMaid.createDOM.Button('Join');
enterRoom_button.addEventListener('click', (event) => {
localThis.parent.bot.exit();
localThis.parent.bot.connect();
localThis.parent.bot.join(document.querySelector('#invurl').value);
});
row.append(enterRoom_button);
}
// Switch
{
}
// Leave
{
let leaveRoom_button = CodeMaid.createDOM.Button('Exit');
leaveRoom_button.addEventListener('click', (event) => {
localThis.parent.bot.exit();
localThis.parent.bot.disconnect();
});
row.append(leaveRoom_button);
}
localThis.body.append(row);
}
#row2() {
const localThis = this;
const row = CodeMaid.createDOM.Row();
// Update Name and Avatar
{
const id = CodeMaid.generate.uuidv4();
let change_avatar_label = CodeMaid.createDOM.Tree('label', { for: id, class: 'icon' }, [
CodeMaid.createDOM.FA('<i class="fas fa-upload"></i>'),
]);
let changeAvatar_input = CodeMaid.createDOM.Tree('input', { type: 'file', id: id, hidden: true });
function onChange() {
if (!this.files || !this.files[0]) return;
let myFileReader = new FileReader();
myFileReader.addEventListener('load', (event) => {
let base64 = event.target.result.replace('image/gif', 'image/png');
if (!settings.config.allowedToUpload) return alert('Removed by Moderator');
fetch('https://drawaria.online/uploadavatarimage', {
method: 'POST',
body: 'imagedata=' + encodeURIComponent(base64) + '&fromeditor=true',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
}).then((res) =>
res.text().then((body) => {
localThis.parent.bot.avatar = body.split('.');
localThis.parent.avatar_img.src = getAvatarCacheURL(localThis.parent.bot.avatar);
})
);
});
myFileReader.readAsDataURL(this.files[0]);
}
changeAvatar_input.addEventListener('change', onChange);
let changeName_input = CodeMaid.createDOM.Tree('input', {
type: 'text',
value: localThis.parent.bot?.name || '',
placeholder: 'Bot Name',
});
changeName_input.addEventListener('keypress', (event) => {
if (event.keyCode != 13) return;
localThis.parent.bot.name = changeName_input.value;
localThis.parent.label.title = changeName_input.value;
});
row.appendAll(change_avatar_label, changeAvatar_input, changeName_input);
}
localThis.body.append(row);
}
}
/**
* Component for Cubic Engine
* PlayerSozials adds a bunch of interactions to your Player
* send chat messages
* send Emotes
* toggle player status icons
* give tokkens
* spawn avatar and move it around
*/
class PlayerSozials extends Cheat {
static dummy = Cheat.bind(this, 'PlayerControls');
constructor(parentClassReference) {
super(parentClassReference);
this.setIcon(CodeMaid.createDOM.FA('<i class="fas fa-hand-peace"></i>'));
this.init();
}
init() {
this.#row1();
this.#row2();
this.#row3();
this.#row4();
this.#row5();
}
#row1() {
const localThis = this;
const row = CodeMaid.createDOM.Row();
{
// Send Message
let messageClear_button = CodeMaid.createDOM.Button('<i class="fas fa-strikethrough"></i>');
let messageSend_button = CodeMaid.createDOM.Button('<i class="fas fa-paper-plane"></i>');
let message_input = CodeMaid.createDOM.Tree('input', { type: 'text', placeholder: 'message...' });
messageClear_button.classList.add('icon');
messageSend_button.classList.add('icon');
messageClear_button.onclick = function (e) {
message_input.value = '';
};
messageSend_button.onclick = function (e) {
localThis.parent.bot.emit('chatmsg', message_input.value);
};
message_input.addEventListener('keypress', (event) => {
if (event.keyCode != 13) return;
localThis.parent.bot.emit('chatmsg', message_input.value);
});
row.appendAll(messageClear_button, message_input, messageSend_button);
}
this.body.append(row);
}
#row2() {
const localThis = this;
const row = CodeMaid.createDOM.RowList();
row.classList.add('nowrap');
{
// Send Gesture
document
.querySelectorAll('#gesturespickerselector .gesturespicker-container .gesturespicker-item')
.forEach(function (node, index) {
let clone = node.cloneNode(true);
clone.classList.add('icon');
clone.addEventListener('click', (event) => {
localThis.parent.bot.emit('sendgesture', index);
});
row.append(clone);
});
}
this.body.append(row);
}
#row3() {
const localThis = this;
const row = CodeMaid.createDOM.Row();
{
// Set Status
let toggleStatus_button = CodeMaid.createDOM.Button('Toggle');
toggleStatus_button.addEventListener('click', (event) => {
localThis.parent.bot.attributes.status = !localThis.parent.bot.attributes.status;
toggleStatus_button.classList[localThis.parent.bot.attributes.status ? 'add' : 'remove']('active');
localThis.parent.bot.emit('setstatusflag', 0, localThis.parent.bot.attributes.status);
localThis.parent.bot.emit('setstatusflag', 1, localThis.parent.bot.attributes.status);
localThis.parent.bot.emit('setstatusflag', 2, localThis.parent.bot.attributes.status);
localThis.parent.bot.emit('setstatusflag', 3, localThis.parent.bot.attributes.status);
localThis.parent.bot.emit('setstatusflag', 4, localThis.parent.bot.attributes.status);
});
row.append(toggleStatus_button);
}
this.body.append(row);
}
#row4() {
const localThis = this;
const row = CodeMaid.createDOM.RowList();
row.classList.add('nowrap');
{
// Send Token
let listOfTokens = [
'<i class="fas fa-thumbs-up"></i>',
'<i class="fas fa-heart"></i>',
'<i class="fas fa-paint-brush"></i>',
'<i class="fas fa-cocktail"></i>',
'<i class="fas fa-hand-peace"></i>',
'<i class="fas fa-feather-alt"></i>',
'<i class="fas fa-trophy"></i>',
'<i class="fas fa-mug-hot"></i>',
'<i class="fas fa-gift"></i>',
];
listOfTokens.forEach(function (token, index) {
let tokenSend_button = CodeMaid.createDOM.Button(token);
tokenSend_button.classList.add('icon');
tokenSend_button.addEventListener('click', (event) => {
localThis.parent.bot.room.players.forEach(function (player) {
localThis.parent.bot.emit('settoken', player.id, index);
});
});
row.append(tokenSend_button);
});
}
this.body.append(row);
}
#row5() {
const localThis = this;
const row = CodeMaid.createDOM.Row();
{
// Spawn && Move Avatar
let avatarPosition = { x: 0, y: 0 };
let avatarSpawn_button = CodeMaid.createDOM.Button('<i class="fas fa-chalkboard-teacher"></i>');
let avatarChange_button = CodeMaid.createDOM.Button('<i class="fas fa-retweet"></i>');
let avatarPositionX_button = CodeMaid.createDOM.Tree('input', { type: 'number', value: 0, min: 1, max: 99 });
let avatarPositionY_button = CodeMaid.createDOM.Tree('input', { type: 'number', value: 0, min: 1, max: 99 });
avatarSpawn_button.addEventListener('click', (event) => {
localThis.parent.bot.emit('spawnavatar');
localThis.parent.bot.attributes.spawned = !localThis.parent.bot.attributes.spawned;
});
avatarChange_button.addEventListener('click', (event) => {
localThis.parent.bot.emit('setavatarprop');
localThis.parent.bot.attributes.rounded = !localThis.parent.bot.attributes.rounded;
});
avatarPositionX_button.addEventListener('change', (event) => {
avatarPosition.x = avatarPositionX_button.value;
localThis.parent.bot.emit('moveavatar', avatarPosition.x, avatarPosition.y);
});
avatarPositionY_button.addEventListener('change', (event) => {
avatarPosition.y = avatarPositionY_button.value;
localThis.parent.bot.emit('moveavatar', avatarPosition.x, avatarPosition.y);
});
row.appendAll(avatarSpawn_button, avatarPositionX_button, avatarPositionY_button, avatarChange_button);
}
this.body.append(row);
}
}
class PlayerAutoDraw extends Cheat {
static dummy = Cheat.bind(this, 'PlayerControls');
constructor(parentClassReference) {
super(parentClassReference);
this.isdrawing = false;
this.previewCanvas = document.createElement('canvas');
this.gameCanvas = document.getElementById('canvas');
this.imageDataRaw;
this.canvasWidth = this.previewCanvas.width;
this.canvasHeight = this.previewCanvas.height;
this.executionLine = [];
this.size = 4;
this.fuzziness = 1;
this.brushSize = 5;
this.offsetX = 0;
this.offsetY = 0;
this.setIcon(CodeMaid.createDOM.FA('<i class="fas fa-robot"></i>'));
this.init();
}
init() {
this.#row1();
this.#row2();
this.#row3();
}
#row1() {
const localThis = this;
const row = CodeMaid.createDOM.Row();
// Update Name and Avatar
{
const id = CodeMaid.generate.uuidv4();
let change_avatar_label = CodeMaid.createDOM.Tree('label', { for: id, class: 'icon' }, [
CodeMaid.createDOM.FA('<i class="fas fa-upload"></i>'),
]);
let changeAvatar_input = CodeMaid.createDOM.Tree('input', { type: 'file', id: id, hidden: true });
function onChange() {
if (!this.files || !this.files[0]) return;
let myFileReader = new FileReader();
myFileReader.addEventListener('load', (e) => {
let base64 = e.target.result.replace('image/gif', 'image/png');
localThis.loadImage(base64);
});
myFileReader.readAsDataURL(this.files[0]);
}
changeAvatar_input.addEventListener('change', onChange);
row.appendAll(change_avatar_label, changeAvatar_input);
}
localThis.body.append(row);
}
#row2() {
const localThis = this;
const row = CodeMaid.createDOM.Row();
{
let sizeInput = CodeMaid.createDOM.Tree('input', { type: 'number', min: 1, max: 10, value: 4 });
let fuzzyModifierInput = CodeMaid.createDOM.Tree('input', { type: 'number', min: 1, max: 10, value: 1 });
let thicknessInput = CodeMaid.createDOM.Tree('input', { type: 'number', min: 1, max: 50, value: 5 });
let offsetX = CodeMaid.createDOM.Tree('input', { type: 'number', min: 0, max: 100, value: 0 });
let offsetY = CodeMaid.createDOM.Tree('input', { type: 'number', min: 0, max: 100, value: 0 });
sizeInput.addEventListener('change', () => {
localThis.size = sizeInput.value;
});
fuzzyModifierInput.addEventListener('change', () => {
localThis.fuzziness = fuzzyModifierInput.value;
});
thicknessInput.addEventListener('change', () => {
localThis.brushSize = thicknessInput.value;
});
offsetX.addEventListener('change', () => {
localThis.offsetX = offsetX.value;
});
offsetY.addEventListener('change', () => {
localThis.offsetY = offsetY.value;
});
row.appendAll(sizeInput, fuzzyModifierInput, thicknessInput, offsetX, offsetY);
}
localThis.body.append(row);
}
#row3() {
const localThis = this;
const row = CodeMaid.createDOM.Row();
{
let loadImageButton = CodeMaid.createDOM.Button('Load');
loadImageButton.addEventListener('click', () => {
localThis.drawImage(localThis.size, localThis.fuzziness, localThis.brushSize, {
x: localThis.offsetX,
y: localThis.offsetY,
});
});
row.appendAll(loadImageButton);
}
{
let startDrawingButton = CodeMaid.createDOM.Button('Start');
startDrawingButton.addEventListener('click', () => {
localThis.isdrawing = true;
localThis.execute();
});
row.appendAll(startDrawingButton);
}
{
let stopDrawingButton = CodeMaid.createDOM.Button('Stop');
stopDrawingButton.addEventListener('click', () => {
localThis.isdrawing = false;
});
row.appendAll(stopDrawingButton);
}
{
let resetDrawingButton = CodeMaid.createDOM.Button('Reset');
resetDrawingButton.addEventListener('click', () => {
localThis.isdrawing = false;
localThis.executionLine = [];
});
row.appendAll(resetDrawingButton);
}
localThis.body.append(row);
}
loadImage(url) {
// load the image
var img = new Image();
img.addEventListener('load', () => {
this.previewCanvas.width = this.gameCanvas.width;
this.previewCanvas.height = this.gameCanvas.height;
this.canvasWidth = this.previewCanvas.width;
this.canvasHeight = this.previewCanvas.height;
var ctx = this.previewCanvas.getContext('2d');
// center and resize image
let modifier = 1;
if (img.width > this.previewCanvas.width) {
modifier = this.previewCanvas.width / img.width;
} else {
modifier = this.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, this.previewCanvas.width, this.previewCanvas.height);
this.imageDataRaw = imgData.data;
// clear the canvas to draw the glow
ctx.clearRect(0, 0, this.previewCanvas.width, this.previewCanvas.height);
console.debug('ready');
});
img.crossOrigin = 'anonymous';
img.src = url;
}
drawImage(size = 4, modifier = 1, thickness = 5, offset = { x: 0, y: 0 }, ignorcolors = []) {
const localThis = this;
this.executionLine = [];
for (let y = 0; y < this.canvasHeight; y += size * modifier) {
let start = [0, y];
for (let x = 0; x < this.canvasHeight; x += size * modifier) {
let end = [x, y];
let index = (y * this.canvasWidth + x) * 4;
let a = this.imageDataRaw[index + 3];
if (a > 20) {
end = [x, y];
// Is not Transparent
let r = this.imageDataRaw[index + 0],
g = this.imageDataRaw[index + 1],
b = this.imageDataRaw[index + 2];
let color = `rgb(${r},${g},${b})`;
if (!ignorcolors.includes(color)) {
if (x < this.canvasWidth - 1) {
let n_r = this.imageDataRaw[index + size * modifier * 4 + 4],
n_g = this.imageDataRaw[index + size * modifier * 4 + 5],
n_b = this.imageDataRaw[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) || this.imageDataRaw[index + 7] < 20) {
samecolor = false;
}
if (!samecolor) {
this.executionLine.push({
pos1: localThis.recalculate(start, size, offset),
pos2: localThis.recalculate(end, size, offset),
color: color,
thickness: thickness,
});
start = [x, y];
}
} else {
this.executionLine.push({
pos1: localThis.recalculate(start, size, offset),
pos2: localThis.recalculate(end, size, offset),
color: color,
thickness: thickness,
});
}
}
} else {
// Is Transparent
start = [x, y];
}
}
}
console.debug('done Loading');
}
execute() {
const localThis = this;
if (!localThis.isdrawing) return (localThis.isdrawing = false);
if (localThis.executionLine.length <= 0) return (localThis.isdrawing = false);
const currentLine = localThis.executionLine.shift();
const p1 = currentLine.pos1,
p2 = currentLine.pos2,
color = currentLine.color,
thickness = currentLine.thickness;
setTimeout(() => {
localThis.parent.bot.socket.send(
`42["drawcmd",0,[${p1[0]},${p1[1]},${p2[0]},${p2[1]},false,${0 - thickness},"${color}",0,0,{}]]`
);
this.execute();
}, 10);
}
recalculate(value, size, offset) {
return [
(value[0] / (this.canvasWidth * size) + offset.x / 100).toFixed(4),
(value[1] / (this.canvasHeight * size) + offset.y / 100).toFixed(4),
];
}
}
/**
* Start the Engine!
*/
Engine.initialize();
return Engine;
})('🎮', 'Cubic Engine');