// ==UserScript==
// @name vanis X gota
// @namespace http://tampermonkey.net/
// @version 1.8
// @description https://discord.gg/UsPCMBDGES
// @author Eva#5121 YaoYT#4707
// @match *://vanis.io/*
// @compatible chrome
// @compatible opera
// @compatible firefox
// @icon https://cdn.discordapp.com/attachments/891105311434367076/972941615607984128/60234575-fondo-rojo-solido-o-papel-rojo-con-textura-de-fondo-para-el-diseno-del-dia-de-san-valentin-o-fondo-d11.jpg
// @require http://code.jquery.com/jquery-3.3.1.min.js
// @require https://code.jquery.com/ui/1.12.0/jquery-ui.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery-confirm/3.3.0/jquery-confirm.min.js
// @run-at document-start
// @grant unsafeWindow
// @license N/A
// ==/UserScript==
(function() {
var local = {
SCRIPT_CONFIG: {
},
MENU_CONFIG: {
COLOR_1: "#0A2A64",
COLOR_2:"#0A2A64",
},
COLOR_HUE: 100,
COLOR_HUE2: 300,
GAME_WS: false,
GAME_INIT: false,
PLAYER_PACKET_SPAWN: [],
PLAYER_SOCKET: false,
PLAYER_IS_DEAD: false,
PLAYER_MOUSE: {
x: false,
y: false,
},
GAME_BYPASS: {
mouseFrozen: Symbol(),
utf8: new TextEncoder()
}
}
function changeHue() {
355 == local.COLOR_HUE && (local.COLOR_HUE = 100), local.COLOR_HUE++;
355 == local.COLOR_HUE2 && (local.COLOR_HUE2 = 100), local.COLOR_HUE2++;
$('.fade-box').css({
background: 'linear-gradient(to right bottom,hsl('+local.COLOR_HUE+', 50%, 10%),hsl('+local.COLOR_HUE2+', 50%, 10%)'
})
}
function ready() {
setInterval(() => {
if(local.MENU_CONFIG.RAINBOW) {
changeHue()
} else {
$('.fade-box').css({
background: `linear-gradient(to right bottom,${local.MENU_CONFIG.COLOR_1},${local.MENU_CONFIG.COLOR_2})`
})
}
}, 10)
}
const { fillText } = CanvasRenderingContext2D.prototype;
CanvasRenderingContext2D.prototype.fillText = function(text, x, y) {
let config = local.SCRIPT_CONFIG
if(text == document.getElementById("nickname").value) {
this.fillStyle = config.NAME_COLOR;
}
fillText.call(this, ...arguments);
}
document.addEventListener("DOMContentLoaded", ready)
})();
class Reader {
offset = 0;
constructor(arrayBuffer, LE = true) {
this.view = new DataView(arrayBuffer);
this.offset = 0;
this.LE = LE;
}
skip = bytes => ((this.offset += bytes), this);
readUint8 = () => this.view.getUint8(this.offset++);
readUint16 = LE => ((this.offset += 2), this.view.getUint16(this.offset - 2, LE ?? this.LE));
readUint32 = LE => ((this.offset += 4), this.view.getUint32(this.offset - 4, LE ?? this.LE));
readInt8 = () => this.view.getInt8(this.offset++);
readInt16 = LE => ((this.offset += 2), this.view.getInt16(this.offset - 2, LE ?? this.LE));
readInt32 = LE => ((this.offset += 4), this.view.getInt32(this.offset - 4, LE ?? this.LE));
readFloat32 = LE => ((this.offset += 4), this.view.getFloat32(this.offset - 4, LE ?? this.LE));
readFloat64 = LE => ((this.offset += 8), this.view.getFloat64(this.offset - 8, LE ?? this.LE));
readBytes = length => ((this.offset += length), this.view.buffer.slice(this.offset - length, this.offset));
readString(unicode = false) {
const method = unicode ? this.readUint16 : this.readUint8;
let string = '';
let charCode = method();
while (charCode !== 0) {
string += String.fromCharCode(charCode);
charCode = method();
}
return string;
}
}
class Writer {
constructor(length, offset = 0) {
this.view = new DataView(new ArrayBuffer(length));
this.offset = offset;
}
skip = bytes => ((this.offset += bytes), this);
writeUint8 = value => (this.view.setUint8(this.offset++, value), this);
writeUint16 = value => ((this.offset += 2), this.view.setUint16(this.offset - 2, value, true), this);
writeUint32 = value => ((this.offset += 4), this.view.setUint32(this.offset - 4, value, true), this);
writeInt8 = value => (this.view.setInt8(this.offset++, value), this);
writeInt16 = value => ((this.offset += 2), this.view.setInt16(this.offset - 2, value, true), this);
writeInt32 = value => ((this.offset += 4), this.view.setInt32(this.offset - 4, value, true), this);
writeFloat32 = value => ((this.offset += 4), this.view.setFloat32(this.offset - 4, value, true), this);
writeFloat64 = value => ((this.offset += 8), this.view.setFloat64(this.offset - 8, value, true), this);
writeString(string, unicode = false) {
const method = unicode ? this.writeUint16 : this.writeUint8;
Array.from(string).forEach(char => method(char.charCodeAt(0)));
method(0);
return this;
}
get raw() {
return this.view;
}
}
/**
*
* @param {keyof HTMLElementTagNameMap} el element tag name e.g. 'div'
* @param {{ [key:string]: string }} attrs attributes array to be applied to new element
* @param {...(string|Node)[]} append children to apply
* @returns {HTMLElement} HTMLElement
*/
function newElement(el, attrs = {}, ...append) {
const element = document.createElement(el);
Object.entries(attrs).forEach(([key, val]) => element.setAttribute(key, val));
element.append(...append);
return element;
}
const WebSocket = window.WebSocket;
const partySpan = newElement('span', {}, 'YaoYT Party');
const totalMass = newElement('span', {}, '0');
const title = newElement(
'div',
{ style: `display:flex;flex-direction:row;justify-content:space-between;flex:1;font-weight:bold;margin-bottom:5px;letter-spacing:1px;` },
partySpan,
totalMass
);
const members = newElement('div', { style: `display:flex;flex-direction:column;font-size:12px` });
const hud = newElement(
'div',
{
style: `white-space:nowrap;display:flex;flex-direction:column;position:absolute;top:230px;background:rgba(0,0,0,.45);border-radius:4px;pointer-events:none;border:1px solid white;padding:5px;min-width:150px;left:5px;box-sizing:border-box;font-size:15px;`,
},
title,
members
);
const createMember = (nickname, mass, color) =>
newElement(
'div',
{ style: `display:flex;flex-direction:row;justify-content:space-between;flex:1` },
newElement('span', { style: `margin-right: 15px;color:${color}` }, nickname),
newElement('span', {}, mass)
);
const formatUrl = url => url.replace('wss://', '').replace(/\/$/, '');
class ApiConnection {
constructor(_) {
this._ = _;
this.toast = Swal.mixin({ toast: true, position: 'top', showConfirmButton: false, showCloseButton: true });
this.v = 1;
this.url = 'wss://vanis-parties.herokuapp.com';
this.massMap = new Map();
/** @type {WebSocket} */
this.ws = null;
this.interval = 0;
this.lastBoop = 0;
this.idlePacket = new Writer(2 + 4 + 8 + 1).writeUint8(2).writeString('null').writeUint16(0).writeUint16(0).writeUint32(0).writeUint8(0).raw.buffer;
this.boot();
}
get account() {
return this._.app.$children[6].$children[2].account;
}
get connected() {
return this.interval !== 0;
}
get allowed() {
return this._ && this.account?.discord_id;
}
getMass(pid) {
const mass = this.massMap.get(pid);
if (mass === -1) return 'SPEC';
if (mass === 0) return 'You are dead';
return mass ?? '????';
}
head = () => fetch(this.url.replace('wss', 'https').replace('ws', 'http'), { method: 'HEAD' });
boot = () => {
if (!this.allowed) {
members.textContent = 'Not logged in, retrying...';
return setTimeout(this.boot, 1000);
}
members.textContent = 'Waking up the server';
this.head()
.then(() => this.connect())
.catch(() => {
members.textContent = 'Waking up failed, re-trying';
setTimeout(this.boot, 1000);
});
};
connect = () => {
if (!this.allowed) {
members.textContent = 'Not logged in, retrying...';
return setTimeout(this.connect, 1000);
}
members.textContent = 'Connecting to the server';
this.ws = new WebSocket(this.url);
this.ws.binaryType = 'arraybuffer';
this.ws.onopen = this.onopen;
this.ws.onclose = this.onclose;
this.ws.onmessage = ({ data }) => this.onmessage(new Reader(data));
};
onopen = () => {
const id = this.account?.discord_id;
if (!id) {
members.textContent = 'Not logged in';
return this.ws.close(4000);
}
members.textContent = 'Logging in...';
this.ws.send(new Writer(3 + id.length).writeUint8(1).writeString(id).writeUint8(this.v).raw.buffer);
};
onclose = e => {
clearInterval(this.interval);
this.interval = 0;
if (e.code === 1008) {
members.textContent = 'Update required';
return this.toast.fire({ title: '[Parties] Old script version, please update', type: 'error' });
}
if (e.code === 1002 || e.code === 1003) {
members.textContent = 'Server is updating or new version is coming';
return this.toast.fire({ title: '[Parties] Protocol error\nWait for new version or a server fix', type: 'error' });
}
members.textContent = 'Disconnected, retrying';
setTimeout(this.connect, 1000);
console.log('you are offline', e);
};
/** @param {Reader} data */
onmessage = data => {
const opcode = data.readUint8();
switch (opcode) {
case 1: {
this.interval = setInterval(this.sendData, 888);
this.lastBoop = Date.now();
members.textContent = '';
break;
}
case 2: {
this.massMap.clear();
let amount = data.readUint8();
while (--amount !== -1) {
const [pid, mass, flags] = [data.readUint16(), data.readUint32(), data.readUint8()];
if (flags & 1) this.massMap.set(pid, -1);
else this.massMap.set(pid, mass);
}
break;
}
default: {
console.log('invalid opcode received: ' + opcode);
}
}
};
sendData = () => {
// keep free tier server alive because apparently alive websocket connections are not a good reason to not shut down due to inactivity
if (this.lastBoop + 10 * 60 * 1000 <= Date.now()) {
const temp = this.lastBoop;
this.lastBoop = Date.now(); // incase the next sendData is called before this request is done
this.head()
.then(() => console.log('booped successfully'))
.catch(e => console.log('boop failed', e, (this.lastBoop = temp + 60000)));
}
if (!this._) return;
if (!this._.ws || this._.ws.readyState !== WebSocket.OPEN || !this._.activePid) return void this.ws.send(this.idlePacket);
const url = formatUrl(this._.ws.url);
const tagId = this._.tagId || 0;
const pid = this._.pid || this._.activePid;
const mass = this._.score || 0;
const flags = Number(this._.spectating);
const multi = this._.minion;
const summed = settings.minionMode === false && !!multi && multi.mass > 0 && mass > 0;
const sendMass = Math.max(0, summed ? mass - multi.mass : mass);
const data = new Writer(2 + url.length + 2 + (2 + 4 + 1) * (multi ? 2 : 1))
.writeUint8(2)
.writeString(url)
.writeUint16(tagId)
.writeUint16(pid)
.writeUint32(sendMass)
.writeUint8(flags);
if (multi) {
data
.writeUint16(multi.pid || 0)
.writeUint32(multi.mass || 0)
.writeUint8(0);
}
this.ws.send(data.raw.buffer);
};
}
const init = _ => {
document.getElementById('hud').append(hud);
const partyConnection = new ApiConnection(_);
_.partyConnection = partyConnection;
const tick = () => {
if (!partyConnection.connected || !_) return;
if (!_?.playerManager?.players) return (members.textContent = 'Not on a server');
if (!_.tagId) return (members.textContent = 'you must be in a tag');
members.textContent = '';
while (members.children.length) members.removeChild(members.firstChild);
let massSum = 0;
const elements = Object.values(_.playerManager.players)
.filter(player => player.tagId === _.tagId)
.map(({ pid, name, nameColorCss }) => {
const color = nameColorCss || '#FFF000';
const mass = partyConnection.getMass(pid);
if (typeof mass === 'number') massSum += mass;
return createMember(name, mass, color);
});
totalMass.textContent = massSum;
members.append(...elements);
};
tick();
setInterval(tick, 1000);
};
const c = Function.prototype.call;
Function.prototype.call = function (t, ...a) {
if (t === a[1] && typeof a[2] === 'function') {
const e = this;
function f() {
e.apply(this, arguments);
members.textContent = 'Please log in';
const _ = arguments[2](1);
const g = _.app.$children[6].$children[2].setAccountData;
_.app.$children[6].$children[2].setAccountData = function () {
init(_);
_.app.$children[6].$children[2].setAccountData = g;
return g.apply(this, arguments);
};
}
Function.prototype.call = c;
return c.apply(f, arguments);
}
return c.apply(this, arguments);
};