1327 lines
No EOL
55 KiB
JavaScript
1327 lines
No EOL
55 KiB
JavaScript
// ==UserScript==
|
|
// @name MultiProbe
|
|
// @namespace https://*.angusnicneven.com/*
|
|
// @version 20241013.1
|
|
// @description Probe with friends!
|
|
// @author tgpholly
|
|
// @match https://*.angusnicneven.com/*
|
|
// @match https://*.heavenonline.xyz/*
|
|
// @icon https://www.google.com/s2/favicons?sz=64&domain=angusnicneven.com
|
|
// @grant none
|
|
// ==/UserScript==
|
|
|
|
let continueRunningScript = true;
|
|
if (!window.TE_ACTIVE) {
|
|
if (window.location.href.includes("www.")) {
|
|
window.location.href = window.location.href.replace("www.", "");
|
|
continueRunningScript = false;
|
|
}
|
|
|
|
let didWeNukeIt = false;
|
|
function nukeCC() {
|
|
if (didWeNukeIt) {
|
|
return;
|
|
}
|
|
try {
|
|
const doWeHaveCC = document.getElementById("cursor-container");
|
|
if (doWeHaveCC) {
|
|
doWeHaveCC.remove();
|
|
didWeNukeIt = true;
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
setInterval(nukeCC, 1000);
|
|
}
|
|
|
|
const windowLocation = window.location.href;
|
|
window.multiprobe_debug = false;
|
|
window.multiprobe_connectLocal = false;
|
|
|
|
const SITE_DEFAULT_CURSOR = {
|
|
"localhost": "https://angusnicneven.com/cursor/rrw.png",
|
|
"angusnicneven.com": "https://angusnicneven.com/cursor/rrw.png",
|
|
"heavenonline.xyz": "https://heavenonline.xyz/core/cursor/frame1.png"
|
|
};
|
|
|
|
const SITE_DEFAULT_HOVER_CURSOR = {
|
|
"localhost": "https://angusnicneven.com/cursor/rrw2.png",
|
|
"angusnicneven.com": "https://angusnicneven.com/cursor/rrw2.png",
|
|
"heavenonline.xyz": "https://heavenonline.xyz/core/cursor_hover/frame1.png"
|
|
};
|
|
|
|
const SITE_LOGOS = {
|
|
"*.angusnicneven.com": "https://eusv.net/NLuGOZGZtS3BEr",
|
|
"multiprobe.eusv.net": "https://eusv.net/NLuGOZGZtS3BEr",
|
|
"*.heavenonline.xyz": "https://eusv.net/Iu8kZfgAC8NFdU",
|
|
"*.eusv.net": "https://eusv.net/C89X3tb5mtkVI7"
|
|
};
|
|
|
|
const PROTOCOL_VERSION = 2;
|
|
|
|
console.log("[MP] MultiProbe init");
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
// Make sure to change the userscript version too!!!!!!!!!!
|
|
const USERSCRIPT_VERSION_RAW = "20241013.1";
|
|
const USERSCRIPT_VERSION = parseInt(USERSCRIPT_VERSION_RAW.replace(".", ""));
|
|
|
|
if (!continueRunningScript) {
|
|
return;
|
|
}
|
|
|
|
// This is a minified version of the code at https://git.eusv.net/tgpholly/bufferStuff/releases/tag/1.5.0 + FunkyArray
|
|
class BrowserBuffer{constructor(dataOrSize){if(typeof dataOrSize==="number"){this.buffer=new Uint8Array(dataOrSize)}else if(typeof dataOrSize==="object"){this.buffer=new Uint8Array(dataOrSize)}else{this.buffer=new Uint8Array(0)}this.dataView=new DataView(this.buffer.buffer)}static allocUnsafe(size){return this.alloc(size)}static allocUnsafeSlow(size){return this.alloc(size)}static alloc(size){return new BrowserBuffer(size)}static concat(buffers,totalLength){let joinedArrays;if(totalLength!==undefined){joinedArrays=new Uint8Array(totalLength)}else{let arraysLength=0;for(const buffer of buffers){arraysLength+=buffer.length}joinedArrays=new Uint8Array(arraysLength)}let offset=0;for(const buffer of buffers){joinedArrays.set(buffer.buffer,offset);offset+=buffer.length}return new BrowserBuffer(joinedArrays)}static from(data){if(typeof data==="string"){throw new Error("BrowserBuffer does not currently support creating buffers from strings.")}return new BrowserBuffer(data)}static of(){}static isBuffer(){}static isEncoding(){}static byteLength(){}static copyBytesFrom(){}static compare(){}get length(){return this.buffer.length}checkRanged(value,valueName,lowRange,highRange){if(value<lowRange||value>highRange){throw new Error(`The value of "${valueName}" is out of range. It must be >= ${lowRange} and <= ${highRange}. Received ${value}`)}}checkValue(value,low,high){this.checkRanged(value,"value",low,high)}checkOffset(offset){if(offset){this.checkRanged(offset,"offset",0,this.buffer.length-1)}}writeInt8(value,offset){this.checkValue(value,-128,127);this.checkOffset(offset);this.dataView.setInt8(offset,value);return this}writeUInt8(value,offset){this.checkValue(value,0,255);this.checkOffset(offset);this.dataView.setUint8(offset,value);return this}writeInt16LE(value,offset){this.checkValue(value,-32768,32767);this.checkOffset(offset);this.dataView.setInt16(offset,value,true);return this}writeUInt16LE(value,offset){this.checkValue(value,0,65535);this.checkOffset(offset);this.dataView.setUint16(offset,value,true);return this}writeInt32LE(value,offset){this.checkValue(value,-2147483648,2147483647);this.checkOffset(offset);this.dataView.setInt32(offset,value,true);return this}writeUInt32LE(value,offset){this.checkValue(value,0,4294967295);this.checkOffset(offset);this.dataView.setUint32(offset,value,true);return this}writeBigInt64LE(value,offset){if(typeof value==="number"){value=BigInt(value)}this.checkValue(value,-9223372036854775808n,9223372036854775808n);this.checkOffset(offset);this.dataView.setBigInt64(offset,value,true);return this}writeBigUint64LE(value,offset){if(typeof value==="number"){value=BigInt(value)}this.checkValue(value,0n,18446744073709551616n);this.checkOffset(offset);this.dataView.setBigUint64(offset,value,true);return this}writeFloatLE(value,offset){this.checkOffset(offset);this.dataView.setFloat32(offset,value,true);return this}writeDoubleLE(value,offset){this.checkOffset(offset);this.dataView.setFloat64(offset,value,true);return this}writeInt16BE(value,offset){this.checkValue(value,-32768,32767);this.checkOffset(offset);this.dataView.setInt16(offset,value,false);return this}writeUInt16BE(value,offset){this.checkValue(value,0,65535);this.checkOffset(offset);this.dataView.setUint16(offset,value,false);return this}writeInt32BE(value,offset){this.checkValue(value,-2147483648,2147483647);this.checkOffset(offset);this.dataView.setInt32(offset,value,false);return this}writeUInt32BE(value,offset){this.checkValue(value,0,4294967295);this.checkOffset(offset);this.dataView.setUint32(offset,value,false);return this}writeBigInt64BE(value,offset){if(typeof value==="number"){value=BigInt(value)}this.checkValue(value,-(2n**63n),2n**63n);this.checkOffset(offset);this.dataView.setBigInt64(offset,value,false);return this}writeBigUint64BE(value,offset){if(typeof value==="number"){value=BigInt(value)}this.checkValue(value,0n,2n**64n);this.checkOffset(offset);this.dataView.setBigUint64(offset,value,false);return this}writeFloatBE(value,offset){this.checkOffset(offset);this.dataView.setFloat32(offset,value,false);return this}writeDoubleBE(value,offset){this.checkOffset(offset);this.dataView.setFloat64(offset,value,false);return this}readInt8(offset){this.checkOffset(offset);return this.dataView.getInt8(offset)}readUInt8(offset){this.checkOffset(offset);return this.dataView.getUint8(offset)}readInt16LE(offset){this.checkOffset(offset);return this.dataView.getInt16(offset,true)}readUInt16LE(offset){this.checkOffset(offset);return this.dataView.getUint16(offset,true)}readInt32LE(offset){this.checkOffset(offset);return this.dataView.getInt32(offset,true)}readUInt32LE(offset){this.checkOffset(offset);return this.dataView.getUint32(offset,true)}readBigInt64LE(offset){this.checkOffset(offset);return this.dataView.getBigInt64(offset,true)}readBigUint64LE(offset){this.checkOffset(offset);return this.dataView.getBigUint64(offset,true)}readFloatLE(offset){this.checkOffset(offset);return this.dataView.getFloat32(offset,true)}readDoubleLE(offset){this.checkOffset(offset);return this.dataView.getFloat64(offset,true)}readInt16BE(offset){this.checkOffset(offset);return this.dataView.getInt16(offset,false)}readUInt16BE(offset){this.checkOffset(offset);return this.dataView.getUint16(offset,false)}readInt32BE(offset){this.checkOffset(offset);return this.dataView.getInt32(offset,false)}readUInt32BE(offset){this.checkOffset(offset);return this.dataView.getUint32(offset,false)}readBigInt64BE(offset){this.checkOffset(offset);return this.dataView.getBigInt64(offset,false)}readBigUint64BE(offset){this.checkOffset(offset);return this.dataView.getBigUint64(offset,false)}readFloatBE(offset){this.checkOffset(offset);return this.dataView.getFloat32(offset,false)}readDoubleBE(offset){this.checkOffset(offset);return this.dataView.getFloat64(offset,false)}}BrowserBuffer.poolSize=8192;function getBufferClass(){if(typeof Buffer==="undefined"){return BrowserBuffer}else{return Buffer}}class ReaderBase{constructor(buffer){this.buffer=buffer;this.offset=0}get readOffset(){return this.offset}get length(){return this.buffer.length}readBuffer(bytes){const value=this.buffer.subarray(this.offset,this.offset+bytes);this.offset+=bytes;return value}readUint8Array(bytes){const croppedBuffer=this.readBuffer(bytes);const newArray=new Uint8Array(croppedBuffer.length);for(let i=0;i<croppedBuffer.length;i++){newArray[i]=croppedBuffer[i]}return newArray}readByte(){const value=this.buffer.readInt8(this.offset);this.offset++;return value}readUByte(){const value=this.buffer.readUInt8(this.offset);this.offset++;return value}readBool(){return Boolean(this.readUByte())}readShortString(){const length=this.readUByte();let text="";for(let i=0;i<length;i++){text+=String.fromCharCode(this.readUByte())}return text}readBytesAsString(bytesToRead){let text="";for(let i=0;i<bytesToRead;i++){text+=String.fromCharCode(this.readUByte())}return text}}class WriterBase{constructor(size=0){this.buffer=getBufferClass().alloc(size);this.offset=0;this.resizable=size===0}get writeOffset(){return this.offset}get length(){return this.buffer.length}toBuffer(){return this.buffer.buffer.buffer}toString(){return this.buffer.toString()}writeBuffer(buffer){this.buffer=getBufferClass().concat([this.buffer,buffer],this.buffer.length+buffer.length);return this}writeUint8Array(array){this.writeBuffer(getBufferClass().from(array));return this}writeByte(value){if(this.resizable){const buffer=getBufferClass().alloc(1);buffer.writeInt8(value);this.writeBuffer(buffer)}else{this.buffer.writeInt8(value,this.offset);this.offset++}return this}writeUByte(value){if(this.resizable){const buffer=getBufferClass().alloc(1);buffer.writeUInt8(value);this.writeBuffer(buffer)}else{this.buffer.writeUInt8(value,this.offset);this.offset++}return this}writeBool(value){if(typeof value==="number"){value=Boolean(value)}this.writeUByte(value?1:0);return this}writeStringAsBytes(text){let buffer;if(this.resizable){buffer=getBufferClass().alloc(text.length)}else{buffer=this.buffer}for(let i=0;i<text.length;i++){buffer.writeUInt8(text.charCodeAt(i),i)}return this}}var Endian;(function(Endian){Endian[Endian["LE"]=0]="LE";Endian[Endian["BE"]=1]="BE"})(Endian||(Endian={}));function createReader(endianness,buffer){if(endianness===Endian.LE){return new ReaderLE(buffer)}else{return new ReaderBE(buffer)}}function createWriter(endianness,size){if(endianness===Endian.LE){return new WriterLE(size)}else{return new WriterBE(size)}}class ReaderBE extends ReaderBase{readShort(){const value=this.buffer.readInt16BE(this.offset);this.offset+=2;return value}readUShort(){const value=this.buffer.readUInt16BE(this.offset);this.offset+=2;return value}readInt(){const value=this.buffer.readInt32BE(this.offset);this.offset+=4;return value}readUInt(){const value=this.buffer.readUInt32BE(this.offset);this.offset+=4;return value}readLong(){const value=this.buffer.readBigInt64BE(this.offset);this.offset+=8;return value}readULong(){const value=this.buffer.readBigUint64BE(this.offset);this.offset+=8;return value}readFloat(){const value=this.buffer.readFloatBE(this.offset);this.offset+=4;return value}readDouble(){const value=this.buffer.readDoubleBE(this.offset);this.offset+=8;return value}readString(){const length=this.readUShort();let text="";for(let i=0;i<length;i++){text+=String.fromCharCode(this.readUByte())}return text}readString16(){const length=this.readUShort();let text="";for(let i=0;i<length;i++){text+=String.fromCharCode(this.readUShort())}return text}readShortsAsString(shortsToRead){let text="";for(let i=0;i<shortsToRead;i++){text+=String.fromCharCode(this.readUShort())}return text}}class ReaderLE extends ReaderBase{readShort(){const value=this.buffer.readInt16LE(this.offset);this.offset+=2;return value}readUShort(){const value=this.buffer.readUInt16LE(this.offset);this.offset+=2;return value}readInt(){const value=this.buffer.readInt32LE(this.offset);this.offset+=4;return value}readUInt(){const value=this.buffer.readUInt32LE(this.offset);this.offset+=4;return value}readLong(){const value=this.buffer.readBigInt64LE(this.offset);this.offset+=8;return value}readULong(){const value=this.buffer.readBigUint64LE(this.offset);this.offset+=8;return value}readFloat(){const value=this.buffer.readFloatLE(this.offset);this.offset+=4;return value}readDouble(){const value=this.buffer.readDoubleLE(this.offset);this.offset+=8;return value}readString(){const length=this.readUShort();let text="";for(let i=0;i<length;i++){text+=String.fromCharCode(this.readUByte())}return text}readString16(){const length=this.readUShort();let text="";for(let i=0;i<length;i++){text+=String.fromCharCode(this.readUShort())}return text}readShortsAsString(shortsToRead){let text="";for(let i=0;i<shortsToRead;i++){text+=String.fromCharCode(this.readUShort())}return text}}class WriterBE extends WriterBase{writeShort(value){if(this.resizable){const buffer=getBufferClass().alloc(2);buffer.writeInt16BE(value);this.writeBuffer(buffer)}else{this.buffer.writeInt16BE(value,this.offset);this.offset+=2}return this}writeUShort(value){if(this.resizable){const buffer=getBufferClass().alloc(2);buffer.writeUInt16BE(value);this.writeBuffer(buffer)}else{this.buffer.writeUInt16BE(value,this.offset);this.offset+=2}return this}writeInt(value){if(this.resizable){const buffer=getBufferClass().alloc(4);buffer.writeInt32BE(value);this.writeBuffer(buffer)}else{this.buffer.writeInt32BE(value,this.offset);this.offset+=4}return this}writeUInt(value){if(this.resizable){const buffer=getBufferClass().alloc(4);buffer.writeUInt32BE(value);this.writeBuffer(buffer)}else{this.buffer.writeUInt32BE(value,this.offset);this.offset+=4}return this}writeLong(value){if(typeof value!=="bigint"){value=BigInt(value)}if(this.resizable){const buffer=getBufferClass().alloc(8);buffer.writeBigInt64BE(value);this.writeBuffer(buffer)}else{this.buffer.writeBigInt64BE(value,this.offset);this.offset+=8}return this}writeULong(value){if(typeof value!=="bigint"){value=BigInt(value)}if(this.resizable){const buffer=getBufferClass().alloc(8);buffer.writeBigUint64BE(value);this.writeBuffer(buffer)}else{this.buffer.writeBigUint64BE(value,this.offset);this.offset+=8}return this}writeFloat(value){if(this.resizable){const buffer=getBufferClass().alloc(4);buffer.writeFloatBE(value);this.writeBuffer(buffer)}else{this.buffer.writeFloatBE(value,this.offset);this.offset+=4}return this}writeDouble(value){if(this.resizable){const buffer=getBufferClass().alloc(8);buffer.writeDoubleBE(value);this.writeBuffer(buffer)}else{this.buffer.writeDoubleBE(value,this.offset);this.offset+=8}return this}writeShortString(text){this.writeUByte(text.length);for(let i=0;i<text.length;i++){this.writeUByte(text.charCodeAt(i))}return this}writeString(text){this.writeUShort(text.length);for(let i=0;i<text.length;i++){this.writeUByte(text.charCodeAt(i))}return this}writeString16(text){this.writeUShort(text.length);for(let i=0;i<text.length;i++){this.writeUShort(text.charCodeAt(i))}return this}writeStringAsShorts(text){let buffer;if(this.resizable){buffer=getBufferClass().alloc(text.length*2)}else{buffer=this.buffer}for(let i=0;i<text.length;i++){buffer.writeUint16BE(text.charCodeAt(i),i)}return this}}class WriterLE extends WriterBase{writeShort(value){if(this.resizable){const buffer=getBufferClass().alloc(2);buffer.writeInt16LE(value);this.writeBuffer(buffer)}else{this.buffer.writeInt16LE(value,this.offset);this.offset+=2}return this}writeUShort(value){if(this.resizable){const buffer=getBufferClass().alloc(2);buffer.writeUInt16LE(value);this.writeBuffer(buffer)}else{this.buffer.writeUInt16LE(value,this.offset);this.offset+=2}return this}writeInt(value){if(this.resizable){const buffer=getBufferClass().alloc(4);buffer.writeInt32LE(value);this.writeBuffer(buffer)}else{this.buffer.writeInt32LE(value,this.offset);this.offset+=4}return this}writeUInt(value){if(this.resizable){const buffer=getBufferClass().alloc(4);buffer.writeUInt32LE(value);this.writeBuffer(buffer)}else{this.buffer.writeUInt32LE(value,this.offset);this.offset+=4}return this}writeLong(value){if(typeof value!=="bigint"){value=BigInt(value)}if(this.resizable){const buffer=getBufferClass().alloc(8);buffer.writeBigInt64LE(value);this.writeBuffer(buffer)}else{this.buffer.writeBigInt64LE(value,this.offset);this.offset+=8}return this}writeULong(value){if(typeof value!=="bigint"){value=BigInt(value)}if(this.resizable){const buffer=getBufferClass().alloc(8);buffer.writeBigUint64LE(value);this.writeBuffer(buffer)}else{this.buffer.writeBigUint64LE(value,this.offset);this.offset+=8}return this}writeFloat(value){if(this.resizable){const buffer=getBufferClass().alloc(4);buffer.writeFloatLE(value);this.writeBuffer(buffer)}else{this.buffer.writeFloatLE(value,this.offset);this.offset+=4}return this}writeDouble(value){if(this.resizable){const buffer=getBufferClass().alloc(8);buffer.writeDoubleLE(value);this.writeBuffer(buffer)}else{this.buffer.writeDoubleLE(value,this.offset);this.offset+=8}return this}writeShortString(text){this.writeUByte(text.length);for(let i=0;i<text.length;i++){this.writeUByte(text.charCodeAt(i))}return this}writeString(text){this.writeUShort(text.length);for(let i=0;i<text.length;i++){this.writeUByte(text.charCodeAt(i))}return this}writeString16(text){this.writeUShort(text.length);for(let i=0;i<text.length;i++){this.writeUShort(text.charCodeAt(i))}return this}writeStringAsShorts(text){let buffer;if(this.resizable){buffer=getBufferClass().alloc(text.length*2)}else{buffer=this.buffer}for(let i=0;i<text.length;i++){buffer.writeUint16LE(text.charCodeAt(i),i)}return this}}class FunkyArray{constructor(){this.items=new Map;this.itemKeys=[]}_getKeys(){const keyArray=[];let result;const iterator=this.items.keys();while(!(result=iterator.next()).done){keyArray.push(result.value)}return keyArray}set(key,item,regenerate=true){this.items.set(key,item);if(regenerate){this.itemKeys=this._getKeys()}return item}remove(key,regenerate=true){const success=this.items.delete(key);if(regenerate){this.itemKeys=this._getKeys()}return success}removeFirst(regenerate=true){const success=this.items.delete(this.items.keys().next().value);if(regenerate){this.itemKeys=this._getKeys()}return success}first(){return this.items.values().next().value}get length(){return this.items.size}get(key){return this.items.get(key)}has(key){return this.itemKeys.includes(key)}get keys(){return this.itemKeys}forEach(callback){return new Promise((async(resolve,reject)=>{if(this.items.size===0){return resolve(true)}try{const iterator=this.items.values();let result;while(!(result=iterator.next()).done){await callback(result.value)}resolve(true)}catch(e){reject(e)}}))}}
|
|
|
|
const MessageType = {
|
|
KeepAlive: 0,
|
|
ClientDetails: 1,
|
|
CursorPos: 2,
|
|
ClientJoined: 3,
|
|
Clients: 4,
|
|
ClientLeft: 5,
|
|
Ping: 6,
|
|
GroupData: 7,
|
|
HonkShoe: 8,
|
|
BadgeUnlock: 9,
|
|
DEBUG_UnlockAllBadges: 10,
|
|
TooOutOfDate: 11
|
|
};
|
|
|
|
let cssCursor = "";
|
|
let cssHoverCursor = "";
|
|
let pageLoadCompleted = true;
|
|
|
|
console.log("[MP] Injecting custom styles...");
|
|
const styles = document.createElement("style");
|
|
function updateStyles() {
|
|
let cursorImageI = window.getComputedStyle(document.body).cursor;
|
|
cssCursor = `${cursorImageI === "auto" || !cursorImageI.includes("url") ? `url(${SITE_DEFAULT_CURSOR[window.location.href.split("//")[1].split("/")[0].split(":")[0]]}) 11 11, auto` : cursorImageI}`;
|
|
if (cssCursor.includes("undefined")) {
|
|
cssCursor = "auto";
|
|
}
|
|
let hoverChangeElement = document.querySelector("a");
|
|
if (!hoverChangeElement) {
|
|
hoverChangeElement = document.querySelector(".probedata");
|
|
}
|
|
if (!hoverChangeElement) {
|
|
hoverChangeElement = document.createElement("a");
|
|
hoverChangeElement.innerText = "css test element";
|
|
hoverChangeElement.id = "MULTIPROBE_CSS_TEST_ELEMENT";
|
|
hoverChangeElement.style = "display:none";
|
|
document.body.appendChild(hoverChangeElement);
|
|
}
|
|
let cursorHoverImageI = window.getComputedStyle(hoverChangeElement).cursor;
|
|
cssHoverCursor = `${cursorHoverImageI === "auto" || !cursorHoverImageI.includes("url") ? `url(${SITE_DEFAULT_HOVER_CURSOR[window.location.href.split("//")[1].split("/")[0].split(":")[0]]}) 11 11, auto` : cursorHoverImageI}`;
|
|
if (cssHoverCursor.includes("undefined")) {
|
|
cssHoverCursor = "auto";
|
|
}
|
|
|
|
styles.innerHTML = `
|
|
html {
|
|
${pageLoadCompleted ? `cursor: ${cssCursor};` : ""}
|
|
}
|
|
|
|
#otherCursors {
|
|
position: absolute;
|
|
top:0px;
|
|
left:0px;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 999999999;
|
|
pointer-events: none;
|
|
overflow:hidden;
|
|
text-shadow: none!important;
|
|
font-family: Arial,sans-serif;
|
|
}
|
|
|
|
#otherCursors img {
|
|
image-rendering: pixelated;
|
|
}
|
|
|
|
@keyframes ping {
|
|
0% {
|
|
scale: 0;
|
|
opacity: 1;
|
|
}
|
|
|
|
100% {
|
|
scale: 2;
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
.ping {
|
|
opacity: 0;
|
|
position: absolute;
|
|
animation: ping 2s linear;
|
|
animation-iteration-count: 1;
|
|
image-rendering: pixelated;
|
|
background: url("https://eusv.net/MA7cm1BBelFHDP");
|
|
width: 64px;
|
|
height: 64px;
|
|
z-index: -1!important;
|
|
}
|
|
|
|
.eepy {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
width: 64px;
|
|
height: 64px;
|
|
image-rendering: pixelated;
|
|
background: url("https://eusv.net/9ZU5r29lg1yKIi");
|
|
z-index: 1;
|
|
transform: translate(60px, -60px);
|
|
}
|
|
|
|
.grouphidden {
|
|
right: -12rem!important;
|
|
}
|
|
|
|
.groupui {
|
|
position: fixed;
|
|
top: calc(2rem + 6px);
|
|
right: 0px;
|
|
height: 25rem;
|
|
width: 12rem;
|
|
background-color: black;
|
|
color: white;
|
|
z-index:9999998;
|
|
text-shadow: none!important;
|
|
font-family: Arial,sans-serif;
|
|
font-size: unset !important;
|
|
transition: right .16s ease-in-out;
|
|
}
|
|
|
|
.groupui-popper {
|
|
position: absolute;
|
|
top: -2px;
|
|
left: -1.5rem;
|
|
width: 1rem;
|
|
height: calc(100% - 6px);
|
|
opacity: 0.25;
|
|
transition: opacity .16s ease-in-out;
|
|
background-color: black;
|
|
border: 1px solid white;
|
|
color: white;
|
|
padding: .125rem;
|
|
line-height: 380px;
|
|
}
|
|
|
|
.groupui:not(.grouphidden) .groupui-popper, .groupui-popper:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
.groupui-title {
|
|
position: absolute;
|
|
top: 0px;
|
|
left: 0px;
|
|
width: 100%;
|
|
text-align: center;
|
|
height: 2rem;
|
|
line-height: 2rem;
|
|
background-color: rgba(255,255,255,0.1);
|
|
overflow:hidden;
|
|
}
|
|
|
|
.groupui-scrollbox {
|
|
position: absolute;
|
|
top: 2rem;
|
|
left: 0px;
|
|
width: 100%;
|
|
height: calc(100% - 2rem);
|
|
overflow-y: scroll;
|
|
font-family: Arial,sans-serif;
|
|
font-size: unset !important;
|
|
}
|
|
|
|
.groupui-user {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 2rem;
|
|
background-color: rgba(255,255,255,0.2);
|
|
}
|
|
|
|
.groupui-user:nth-child(2n) {
|
|
background-color: rgba(255,255,255,0.15);
|
|
}
|
|
|
|
.groupui-user mp_text {
|
|
position: absolute;
|
|
line-height: 2rem;
|
|
margin: 0;
|
|
margin-left: .5rem;
|
|
}
|
|
|
|
.groupui-user .buttons {
|
|
position: absolute;
|
|
height: 100%;
|
|
top: 0px;
|
|
right: 0px;
|
|
margin-right: .5rem;
|
|
}
|
|
|
|
.groupui-user .buttons button {
|
|
margin-left: .5rem;
|
|
margin-top: .2rem;
|
|
}
|
|
|
|
kbd {
|
|
background-color: #eee;
|
|
border-radius: 3px;
|
|
border: 1px solid #b4b4b4;
|
|
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 2px 0 0 rgba(255, 255, 255, 0.7) inset;
|
|
color: #333;
|
|
display: inline-block;
|
|
font-size: 0.85em;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
padding: 2px 4px;
|
|
white-space: nowrap;
|
|
font-family: Arial,sans-serif;
|
|
font-size: unset !important;
|
|
}
|
|
|
|
.mplink, .mplink:visited {
|
|
color: #d2738a;
|
|
}
|
|
|
|
.mplink:hover {
|
|
color: #c1b492;
|
|
}
|
|
|
|
@keyframes badgepop {
|
|
0% {
|
|
transform: translate(-50%, -125%);
|
|
}
|
|
|
|
100% {
|
|
transform: translate(-50%, 0%);
|
|
}
|
|
}
|
|
|
|
@keyframes badgepopout {
|
|
0% {
|
|
transform: translate(-50%, 0%);
|
|
}
|
|
|
|
100% {
|
|
transform: translate(-50%, -125%);
|
|
}
|
|
}
|
|
|
|
.badgepopout {
|
|
transform: translate(-50%, -125%);
|
|
visibility: visible!important;
|
|
animation: badgepopout .35s ease-in-out !important;
|
|
}
|
|
|
|
.badgepopin {
|
|
transform: translate(-50%, 0%);
|
|
visibility: visible!important;
|
|
animation: badgepop .35s ease-in-out !important;
|
|
animation-iteration-count: 1;
|
|
}
|
|
|
|
.badgepopcreate {
|
|
transform: translate(-50%, -125%);
|
|
}
|
|
|
|
.badgepop {
|
|
position: absolute;
|
|
margin-top: .5rem;
|
|
visibility: hidden;
|
|
width: calc(15.0rem + 4vw);
|
|
background-color: black;
|
|
text-align: start!important;
|
|
padding: .5rem;
|
|
border: 1px solid white;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.badgepop mp_col_auto {
|
|
padding-right: 0;
|
|
}
|
|
|
|
.badgepop mp_text {
|
|
font-size: calc(0.5rem + 0.1vw);
|
|
color: white;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
mp_text {
|
|
color: white;
|
|
display: block;
|
|
}
|
|
|
|
mp_container {
|
|
display: block;
|
|
}
|
|
|
|
mp_row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
margin-top: calc(-1 * 0);
|
|
margin-right: calc(-0.5 * 1.5rem);
|
|
margin-left: calc(-0.5 * 1.5rem);
|
|
}
|
|
|
|
mp_row > * {
|
|
flex-shrink: 0;
|
|
width: 100%;
|
|
max-width: 100%;
|
|
padding-right: calc(1.5rem * 0.5);
|
|
padding-left: calc(1.5rem * 0.5);
|
|
margin-top: 0;
|
|
}
|
|
|
|
mp_col {
|
|
flex: 1 0 0%;
|
|
}
|
|
|
|
mp_col_auto {
|
|
flex: 0 0 auto;
|
|
width: auto;
|
|
}
|
|
|
|
.badgepop * {
|
|
text-align: unset!important;
|
|
text-shadow: none!important;
|
|
font-family: Arial,sans-serif;
|
|
}
|
|
|
|
mp_container a {
|
|
cursor: ${cssHoverCursor};
|
|
}
|
|
|
|
mp_container ul {
|
|
/* base t00 ul style */
|
|
background: #d2738a!important;
|
|
margin: 40px 10px 10px 10px!important;
|
|
padding: 0px 0px 0px 0px!important;
|
|
width: 800px!important;
|
|
box-shadow: 0px 5px 10px #000000!important;
|
|
line-height: 30px!important;
|
|
/* ravarcheon fixes */
|
|
position: relative!important;
|
|
left: unset!important;
|
|
transform: unset!important;
|
|
}
|
|
|
|
mp_container ul li {
|
|
background: #000000!important;
|
|
margin: 0px 0px 2px 0px!important;
|
|
padding: 20px 20px 20px 20px!important;
|
|
list-style: none!important;
|
|
/* ravarcheon fixes */
|
|
text-shadow: unset!important;
|
|
}
|
|
|
|
mp_group_user {
|
|
display: block;
|
|
}
|
|
|
|
mp_button {
|
|
display: inline-block;
|
|
background-color: black;
|
|
color: white;
|
|
border: 1px solid white;
|
|
text-shadow: none!important;
|
|
font-family: Arial,sans-serif;
|
|
font-size: unset !important;
|
|
padding: 4px 8px;
|
|
margin: 2px;
|
|
text-align: center;
|
|
cursor: ${cssHoverCursor};
|
|
}
|
|
|
|
.out-of-the-way {
|
|
opacity: 0.25;
|
|
transition: opacity .16s ease-in-out;
|
|
}
|
|
|
|
.out-of-the-way:hover {
|
|
opacity: 1;
|
|
}
|
|
`.split("\n").join("").split("\r").join("").split("\t").join("");
|
|
}
|
|
|
|
// Cursed way of detecting heavenonline load
|
|
if (window.location.href.split("://")[1].split("/")[0] === "heavenonline.xyz") {
|
|
pageLoadCompleted = false;
|
|
const oldConsoleLog = console.log;
|
|
console.log = function(...args) {
|
|
if (args.length >= 1) {
|
|
if (args[0].trim() === `Page is ready at ${finished}.`) {
|
|
console.log = oldConsoleLog;
|
|
console.log("[MP] Reverted console.log override.");
|
|
pageLoadCompleted = true;
|
|
updateStyles();
|
|
}
|
|
}
|
|
|
|
oldConsoleLog(...args);
|
|
}
|
|
} else {
|
|
updateStyles();
|
|
}
|
|
|
|
document.documentElement.appendChild(styles);
|
|
|
|
if (!localStorage["mpconnectonload"]) {
|
|
localStorage["mpconnectonload"] = true;
|
|
}
|
|
|
|
const otherCursors = document.createElement("mp_cursors");
|
|
otherCursors.id = "otherCursors";
|
|
document.documentElement.appendChild(otherCursors);
|
|
|
|
const groupUIBase = document.createElement("mp_party");
|
|
groupUIBase.style = "display:none";
|
|
groupUIBase.classList.add("groupui");
|
|
groupUIBase.classList.add("grouphidden");
|
|
const groupPopper = document.createElement("mp_button");
|
|
groupPopper.classList.add("groupui-popper");
|
|
groupPopper.onclick = () => {
|
|
groupUIBase.classList.toggle("grouphidden");
|
|
groupPopper.innerText = groupUIBase.classList.contains("grouphidden") ? "<" : ">";
|
|
};
|
|
groupPopper.innerText = "<";
|
|
groupUIBase.appendChild(groupPopper);
|
|
const groupTitle = document.createElement("mp_container");
|
|
groupTitle.classList.add("groupui-title");
|
|
groupTitle.innerText = "GROUP_NAME";
|
|
groupUIBase.appendChild(groupTitle);
|
|
const groupUsers = document.createElement("mp_container");
|
|
groupUsers.classList.add("groupui-scrollbox");
|
|
groupUIBase.appendChild(groupUsers);
|
|
document.documentElement.appendChild(groupUIBase);
|
|
|
|
const popupRoot = document.createElement("mp_badge_container");
|
|
popupRoot.style = "position:fixed;left:50%;top:0;z-index:9999998";
|
|
document.documentElement.appendChild(popupRoot);
|
|
|
|
const popupSound = new Audio("https://eusv.net/kdHl7VYI1XMS4c");
|
|
document.documentElement.appendChild(popupSound);
|
|
|
|
function createGroupUser(username, location) {
|
|
const user = document.createElement("mp_group_user");
|
|
user.classList.add("groupui-user");
|
|
const usernameBox = document.createElement("mp_text");
|
|
usernameBox.innerText = username;
|
|
user.appendChild(usernameBox);
|
|
const buttonBox = document.createElement("mp_container");
|
|
buttonBox.classList.add("buttons");
|
|
user.appendChild(buttonBox);
|
|
|
|
const gotoButton = document.createElement("mp_button");
|
|
gotoButton.innerText = "Go To";
|
|
const gotoLoc = location;
|
|
gotoButton.onclick = () => {
|
|
window.location.href = `https://${gotoLoc}`;
|
|
}
|
|
buttonBox.appendChild(gotoButton);
|
|
|
|
groupUsers.appendChild(user);
|
|
}
|
|
|
|
let clientWidth = document.documentElement.getBoundingClientRect().width;
|
|
let bodyBoundingRect = document.documentElement.getBoundingClientRect();
|
|
|
|
setInterval(() => {
|
|
bodyBoundingRect = document.documentElement.getBoundingClientRect();
|
|
if (document.documentElement.scrollHeight > window.innerHeight) {
|
|
otherCursors.style = `width:${clientWidth = bodyBoundingRect.width}px;height:${document.documentElement.scrollHeight}px;cursor: ${cssCursor};`;
|
|
} else {
|
|
otherCursors.style = `width:${clientWidth = bodyBoundingRect.width}px;height:${window.innerHeight}px;cursor: ${cssCursor};`;
|
|
}
|
|
}, 1000);
|
|
|
|
const debugMessageContainer = document.createElement("mp_container");
|
|
debugMessageContainer.style = "position:fixed;top:0;left:0;padding:8px;margin:4px;pointer-events:none;text-shadow: 0px 0px 4px black;z-index:999999999999;background-color:rgba(0,0,0,0.5);outline:1px solid white;backdrop-filter:blur(6px)";
|
|
if (!window.multiprobe_debug) {
|
|
debugMessageContainer.style.display = "none";
|
|
}
|
|
|
|
const debugText = document.createElement("mp_text");
|
|
debugText.innerText = "MultiProbe DEBUG";
|
|
debugText.style.color = "rgb(255, 200, 200)";
|
|
|
|
const debugConnectedTo = document.createElement("mp_text");
|
|
debugConnectedTo.innerText = "Disconnected";
|
|
|
|
const debugAuthedAs = document.createElement("mp_text");
|
|
debugAuthedAs.innerText = "Not Authenticated";
|
|
|
|
const debugPing = document.createElement("mp_text");
|
|
debugPing.innerText = "Ping: Calculating...";
|
|
|
|
debugMessageContainer.appendChild(debugText);
|
|
debugMessageContainer.appendChild(debugConnectedTo);
|
|
debugMessageContainer.appendChild(debugAuthedAs);
|
|
debugMessageContainer.appendChild(debugPing);
|
|
document.documentElement.appendChild(debugMessageContainer);
|
|
|
|
let needsToUpdate = false;
|
|
console.log("[MP] Checking for new versions...");
|
|
const versionFetchAddress = windowLocation.replace("127.0.0.1", "localhost").includes("//localhost:") ? "http://localhost:38194/api/version" : "https://multiprobe.eusv.net/api/version";
|
|
fetch(versionFetchAddress, { method: "post" }).then(response => {
|
|
response.text().then(versionNumberRaw => {
|
|
const versionNumber = parseInt(versionNumberRaw);
|
|
if (versionNumber > USERSCRIPT_VERSION) {
|
|
// We're out of date >:(
|
|
needsToUpdate = true;
|
|
createUpdateDialog(`${versionNumberRaw.slice(0, versionNumberRaw.length - 1)}.${versionNumberRaw.slice(-1)}`);
|
|
console.log("[MP] We're out of date :(");
|
|
} else {
|
|
console.log("[MP] We're up to date!");
|
|
}
|
|
});
|
|
});
|
|
|
|
const keepAlivePacket = createWriter(Endian.LE, 1).writeUByte(MessageType.KeepAlive).toBuffer();
|
|
|
|
let remoteClients = new FunkyArray();
|
|
|
|
let ws;
|
|
let ready = false;
|
|
let currentMouseX = 0;
|
|
let currentMouseY = 0;
|
|
let oldMouseX = 0;
|
|
let oldMouseY = 0;
|
|
let lastSendTime = 0;
|
|
|
|
function unlockAllBadges() {
|
|
if (ws) {
|
|
ws.send(createWriter(Endian.LE, 1).writeUByte(MessageType.DEBUG_UnlockAllBadges).toBuffer());
|
|
}
|
|
}
|
|
window.unlockAllBadges = unlockAllBadges;
|
|
|
|
class RemoteClient {
|
|
constructor(name, startAfk, isSelfCursor = false) {
|
|
this.name = name;
|
|
this.element = document.createElement("mp_cursor");
|
|
this.element.style.position = "absolute";
|
|
this.element.style.transform = `translate(-50%, -50%)`;
|
|
const image = new Image();
|
|
let cursorImage = window.getComputedStyle(document.body).cursor.replace("url(","").split('"').join("").split("'").join("").split(")")[0];
|
|
if (cursorImage === "auto") {
|
|
cursorImage = "https://angusnicneven.com/cursor/rrw.png";
|
|
}
|
|
image.src = cursorImage;
|
|
this.probeImage = image;
|
|
this.element.appendChild(image);
|
|
const clientName = document.createElement("mp_container");
|
|
clientName.style = "position:absolute;left:100%;top:100%;background-color:black;padding:4px 8px;color:white;font-size:12px;font-family:Arial,sans-serif;";
|
|
clientName.innerText = name;
|
|
this.element.appendChild(clientName);
|
|
if (!isSelfCursor) {
|
|
this.eepy = document.createElement("mp_eepy");
|
|
this.eepy.className = "eepy";
|
|
this.eepy.style = startAfk ? "" : "display:none";
|
|
this.element.appendChild(this.eepy);
|
|
}
|
|
otherCursors.appendChild(this.element);
|
|
this.targetX = 0;
|
|
this.targetY = 0;
|
|
this.actualX = 0;
|
|
this.actualY = 0;
|
|
this.oldActualX = 0;
|
|
this.oldActualY = 0;
|
|
this.hasBeenMoved = false;
|
|
this.element.visibility = "hidden";
|
|
}
|
|
|
|
setAfk(afkState) {
|
|
this.eepy.style = afkState ? "" : "display:none";
|
|
}
|
|
|
|
rawSetPos(x, y) {
|
|
if (!this.hasBeenMoved) {
|
|
this.element.visibility = "";
|
|
this.hasBeenMoved = true;
|
|
}
|
|
|
|
this.targetX = Math.round(x * clientWidth);
|
|
this.targetY = y;
|
|
}
|
|
|
|
rawSetPosInit(x, y) {
|
|
if (!this.hasBeenMoved) {
|
|
this.element.visibility = "";
|
|
this.hasBeenMoved = true;
|
|
}
|
|
|
|
this.actualX = this.targetX = Math.round(x * clientWidth);
|
|
this.actualY = this.targetY = y;
|
|
}
|
|
|
|
updateCursor() {
|
|
const x = Math.round(this.actualX);
|
|
const y = Math.round(this.actualY);
|
|
if (y !== this.oldActualY) {
|
|
this.element.style.top = `${y}px`;
|
|
this.oldActualY = y;
|
|
}
|
|
if (x !== this.oldActualX) {
|
|
this.element.style.left = `${x}px`;
|
|
this.oldActualX = x;
|
|
}
|
|
}
|
|
}
|
|
|
|
let selfCursor;
|
|
|
|
function createPing(x, y) {
|
|
const pingDiv = document.createElement("mp_ping");
|
|
pingDiv.className = "ping";
|
|
pingDiv.style.top = `${y}px`;
|
|
pingDiv.style.left = `${x}px`;
|
|
otherCursors.appendChild(pingDiv);
|
|
setTimeout(() => pingDiv.remove(), 2000);
|
|
}
|
|
|
|
function removeClient(id) {
|
|
if (remoteClients.has(id)) {
|
|
const client = remoteClients.get(id);
|
|
remoteClients.remove(id);
|
|
client.element.remove();
|
|
}
|
|
}
|
|
|
|
let isAfkLocal = false;
|
|
let lastSendAfkState = false
|
|
|
|
window.onfocus = (e) => {
|
|
isAfkLocal = false;
|
|
}
|
|
|
|
window.onblur = (e) => {
|
|
isAfkLocal = true;
|
|
}
|
|
|
|
let rawMouseX = 0;
|
|
let rawMouseY = 0;
|
|
|
|
window.onmousemove = (e) => {
|
|
currentMouseX = (rawMouseX = e.clientX) + window.scrollX;
|
|
currentMouseY = (rawMouseY = e.clientY) + window.scrollY;
|
|
if (selfCursor) {
|
|
selfCursor.rawSetPosInit(currentMouseX / clientWidth, currentMouseY);
|
|
selfCursor.updateCursor();
|
|
}
|
|
}
|
|
|
|
window.onscroll = () => {
|
|
bodyBoundingRect = document.documentElement.getBoundingClientRect();
|
|
currentMouseX = rawMouseX + window.scrollX;
|
|
currentMouseY = rawMouseY + window.scrollY;
|
|
if (selfCursor) {
|
|
selfCursor.rawSetPosInit(currentMouseX / clientWidth, currentMouseY);
|
|
selfCursor.updateCursor();
|
|
}
|
|
}
|
|
|
|
function log(type, text) {
|
|
const d = new Date();
|
|
console.log(`[hNET] [${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}:${String(d.getSeconds()).padStart(2, "0")}.${String(d.getMilliseconds()).padStart(4, "0")}] [${type}] ${text}`);
|
|
}
|
|
|
|
let allowedPings = 10;
|
|
window.onkeypress = (e) => {
|
|
if (e.key === "p") {
|
|
if (ws && ready) {
|
|
if (allowedPings > 0) {
|
|
allowedPings--;
|
|
ws.send(createWriter(Endian.LE, 9).writeUByte(MessageType.Ping).writeFloat((rawMouseX + window.scrollX - 32) / clientWidth).writeInt(rawMouseY + window.scrollY - 32).toBuffer());
|
|
}
|
|
}
|
|
} else if (e.key === "n") {
|
|
if (ws && ready && selfCursor) {
|
|
localStorage["t00mp_cursorStyle"] = selfCursor.element.style.visibility = selfCursor.element.style.visibility === "hidden" ? "" : "hidden";
|
|
}
|
|
} else if (e.key === "g") {
|
|
if (ws && ready && selfCursor) {
|
|
groupPopper.click();
|
|
}
|
|
}
|
|
}
|
|
|
|
window.onkeydown = (e) => {
|
|
if (!windowContainer && e.key === "F1") {
|
|
e.preventDefault();
|
|
localStorage["mpshowfirsttime"] = "true";
|
|
createFirstTimeDialog();
|
|
return false;
|
|
}
|
|
if (e.key === "F3") {
|
|
e.preventDefault();
|
|
window.multiprobe_debug = !window.multiprobe_debug;
|
|
debugMessageContainer.style.display = window.multiprobe_debug ? "block" : "none";
|
|
return false;
|
|
}
|
|
};
|
|
|
|
function lerp(value1, value2, amount) {
|
|
return value1 + (value2 - value1) * amount;
|
|
}
|
|
|
|
let timeLastFrame = performance.now();
|
|
let frameDeltaTime = 0;
|
|
let timeSinceLastPingReset = performance.now();
|
|
try {
|
|
function animate() {
|
|
frameDeltaTime = (performance.now() - timeLastFrame) * 0.001;
|
|
|
|
if (ws && ready) {
|
|
if (currentMouseX !== oldMouseX || currentMouseY !== oldMouseY) {
|
|
if ((performance.now() - lastSendTime) >= 41) {
|
|
lastSendTime = performance.now();
|
|
oldMouseX = currentMouseX;
|
|
oldMouseY = currentMouseY;
|
|
ws.send(createWriter(Endian.LE, 9).writeUByte(MessageType.CursorPos).writeFloat(oldMouseX / clientWidth).writeInt(currentMouseY).toBuffer());
|
|
}
|
|
}
|
|
if (isAfkLocal !== lastSendAfkState) {
|
|
lastSendAfkState = isAfkLocal;
|
|
ws.send(createWriter(Endian.LE, 2).writeUByte(MessageType.HonkShoe).writeBool(isAfkLocal).toBuffer());
|
|
}
|
|
}
|
|
|
|
if ((performance.now() - timeSinceLastPingReset) >= 1000) {
|
|
allowedPings = 10;
|
|
timeSinceLastPingReset = performance.now();
|
|
}
|
|
|
|
remoteClients.forEach(remoteUser => {
|
|
remoteUser.actualX = lerp(remoteUser.actualX, remoteUser.targetX, 20 * frameDeltaTime);
|
|
remoteUser.actualY = lerp(remoteUser.actualY, remoteUser.targetY, 20 * frameDeltaTime);
|
|
remoteUser.updateCursor();
|
|
});
|
|
|
|
requestAnimationFrame(animate);
|
|
timeLastFrame = performance.now();
|
|
}
|
|
animate();
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
|
|
let windowContainer = null;
|
|
let preventReconnect = false;
|
|
|
|
function doConnect(apiKey) {
|
|
if (preventReconnect) {
|
|
return;
|
|
}
|
|
|
|
const Buffer = getBufferClass();
|
|
|
|
console.log("[MP] Connecting to realtime server...");
|
|
ws = new WebSocket((windowLocation.includes("//localhost:") || window.multiprobe_connectLocal) ? "wss://ws.eusv.net/t00mpdev" : "wss://ws.eusv.net/t00mp");
|
|
let keepAliveInterval;
|
|
let pingStartTime = -1;
|
|
let lastPing = 0;
|
|
ws.onopen = () => {
|
|
console.log("[MP] Connected! Authenticating...");
|
|
debugConnectedTo.innerText = `Connected to ${ws.url}`;
|
|
otherCursors.innerHTML = "";
|
|
selfCursor = new RemoteClient(localStorage["t00mp_username"], false, true);
|
|
selfCursor.probeImage.style.visibility = "hidden";
|
|
selfCursor.element.style.visibility = localStorage["t00mp_cursorStyle"] ?? "hidden";
|
|
selfCursor.hasBeenMoved = true;
|
|
const currentPage = windowLocation.split("/").slice(2).join("/");
|
|
const key = window.ebfohezwACBL8DU8sqvedw ?? "";
|
|
ws.send(keepAlivePacket);
|
|
ws.send(createWriter(Endian.LE, 8 + apiKey.length + currentPage.length + key.length)
|
|
.writeUByte(MessageType.ClientDetails)
|
|
.writeShortString(apiKey)
|
|
.writeString(currentPage)
|
|
.writeUShort(PROTOCOL_VERSION)
|
|
.writeString(key)
|
|
.toBuffer());
|
|
pingStartTime = performance.now();
|
|
keepAliveInterval = setInterval(() => {
|
|
pingStartTime = performance.now();
|
|
ws.send(keepAlivePacket);
|
|
}, 2000);
|
|
}
|
|
|
|
function onMessage(buf) {
|
|
const reader = createReader(Endian.LE, Buffer.from(buf));
|
|
switch (reader.readByte()) {
|
|
case MessageType.KeepAlive:
|
|
{
|
|
lastPing = performance.now() - pingStartTime;
|
|
debugPing.innerText = `Ping: ${lastPing}ms`;
|
|
break;
|
|
}
|
|
case MessageType.Clients:
|
|
{
|
|
const clientCount = reader.readUShort();
|
|
for (let i = 0; i < clientCount; i++) {
|
|
const clientId = reader.readUInt();
|
|
const clientName = reader.readShortString();
|
|
const clientX = reader.readFloat();
|
|
const clientY = reader.readInt();
|
|
const isAfk = reader.readBool();
|
|
remoteClients.set(clientId, new RemoteClient(clientName, isAfk)).rawSetPosInit(clientX, clientY);
|
|
}
|
|
if (window.multiprobe_debug) {
|
|
log("RECV", `Initial client packet, got ${clientCount} clients.`);
|
|
}
|
|
ready = true;
|
|
if (windowContainer) {
|
|
windowContainer.remove();
|
|
windowContainer = null;
|
|
}
|
|
if (!needsToUpdate) {
|
|
createFirstTimeDialog();
|
|
}
|
|
debugAuthedAs.innerText = `Authenticated as ${localStorage["t00mp_username"]}`;
|
|
console.log("[MP] Authenticated!");
|
|
break;
|
|
}
|
|
case MessageType.ClientJoined:
|
|
{
|
|
const clientId = reader.readUInt();
|
|
const clientName = reader.readShortString();
|
|
remoteClients.set(clientId, new RemoteClient(clientName, false));
|
|
if (window.multiprobe_debug) {
|
|
log("RECV", `New client joined page: ${clientName} ID=${clientId}`);
|
|
}
|
|
break;
|
|
}
|
|
case MessageType.CursorPos:
|
|
{
|
|
const clientId = reader.readUInt();
|
|
if (remoteClients.has(clientId)) {
|
|
const cursorX = reader.readFloat();
|
|
const cursorY = reader.readInt();
|
|
remoteClients.get(clientId).rawSetPos(cursorX, cursorY);
|
|
if (window.multiprobe_debug) {
|
|
log("RECV", `Cursor position update for ${clientId}, X=${cursorX}, Y=${cursorY}`);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case MessageType.ClientLeft:
|
|
{
|
|
const clientId = reader.readUInt();
|
|
removeClient(clientId);
|
|
if (window.multiprobe_debug) {
|
|
log("RECV", `Client ${clientId} left or switched pages`);
|
|
}
|
|
break;
|
|
}
|
|
case MessageType.Ping:
|
|
{
|
|
const cursorX = reader.readFloat();
|
|
const cursorY = reader.readInt();
|
|
createPing(cursorX * clientWidth, cursorY);
|
|
if (window.multiprobe_debug) {
|
|
log("RECV", `Got a ping, X=${cursorX}, Y=${cursorY}`);
|
|
}
|
|
break;
|
|
}
|
|
case MessageType.GroupData:
|
|
{
|
|
groupUIBase.style = "";
|
|
groupUsers.innerHTML = "";
|
|
groupTitle.innerText = reader.readShortString();
|
|
const groupUserCount = reader.readUShort();
|
|
if (window.multiprobe_debug) {
|
|
log("RECV", `Server sent group information for "${groupTitle.innerText}" (${groupUserCount} clients)`);
|
|
}
|
|
for (let i = 0; i < groupUserCount; i++) {
|
|
const groupUsername = reader.readShortString();
|
|
const groupUserLocation = reader.readString();
|
|
if (window.multiprobe_debug) {
|
|
log("RECV", `[GROUP USER] USERNAME=${groupUsername}, LOCATION=${groupUserLocation}`);
|
|
}
|
|
createGroupUser(groupUsername, groupUserLocation);
|
|
}
|
|
break;
|
|
}
|
|
case MessageType.HonkShoe:
|
|
{
|
|
const clientId = reader.readUInt();
|
|
const isAfk = reader.readBool();
|
|
|
|
if (remoteClients.has(clientId)) {
|
|
remoteClients.get(clientId).setAfk(isAfk);
|
|
if (window.multiprobe_debug) {
|
|
if (isAfk) {
|
|
log("RECV", `Client ${clientId} went afk`);
|
|
} else {
|
|
log("RECV", `Client ${clientId} is no longer afk`);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case MessageType.BadgeUnlock:
|
|
{
|
|
const badgeTitle = reader.readString16();
|
|
const badgeDescription = reader.readString16();
|
|
const badgeIconUrl = reader.readString16();
|
|
|
|
createBadgePopup(badgeTitle, badgeDescription, badgeIconUrl);
|
|
break;
|
|
}
|
|
case MessageType.TooOutOfDate:
|
|
{
|
|
ws.close();
|
|
preventReconnect = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ws.onmessage = (e) => {
|
|
e.data.arrayBuffer().then(onMessage);
|
|
}
|
|
|
|
function onCloseAndError() {
|
|
if (keepAliveInterval) {
|
|
clearInterval(keepAliveInterval);
|
|
keepAliveInterval = undefined;
|
|
debugConnectedTo.innerText = "Disconnected";
|
|
debugAuthedAs.innerText = "Not Authenticated";
|
|
}
|
|
ws = undefined;
|
|
ready = false;
|
|
setTimeout(() => doConnect(localStorage["mpapikey"]), 5000);
|
|
}
|
|
ws.onclose = onCloseAndError;
|
|
ws.onerror = (e) => {
|
|
console.error(e);
|
|
}
|
|
}
|
|
|
|
function createOnlineDialog() {
|
|
const bg = document.createElement("mp_container");
|
|
windowContainer = bg;
|
|
bg.style = "z-index:1000000000;position:fixed;top:0px;left:0px;width:100%;height:100%;background-color:rgba(0,0,0,0.5);text-shadow: none!important;font-family: Arial,sans-serif;font-size: unset !important;";
|
|
const dialog = document.createElement("mp_container");
|
|
dialog.style = "position:absolute;top:50%;left:50%;min-width:15rem;background-color:#343434;padding:1rem;transform:translate(-50%,-50%);text-align:center;color:white";
|
|
bg.appendChild(dialog);
|
|
const title = document.createElement("img");
|
|
for (const siteKey of Object.keys(SITE_LOGOS)) {
|
|
const domain = window.location.href.split("://")[1].split("/")[0];
|
|
if ((siteKey.startsWith("*.") && domain.includes(siteKey.replace("*.", ""))) || domain.startsWith(siteKey)) {
|
|
title.src = SITE_LOGOS[siteKey];
|
|
break;
|
|
}
|
|
}
|
|
title.style = "background-color:black;padding:.25rem;margin-bottom:.5rem;border:1px solid white";
|
|
dialog.appendChild(title);
|
|
const subtitle = document.createElement("h5");
|
|
subtitle.innerText = `Logged in as ${localStorage["t00mp_username"]}`;
|
|
subtitle.style.marginTop = ".5rem";
|
|
dialog.appendChild(subtitle);
|
|
|
|
const buttons = document.createElement("mp_container");
|
|
buttons.style.marginTop = "1rem";
|
|
|
|
const disconnectButton = document.createElement("mp_button");
|
|
disconnectButton.innerText = localStorage["mpconnectonload"] === "true" ? "Disconnect" : "Connect";
|
|
disconnectButton.onclick = () => {
|
|
if (localStorage["mpconnectonload"] === "true") {
|
|
localStorage["mpconnectonload"] = false;
|
|
if (ws) {
|
|
ws.close();
|
|
}
|
|
} else {
|
|
localStorage["mpconnectonload"] = true;
|
|
}
|
|
disconnectButton.innerText = localStorage["mpconnectonload"] === "true" ? "Disconnect" : "Connect";
|
|
|
|
window.location.reload();
|
|
};
|
|
buttons.appendChild(disconnectButton);
|
|
|
|
const manageAccountLink = document.createElement("a");
|
|
manageAccountLink.style.display = "none";
|
|
manageAccountLink.href = "https://multiprobe.eusv.net/";
|
|
manageAccountLink.target = "_blank";
|
|
buttons.appendChild(manageAccountLink);
|
|
const manageAccount = document.createElement("mp_button");
|
|
manageAccount.style.marginLeft = "1rem";
|
|
manageAccount.innerText = "Manage Account";
|
|
manageAccount.onclick = () => {
|
|
manageAccountLink.click();
|
|
}
|
|
buttons.appendChild(manageAccount);
|
|
|
|
const closeButton = document.createElement("mp_button");
|
|
closeButton.innerText = "Close";
|
|
closeButton.onclick = () => {
|
|
bg.remove();
|
|
windowContainer = null;
|
|
};
|
|
closeButton.style.marginLeft = "1rem";
|
|
buttons.appendChild(closeButton);
|
|
|
|
dialog.appendChild(buttons);
|
|
|
|
document.documentElement.appendChild(bg);
|
|
}
|
|
|
|
function createUpdateDialog(versionNumber) {
|
|
const bg = document.createElement("mp_container");
|
|
bg.style = "z-index:1000000000;position:fixed;top:0px;left:0px;width:100%;height:100%;background-color:rgba(0,0,0,0.5);text-shadow: none!important;font-family: Arial,sans-serif;font-size: unset !important;";
|
|
const dialog = document.createElement("mp_container");
|
|
dialog.style = "position:absolute;top:50%;left:50%;min-width:15rem;background-color:#343434;padding:1rem;transform:translate(-50%,-50%);text-align:center;color:white";
|
|
bg.appendChild(dialog);
|
|
const title = document.createElement("h4");
|
|
title.innerText = "An update is available!";
|
|
title.style.marginBottom = "0px";
|
|
dialog.appendChild(title);
|
|
|
|
const updateText = document.createElement("mp_text");
|
|
updateText.innerHTML = `<a class="mplink" href="https://git.eusv.net/tgpholly/t00-multiuser/raw/branch/master/client/Terminal-00-Multiuser.user.js?${Date.now()}">Please click here to update to the latest version.</a><br>This takes less than a few seconds to do and ensures you can continue to use MultiProbe.<br><br>Current Version: ${USERSCRIPT_VERSION_RAW}<br>New Version: ${versionNumber}`;
|
|
dialog.appendChild(updateText);
|
|
|
|
const buttons = document.createElement("mp_container");
|
|
buttons.style.marginTop = "1rem";
|
|
|
|
const gotIt = document.createElement("mp_button");
|
|
gotIt.onclick = () => {
|
|
bg.remove();
|
|
}
|
|
gotIt.innerText = "Later";
|
|
buttons.appendChild(gotIt);
|
|
|
|
dialog.appendChild(buttons);
|
|
|
|
document.documentElement.appendChild(bg);
|
|
}
|
|
|
|
function createFirstTimeDialog() {
|
|
if (localStorage["mpshowfirsttime"] && localStorage["mpshowfirsttime"] === "false") {
|
|
return;
|
|
}
|
|
|
|
const bg = document.createElement("mp_container");
|
|
windowContainer = bg;
|
|
bg.style = "z-index:1000000000;position:fixed;top:0px;left:0px;width:100%;height:100%;background-color:rgba(0,0,0,0.5);text-shadow: none!important;font-family: Arial,sans-serif;font-size: unset !important;";
|
|
const dialog = document.createElement("mp_container");
|
|
dialog.style = "position:absolute;top:50%;left:50%;min-width:15rem;background-color:#343434;padding:1rem;transform:translate(-50%,-50%);text-align:center;color:white";
|
|
bg.appendChild(dialog);
|
|
const title = document.createElement("h4");
|
|
title.innerText = "Welcome to MultiProbe!";
|
|
title.style.marginBottom = "0px";
|
|
dialog.appendChild(title);
|
|
|
|
const actionList = document.createElement("ul");
|
|
actionList.style.textAlign = "start";
|
|
dialog.appendChild(actionList);
|
|
|
|
const pingLI = document.createElement("li");
|
|
pingLI.innerHTML = "You can create a \"ping\" at your cursor position by pressing <kbd>P</kbd>";
|
|
actionList.appendChild(pingLI);
|
|
|
|
const nameLI = document.createElement("li");
|
|
nameLI.innerHTML = "You can show/hide your own name for screenshots by pressing <kbd>N</kbd>";
|
|
actionList.appendChild(nameLI);
|
|
|
|
const groupLI = document.createElement("li");
|
|
groupLI.innerHTML = "You can show/hide the party sidebar if you are in one by pressing <kbd>G</kbd>";
|
|
actionList.appendChild(groupLI);
|
|
|
|
const helpTip = document.createElement("li");
|
|
helpTip.innerHTML = "If you press <kbd>F1</kbd> you can open this help again.";
|
|
actionList.appendChild(helpTip);
|
|
|
|
const buttons = document.createElement("mp_container");
|
|
buttons.style.marginTop = "1rem";
|
|
|
|
const gotIt = document.createElement("mp_button");
|
|
gotIt.onclick = () => {
|
|
localStorage["mpshowfirsttime"] = "false";
|
|
bg.remove();
|
|
windowContainer = null;
|
|
}
|
|
gotIt.innerText = "Got It";
|
|
buttons.appendChild(gotIt);
|
|
|
|
dialog.appendChild(buttons);
|
|
|
|
document.documentElement.appendChild(bg);
|
|
}
|
|
|
|
const badgeQueue = [];
|
|
let displaying = false;
|
|
|
|
function createBadgePopup(title, description, image) {
|
|
if (displaying) {
|
|
badgeQueue.push([title, description, image]);
|
|
return;
|
|
}
|
|
displaying = true;
|
|
|
|
const popup = document.createElement("mp_badge");
|
|
popup.classList.add("badgepop");
|
|
popup.classList.add("badgepopcreate");
|
|
|
|
const baseRow = document.createElement("mp_row");
|
|
popup.appendChild(baseRow);
|
|
|
|
const imageCol = document.createElement("mp_col_auto");
|
|
baseRow.appendChild(imageCol);
|
|
|
|
const badgeImage = new Image(32, 32);
|
|
badgeImage.src = image;
|
|
badgeImage.style = "image-rendering:pixelated";
|
|
badgeImage.onload = () => {
|
|
popup.classList.remove("badgepopcreate");
|
|
popup.classList.add("badgepopin");
|
|
setTimeout(() => popupSound.play(), 100);
|
|
};
|
|
imageCol.appendChild(badgeImage);
|
|
|
|
const textRowCol = document.createElement("mp_col");
|
|
baseRow.appendChild(textRowCol);
|
|
|
|
const badgeTitle = document.createElement("mp_text");
|
|
badgeTitle.innerHTML = `<b>${title}</b>`;
|
|
textRowCol.appendChild(badgeTitle);
|
|
|
|
const badgeDescription = document.createElement("mp_text");
|
|
badgeDescription.innerHTML = description.replaceAll("\r", "").replaceAll("\n", "<br>");
|
|
textRowCol.appendChild(badgeDescription);
|
|
|
|
popupRoot.appendChild(popup);
|
|
|
|
setTimeout(() => {
|
|
popup.classList.remove("badgepopin");
|
|
popup.classList.add("badgepopout");
|
|
setTimeout(() => {
|
|
popup.remove();
|
|
}, 500);
|
|
displaying = false;
|
|
if (badgeQueue.length > 0) {
|
|
const badgeData = badgeQueue.splice(0, 1)[0];
|
|
setTimeout(() => createBadgePopup(badgeData[0], badgeData[1], badgeData[2]), 0);
|
|
}
|
|
}, 5350);
|
|
}
|
|
window.createBadgePopup = createBadgePopup;
|
|
|
|
function createLoginDialog() {
|
|
const bg = document.createElement("mp_container");
|
|
windowContainer = bg;
|
|
bg.style = "z-index:1000000000;position:fixed;top:0px;left:0px;width:100%;height:100%;background-color:rgba(0,0,0,0.5);text-shadow: none!important;font-family: Arial,sans-serif;font-size: unset !important;";
|
|
const dialog = document.createElement("mp_container");
|
|
dialog.style = "position:absolute;top:50%;left:50%;width:15rem;background-color:#343434;padding-bottom:1rem;transform:translate(-50%,-50%);text-align:center;color:white";
|
|
bg.appendChild(dialog);
|
|
const title = document.createElement("h4");
|
|
title.innerText = "MultiProbe";
|
|
title.style.marginBottom = ".5rem";
|
|
dialog.appendChild(title);
|
|
const manageAccountLink = document.createElement("a");
|
|
manageAccountLink.className = "mplink";
|
|
manageAccountLink.href = "https://multiprobe.eusv.net/";
|
|
manageAccountLink.innerHTML = "Click here to create an account<br><br>";
|
|
manageAccountLink.target = "_blank";
|
|
manageAccountLink.style.marginLeft = manageAccountLink.style.marginRight = ".5rem";
|
|
dialog.appendChild(manageAccountLink);
|
|
const loginForm = document.createElement("form");
|
|
dialog.appendChild(loginForm);
|
|
const username = document.createElement("input");
|
|
username.placeholder = "Enter Username";
|
|
username.maxLength = 32;
|
|
username.style.width = "12rem";
|
|
username.name = "username";
|
|
loginForm.appendChild(username);
|
|
const password = document.createElement("input");
|
|
password.type = "password";
|
|
password.style.marginTop = ".5rem";
|
|
password.placeholder = "Enter Password";
|
|
password.style.width = "12rem";
|
|
password.name = "password";
|
|
loginForm.appendChild(password);
|
|
const buttons = document.createElement("mp_container");
|
|
buttons.style.marginTop = "1rem";
|
|
loginForm.appendChild(buttons);
|
|
const submitButton = document.createElement("mp_button");
|
|
submitButton.innerText = "Connect";
|
|
buttons.appendChild(submitButton);
|
|
const doNotButton = document.createElement("mp_button");
|
|
doNotButton.innerText = "Close";
|
|
doNotButton.onclick = () => bg.remove();
|
|
doNotButton.style.marginLeft = "1rem";
|
|
buttons.appendChild(doNotButton);
|
|
|
|
function performLogin() {
|
|
username.disabled = true;
|
|
password.disabled = true;
|
|
submitButton.disabled = true;
|
|
doNotButton.disabled = true;
|
|
|
|
title.innerText = "Authenticating...";
|
|
fetch((windowLocation.replace("127.0.0.1", "localhost").includes("//localhost:") || window.multiprobe_connectLocal) ? "http://localhost:38194/api/login" : "https://multiprobe.eusv.net/api/login", {
|
|
method: "POST",
|
|
body: `username=${encodeURIComponent(username.value)}&password=${encodeURIComponent(password.value)}`,
|
|
headers: {
|
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
}
|
|
}).then(res => {
|
|
if (res.status === 200) {
|
|
res.text().then(apiKey => {
|
|
title.innerText = "Connecting to realtime server...";
|
|
localStorage["t00mp_username"] = username.value;
|
|
localStorage["mpapikey"] = apiKey;
|
|
doConnect(apiKey);
|
|
});
|
|
} else {
|
|
title.innerText = "Incorrect Login";
|
|
username.disabled = false;
|
|
password.disabled = false;
|
|
submitButton.disabled = false;
|
|
doNotButton.disabled = false;
|
|
}
|
|
}).catch(err => {
|
|
console.error(err);
|
|
username.disabled = false;
|
|
password.disabled = false;
|
|
submitButton.disabled = false;
|
|
doNotButton.disabled = false;
|
|
});
|
|
}
|
|
|
|
submitButton.onclick = () => performLogin();
|
|
loginForm.onsubmit = (e) => {
|
|
e.preventDefault();
|
|
|
|
performLogin();
|
|
}
|
|
|
|
document.documentElement.appendChild(bg);
|
|
}
|
|
|
|
const openMenuButton = document.createElement("mp_button");
|
|
openMenuButton.style = "position:fixed;top:0px;right:0px;z-index:9999999;margin:4px";
|
|
openMenuButton.classList.add("out-of-the-way");
|
|
openMenuButton.innerText = "MultiProbe";
|
|
openMenuButton.onclick = () => {
|
|
if (ws || localStorage["mpapikey"]) {
|
|
createOnlineDialog();
|
|
} else {
|
|
createLoginDialog();
|
|
}
|
|
};
|
|
document.documentElement.appendChild(openMenuButton);
|
|
|
|
if (localStorage["mpapikey"] && localStorage["mpapikey"] !== "" && localStorage["mpconnectonload"] === "true") {
|
|
doConnect(localStorage["mpapikey"]);
|
|
}
|
|
|
|
console.log("[MP] Init complete.");
|
|
})(); |