2024-04-18 23:18:49 +01:00
// ==UserScript==
2024-04-26 17:34:34 +01:00
// @name MultiProbe
2024-04-18 23:18:49 +01:00
// @namespace https://*.angusnicneven.com/*
2024-10-30 15:13:43 +00:00
// @version 20241030.0
2024-04-18 23:18:49 +01:00
// @description Probe with friends!
// @author tgpholly
// @match https://*.angusnicneven.com/*
2024-05-27 12:56:49 +01:00
// @match https://*.heavenonline.xyz/*
2024-04-18 23:18:49 +01:00
// @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 ) ;
}
2024-05-07 21:46:52 +01:00
const windowLocation = window . location . href ;
2024-05-06 15:41:26 +01:00
window . multiprobe _debug = false ;
2024-10-02 22:25:08 +01:00
window . multiprobe _connectLocal = false ;
2024-05-06 15:41:26 +01:00
2024-05-27 12:56:49 +01:00
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"
} ;
2024-10-02 19:26:26 +01:00
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"
} ;
2024-10-13 12:41:45 +01:00
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 ;
2024-05-07 22:17:30 +01:00
console . log ( "[MP] MultiProbe init" ) ;
2024-04-18 23:18:49 +01:00
( function ( ) {
'use strict' ;
2024-04-26 10:21:27 +01:00
// Make sure to change the userscript version too!!!!!!!!!!
2024-10-30 15:13:43 +00:00
const USERSCRIPT _VERSION _RAW = "20241030.0" ;
2024-04-26 10:21:27 +01:00
const USERSCRIPT _VERSION = parseInt ( USERSCRIPT _VERSION _RAW . replace ( "." , "" ) ) ;
2024-04-26 03:01:06 +01:00
2024-04-18 23:18:49 +01:00
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 , - 9223372036854775808 n , 9223372036854775808 n ) ; this . checkOffset ( offset ) ; this . dataView . setBigInt64 ( offset , value , true ) ; return this } writeBigUint64LE ( value , offset ) { if ( typeof value === "number" ) { value = BigInt ( value ) } this . checkValue ( value , 0 n , 18446744073709551616 n ) ; 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 , - ( 2 n * * 63 n ) , 2 n * * 63 n ) ; this . checkOffset ( offset ) ; this . dataView . setBigInt64 ( offset , value , false ) ; return this } writeBigUint64BE ( value , offset ) { if ( typeof value === "number" ) { value = BigInt ( value ) } this . checkValue ( value , 0 n , 2 n * * 64 n ) ; 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 } writeDouble
const MessageType = {
KeepAlive : 0 ,
ClientDetails : 1 ,
CursorPos : 2 ,
ClientJoined : 3 ,
Clients : 4 ,
ClientLeft : 5 ,
2024-04-25 02:37:37 +01:00
Ping : 6 ,
2024-05-27 12:56:49 +01:00
GroupData : 7 ,
2024-10-02 01:08:03 +01:00
HonkShoe : 8 ,
2024-10-08 01:06:36 +01:00
BadgeUnlock : 9 ,
2024-10-13 12:41:45 +01:00
DEBUG _UnlockAllBadges : 10 ,
TooOutOfDate : 11
2024-04-18 23:18:49 +01:00
} ;
2024-10-02 21:18:03 +01:00
let cssCursor = "" ;
let cssHoverCursor = "" ;
let pageLoadCompleted = true ;
2024-05-07 22:17:30 +01:00
console . log ( "[MP] Injecting custom styles..." ) ;
2024-04-18 23:18:49 +01:00
const styles = document . createElement ( "style" ) ;
2024-10-02 21:18:03 +01:00
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" ) ;
}
2024-10-02 22:25:08 +01:00
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 ) ;
}
2024-10-02 21:18:03 +01:00
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 = `
2024-05-02 16:50:05 +01:00
html {
2024-10-02 21:18:03 +01:00
$ { pageLoadCompleted ? ` cursor: ${ cssCursor } ; ` : "" }
2024-05-02 16:50:05 +01:00
}
2024-04-18 23:18:49 +01:00
# otherCursors {
position : absolute ;
top : 0 px ;
left : 0 px ;
width : 100 % ;
height : 100 % ;
z - index : 999999999 ;
pointer - events : none ;
overflow : hidden ;
2024-05-07 21:46:52 +01:00
text - shadow : none ! important ;
font - family : Arial , sans - serif ;
2024-04-18 23:18:49 +01:00
}
2024-04-26 16:52:58 +01:00
# otherCursors img {
image - rendering : pixelated ;
}
2024-10-02 01:08:03 +01:00
2024-04-18 23:18:49 +01:00
@ keyframes ping {
0 % {
scale : 0 ;
opacity : 1 ;
}
100 % {
scale : 2 ;
opacity : 0 ;
}
}
. ping {
opacity : 0 ;
position : absolute ;
animation : ping 2 s linear ;
animation - iteration - count : 1 ;
image - rendering : pixelated ;
2024-10-13 12:41:45 +01:00
background : url ( "https://eusv.net/MA7cm1BBelFHDP" ) ;
2024-04-18 23:18:49 +01:00
width : 64 px ;
height : 64 px ;
2024-04-20 17:25:16 +01:00
z - index : - 1 ! important ;
2024-04-18 23:18:49 +01:00
}
2024-04-24 00:06:42 +01:00
2024-05-27 12:56:49 +01:00
. eepy {
position : absolute ;
top : 0 ;
right : 0 ;
width : 64 px ;
height : 64 px ;
image - rendering : pixelated ;
2024-10-13 12:41:45 +01:00
background : url ( "https://eusv.net/9ZU5r29lg1yKIi" ) ;
2024-05-27 12:56:49 +01:00
z - index : 1 ;
transform : translate ( 60 px , - 60 px ) ;
}
2024-04-24 00:06:42 +01:00
. grouphidden {
right : - 12 rem ! important ;
}
. groupui {
position : fixed ;
2024-10-02 19:11:33 +01:00
top : calc ( 2 rem + 6 px ) ;
2024-04-24 00:06:42 +01:00
right : 0 px ;
height : 25 rem ;
width : 12 rem ;
background - color : black ;
color : white ;
z - index : 9999998 ;
2024-05-07 21:46:52 +01:00
text - shadow : none ! important ;
font - family : Arial , sans - serif ;
font - size : unset ! important ;
2024-10-02 19:11:33 +01:00
transition : right . 16 s ease - in - out ;
2024-04-24 00:06:42 +01:00
}
. groupui - popper {
position : absolute ;
2024-10-02 19:11:33 +01:00
top : - 2 px ;
left : - 1.5 rem ;
2024-04-24 00:06:42 +01:00
width : 1 rem ;
2024-10-02 19:11:33 +01:00
height : calc ( 100 % - 6 px ) ;
2024-04-24 00:06:42 +01:00
opacity : 0.25 ;
2024-10-02 19:11:33 +01:00
transition : opacity . 16 s ease - in - out ;
2024-04-24 00:06:42 +01:00
background - color : black ;
border : 1 px solid white ;
color : white ;
2024-10-02 19:11:33 +01:00
padding : . 125 rem ;
line - height : 380 px ;
}
. groupui : not ( . grouphidden ) . groupui - popper , . groupui - popper : hover {
opacity : 1 ;
2024-04-24 00:06:42 +01:00
}
. groupui - title {
position : absolute ;
top : 0 px ;
left : 0 px ;
width : 100 % ;
text - align : center ;
height : 2 rem ;
line - height : 2 rem ;
background - color : rgba ( 255 , 255 , 255 , 0.1 ) ;
2024-05-07 21:52:46 +01:00
overflow : hidden ;
2024-04-24 00:06:42 +01:00
}
. groupui - scrollbox {
position : absolute ;
top : 2 rem ;
left : 0 px ;
width : 100 % ;
height : calc ( 100 % - 2 rem ) ;
overflow - y : scroll ;
2024-05-07 21:46:52 +01:00
font - family : Arial , sans - serif ;
font - size : unset ! important ;
2024-04-24 00:06:42 +01:00
}
. groupui - user {
position : relative ;
width : 100 % ;
height : 2 rem ;
background - color : rgba ( 255 , 255 , 255 , 0.2 ) ;
}
. groupui - user : nth - child ( 2 n ) {
background - color : rgba ( 255 , 255 , 255 , 0.15 ) ;
}
2024-10-02 01:08:03 +01:00
. groupui - user mp _text {
2024-04-24 00:06:42 +01:00
position : absolute ;
line - height : 2 rem ;
margin : 0 ;
margin - left : . 5 rem ;
}
. groupui - user . buttons {
position : absolute ;
height : 100 % ;
top : 0 px ;
right : 0 px ;
margin - right : . 5 rem ;
}
. groupui - user . buttons button {
margin - left : . 5 rem ;
margin - top : . 2 rem ;
}
2024-04-25 10:12:17 +01:00
kbd {
background - color : # eee ;
border - radius : 3 px ;
border : 1 px solid # b4b4b4 ;
box - shadow : 0 1 px 1 px rgba ( 0 , 0 , 0 , 0.2 ) , 0 2 px 0 0 rgba ( 255 , 255 , 255 , 0.7 ) inset ;
color : # 333 ;
display : inline - block ;
font - size : 0.85 em ;
font - weight : 700 ;
line - height : 1 ;
padding : 2 px 4 px ;
white - space : nowrap ;
2024-05-07 21:46:52 +01:00
font - family : Arial , sans - serif ;
font - size : unset ! important ;
2024-04-25 10:12:17 +01:00
}
2024-04-26 03:01:06 +01:00
. mplink , . mplink : visited {
color : # d2738a ;
}
. mplink : hover {
color : # c1b492 ;
}
2024-10-02 01:08:03 +01:00
@ keyframes badgepop {
0 % {
2024-10-13 00:34:49 +01:00
transform : translate ( - 50 % , - 125 % ) ;
2024-10-02 01:08:03 +01:00
}
100 % {
2024-10-13 00:34:49 +01:00
transform : translate ( - 50 % , 0 % ) ;
2024-10-02 01:08:03 +01:00
}
}
@ keyframes badgepopout {
0 % {
2024-10-13 00:34:49 +01:00
transform : translate ( - 50 % , 0 % ) ;
2024-10-02 01:08:03 +01:00
}
100 % {
2024-10-13 00:34:49 +01:00
transform : translate ( - 50 % , - 125 % ) ;
2024-10-02 01:08:03 +01:00
}
}
. badgepopout {
2024-10-13 00:34:49 +01:00
transform : translate ( - 50 % , - 125 % ) ;
visibility : visible ! important ;
2024-10-02 01:08:03 +01:00
animation : badgepopout . 35 s ease - in - out ! important ;
}
. badgepopin {
2024-10-13 00:34:49 +01:00
transform : translate ( - 50 % , 0 % ) ;
visibility : visible ! important ;
2024-10-02 01:08:03 +01:00
animation : badgepop . 35 s ease - in - out ! important ;
animation - iteration - count : 1 ;
}
. badgepopcreate {
2024-10-13 00:34:49 +01:00
transform : translate ( - 50 % , - 125 % ) ;
2024-10-02 01:08:03 +01:00
}
. badgepop {
position : absolute ;
margin - top : . 5 rem ;
2024-10-13 00:34:49 +01:00
visibility : hidden ;
width : calc ( 15.0 rem + 4 vw ) ;
2024-10-02 01:08:03 +01:00
background - color : black ;
text - align : start ! important ;
padding : . 5 rem ;
border : 1 px solid white ;
2024-10-13 00:34:49 +01:00
pointer - events : none ;
}
. badgepop mp _col _auto {
padding - right : 0 ;
2024-10-02 01:08:03 +01:00
}
. badgepop mp _text {
2024-10-13 00:34:49 +01:00
font - size : calc ( 0.5 rem + 0.1 vw ) ;
2024-10-02 01:08:03 +01:00
color : white ;
padding : 0 ;
margin : 0 ;
}
mp _text {
color : white ;
display : block ;
}
mp _container {
display : block ;
}
2024-10-13 00:34:49 +01:00
mp _row {
display : flex ;
flex - wrap : wrap ;
margin - top : calc ( - 1 * 0 ) ;
margin - right : calc ( - 0.5 * 1.5 rem ) ;
margin - left : calc ( - 0.5 * 1.5 rem ) ;
}
mp _row > * {
flex - shrink : 0 ;
width : 100 % ;
max - width : 100 % ;
padding - right : calc ( 1.5 rem * 0.5 ) ;
padding - left : calc ( 1.5 rem * 0.5 ) ;
margin - top : 0 ;
}
mp _col {
flex : 1 0 0 % ;
}
mp _col _auto {
flex : 0 0 auto ;
width : auto ;
}
2024-10-02 01:08:03 +01:00
. badgepop * {
text - align : unset ! important ;
text - shadow : none ! important ;
font - family : Arial , sans - serif ;
}
2024-10-02 19:26:26 +01:00
mp _container a {
cursor : $ { cssHoverCursor } ;
}
2024-10-02 01:08:03 +01:00
mp _container ul {
/* base t00 ul style */
background : # d2738a ! important ;
margin : 40 px 10 px 10 px 10 px ! important ;
padding : 0 px 0 px 0 px 0 px ! important ;
width : 800 px ! important ;
box - shadow : 0 px 5 px 10 px # 000000 ! important ;
line - height : 30 px ! important ;
/* ravarcheon fixes */
position : relative ! important ;
left : unset ! important ;
transform : unset ! important ;
}
mp _container ul li {
background : # 000000 ! important ;
margin : 0 px 0 px 2 px 0 px ! important ;
padding : 20 px 20 px 20 px 20 px ! important ;
list - style : none ! important ;
/* ravarcheon fixes */
text - shadow : unset ! important ;
}
2024-10-02 12:32:04 +01:00
mp _group _user {
display : block ;
}
2024-10-02 19:11:33 +01:00
mp _button {
display : inline - block ;
background - color : black ;
color : white ;
border : 1 px solid white ;
text - shadow : none ! important ;
font - family : Arial , sans - serif ;
font - size : unset ! important ;
padding : 4 px 8 px ;
margin : 2 px ;
text - align : center ;
2024-10-02 19:26:26 +01:00
cursor : $ { cssHoverCursor } ;
2024-10-02 19:11:33 +01:00
}
. out - of - the - way {
opacity : 0.25 ;
transition : opacity . 16 s ease - in - out ;
}
. out - of - the - way : hover {
opacity : 1 ;
}
2024-04-18 23:18:49 +01:00
` .split(" \n ").join("").split(" \r ").join("").split(" \t ").join("");
2024-10-02 21:18:03 +01:00
}
// Cursed way of detecting heavenonline load
2024-10-13 17:43:56 +01:00
if ( window . location . href . split ( "://" ) [ 1 ] . split ( "/" ) [ 0 ] === "heavenonline.xyz" && typeof ( finished ) !== "undefined" ) {
2024-10-02 21:18:03 +01:00
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 ( ) ;
}
2024-05-02 16:50:05 +01:00
2024-10-08 14:39:42 +01:00
document . documentElement . appendChild ( styles ) ;
2024-04-18 23:18:49 +01:00
2024-04-26 03:01:06 +01:00
if ( ! localStorage [ "mpconnectonload" ] ) {
localStorage [ "mpconnectonload" ] = true ;
}
2024-05-02 16:33:50 +01:00
2024-10-02 01:08:03 +01:00
const otherCursors = document . createElement ( "mp_cursors" ) ;
2024-04-18 23:18:49 +01:00
otherCursors . id = "otherCursors" ;
2024-10-02 01:08:03 +01:00
document . documentElement . appendChild ( otherCursors ) ;
2024-04-19 12:03:38 +01:00
2024-10-02 01:08:03 +01:00
const groupUIBase = document . createElement ( "mp_party" ) ;
2024-04-24 00:06:42 +01:00
groupUIBase . style = "display:none" ;
groupUIBase . classList . add ( "groupui" ) ;
groupUIBase . classList . add ( "grouphidden" ) ;
2024-10-02 19:11:33 +01:00
const groupPopper = document . createElement ( "mp_button" ) ;
2024-04-24 00:06:42 +01:00
groupPopper . classList . add ( "groupui-popper" ) ;
groupPopper . onclick = ( ) => {
groupUIBase . classList . toggle ( "grouphidden" ) ;
groupPopper . innerText = groupUIBase . classList . contains ( "grouphidden" ) ? "<" : ">" ;
} ;
groupPopper . innerText = "<" ;
groupUIBase . appendChild ( groupPopper ) ;
2024-10-02 01:08:03 +01:00
const groupTitle = document . createElement ( "mp_container" ) ;
2024-04-24 00:06:42 +01:00
groupTitle . classList . add ( "groupui-title" ) ;
groupTitle . innerText = "GROUP_NAME" ;
groupUIBase . appendChild ( groupTitle ) ;
2024-10-02 01:08:03 +01:00
const groupUsers = document . createElement ( "mp_container" ) ;
2024-04-24 00:06:42 +01:00
groupUsers . classList . add ( "groupui-scrollbox" ) ;
groupUIBase . appendChild ( groupUsers ) ;
2024-10-02 01:08:03 +01:00
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 ) ;
2024-04-24 00:06:42 +01:00
2024-10-02 22:25:08 +01:00
const popupSound = new Audio ( "https://eusv.net/kdHl7VYI1XMS4c" ) ;
document . documentElement . appendChild ( popupSound ) ;
2024-04-24 00:06:42 +01:00
function createGroupUser ( username , location ) {
2024-10-02 01:08:03 +01:00
const user = document . createElement ( "mp_group_user" ) ;
2024-04-24 00:06:42 +01:00
user . classList . add ( "groupui-user" ) ;
2024-10-02 01:08:03 +01:00
const usernameBox = document . createElement ( "mp_text" ) ;
2024-04-24 00:06:42 +01:00
usernameBox . innerText = username ;
user . appendChild ( usernameBox ) ;
2024-10-02 01:08:03 +01:00
const buttonBox = document . createElement ( "mp_container" ) ;
2024-04-24 00:06:42 +01:00
buttonBox . classList . add ( "buttons" ) ;
user . appendChild ( buttonBox ) ;
2024-10-02 19:11:33 +01:00
const gotoButton = document . createElement ( "mp_button" ) ;
2024-04-26 03:01:06 +01:00
gotoButton . innerText = "Go To" ;
2024-10-02 01:08:03 +01:00
const gotoLoc = location ;
2024-09-16 19:28:44 +01:00
gotoButton . onclick = ( ) => {
2024-10-02 01:08:03 +01:00
window . location . href = ` https:// ${ gotoLoc } ` ;
}
2024-04-24 00:06:42 +01:00
buttonBox . appendChild ( gotoButton ) ;
groupUsers . appendChild ( user ) ;
}
2024-10-09 22:57:18 +01:00
let clientWidth = document . documentElement . getBoundingClientRect ( ) . width ;
2024-10-30 15:13:43 +00:00
let htmlBoundingRect = document . documentElement . getBoundingClientRect ( ) ;
let bodyBoundingRect = document . body . getBoundingClientRect ( ) ;
2024-04-20 17:20:23 +01:00
2024-10-30 15:13:43 +00:00
const resizeCursorContainer = ( ) => {
htmlBoundingRect = document . documentElement . getBoundingClientRect ( ) ;
bodyBoundingRect = document . body . getBoundingClientRect ( ) ;
2024-10-09 22:57:18 +01:00
if ( document . documentElement . scrollHeight > window . innerHeight ) {
2024-10-30 15:13:43 +00:00
otherCursors . style = ` width: ${ clientWidth = bodyBoundingRect . width > htmlBoundingRect . width ? bodyBoundingRect . width : htmlBoundingRect . width } px;height: ${ document . documentElement . scrollHeight } px;cursor: ${ cssCursor } ; ` ;
2024-04-20 17:20:23 +01:00
} else {
2024-10-30 15:13:43 +00:00
otherCursors . style = ` width: ${ clientWidth = bodyBoundingRect . width > htmlBoundingRect . width ? bodyBoundingRect . width : htmlBoundingRect . width } px;height: ${ window . innerHeight } px;cursor: ${ cssCursor } ; ` ;
2024-04-20 17:20:23 +01:00
}
2024-10-30 15:13:43 +00:00
}
setInterval ( resizeCursorContainer , 1000 ) ;
document . addEventListener ( "resize" , resizeCursorContainer ) ;
2024-04-26 03:01:06 +01:00
2024-10-02 22:25:08 +01:00
const debugMessageContainer = document . createElement ( "mp_container" ) ;
2024-10-08 23:01:13 +01:00
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)" ;
2024-10-08 22:55:01 +01:00
if ( ! window . multiprobe _debug ) {
debugMessageContainer . style . display = "none" ;
2024-10-02 22:25:08 +01:00
}
2024-10-08 22:55:01 +01:00
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 ) ;
2024-04-26 17:34:34 +01:00
let needsToUpdate = false ;
2024-05-07 22:17:30 +01:00
console . log ( "[MP] Checking for new versions..." ) ;
2024-05-07 21:46:52 +01:00
const versionFetchAddress = windowLocation . replace ( "127.0.0.1" , "localhost" ) . includes ( "//localhost:" ) ? "http://localhost:38194/api/version" : "https://multiprobe.eusv.net/api/version" ;
2024-04-26 10:38:46 +01:00
fetch ( versionFetchAddress , { method : "post" } ) . then ( response => {
2024-04-26 10:21:27 +01:00
response . text ( ) . then ( versionNumberRaw => {
const versionNumber = parseInt ( versionNumberRaw ) ;
if ( versionNumber > USERSCRIPT _VERSION ) {
// We're out of date >:(
2024-04-26 17:34:34 +01:00
needsToUpdate = true ;
2024-04-26 10:21:27 +01:00
createUpdateDialog ( ` ${ versionNumberRaw . slice ( 0 , versionNumberRaw . length - 1 ) } . ${ versionNumberRaw . slice ( - 1 ) } ` ) ;
2024-05-07 22:17:30 +01:00
console . log ( "[MP] We're out of date :(" ) ;
} else {
console . log ( "[MP] We're up to date!" ) ;
2024-04-26 03:01:06 +01:00
}
} ) ;
2024-04-26 10:21:27 +01:00
} ) ;
2024-10-02 01:08:03 +01:00
2024-05-27 12:56:49 +01:00
const keepAlivePacket = createWriter ( Endian . LE , 1 ) . writeUByte ( MessageType . KeepAlive ) . toBuffer ( ) ;
2024-04-18 23:18:49 +01:00
let remoteClients = new FunkyArray ( ) ;
let ws ;
let ready = false ;
let currentMouseX = 0 ;
let currentMouseY = 0 ;
let oldMouseX = 0 ;
let oldMouseY = 0 ;
let lastSendTime = 0 ;
2024-10-08 01:06:36 +01:00
function unlockAllBadges ( ) {
if ( ws ) {
ws . send ( createWriter ( Endian . LE , 1 ) . writeUByte ( MessageType . DEBUG _UnlockAllBadges ) . toBuffer ( ) ) ;
}
}
window . unlockAllBadges = unlockAllBadges ;
2024-04-18 23:18:49 +01:00
class RemoteClient {
2024-05-27 12:56:49 +01:00
constructor ( name , startAfk , isSelfCursor = false ) {
2024-04-18 23:18:49 +01:00
this . name = name ;
2024-10-02 01:08:03 +01:00
this . element = document . createElement ( "mp_cursor" ) ;
2024-04-18 23:18:49 +01:00
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 ;
2024-04-20 17:20:23 +01:00
this . probeImage = image ;
2024-04-18 23:18:49 +01:00
this . element . appendChild ( image ) ;
2024-10-02 01:08:03 +01:00
const clientName = document . createElement ( "mp_container" ) ;
2024-04-20 17:20:23 +01:00
clientName . style = "position:absolute;left:100%;top:100%;background-color:black;padding:4px 8px;color:white;font-size:12px;font-family:Arial,sans-serif;" ;
2024-04-18 23:18:49 +01:00
clientName . innerText = name ;
this . element . appendChild ( clientName ) ;
2024-05-27 12:56:49 +01:00
if ( ! isSelfCursor ) {
2024-10-02 01:08:03 +01:00
this . eepy = document . createElement ( "mp_eepy" ) ;
2024-05-27 12:56:49 +01:00
this . eepy . className = "eepy" ;
this . eepy . style = startAfk ? "" : "display:none" ;
this . element . appendChild ( this . eepy ) ;
}
2024-04-18 23:18:49 +01:00
otherCursors . appendChild ( this . element ) ;
this . targetX = 0 ;
this . targetY = 0 ;
this . actualX = 0 ;
this . actualY = 0 ;
this . oldActualX = 0 ;
this . oldActualY = 0 ;
2024-04-20 17:20:23 +01:00
this . hasBeenMoved = false ;
this . element . visibility = "hidden" ;
2024-04-18 23:18:49 +01:00
}
2024-05-27 12:56:49 +01:00
setAfk ( afkState ) {
this . eepy . style = afkState ? "" : "display:none" ;
}
2024-04-18 23:18:49 +01:00
rawSetPos ( x , y ) {
2024-04-20 17:20:23 +01:00
if ( ! this . hasBeenMoved ) {
this . element . visibility = "" ;
2024-04-20 17:34:32 +01:00
this . hasBeenMoved = true ;
2024-04-20 17:20:23 +01:00
}
this . targetX = Math . round ( x * clientWidth ) ;
2024-04-18 23:18:49 +01:00
this . targetY = y ;
}
2024-04-19 00:18:12 +01:00
rawSetPosInit ( x , y ) {
2024-04-20 17:20:23 +01:00
if ( ! this . hasBeenMoved ) {
this . element . visibility = "" ;
2024-04-20 17:34:32 +01:00
this . hasBeenMoved = true ;
2024-04-20 17:20:23 +01:00
}
this . actualX = this . targetX = Math . round ( x * clientWidth ) ;
2024-04-19 00:18:12 +01:00
this . actualY = this . targetY = y ;
}
2024-04-18 23:18:49 +01:00
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 ;
}
}
}
2024-04-20 17:20:23 +01:00
let selfCursor ;
2024-04-18 23:18:49 +01:00
function createPing ( x , y ) {
2024-10-02 01:08:03 +01:00
const pingDiv = document . createElement ( "mp_ping" ) ;
2024-04-18 23:18:49 +01:00
pingDiv . className = "ping" ;
pingDiv . style . top = ` ${ y } px ` ;
pingDiv . style . left = ` ${ x } px ` ;
otherCursors . appendChild ( pingDiv ) ;
2024-04-19 09:44:26 +01:00
setTimeout ( ( ) => pingDiv . remove ( ) , 2000 ) ;
2024-04-18 23:18:49 +01:00
}
function removeClient ( id ) {
if ( remoteClients . has ( id ) ) {
const client = remoteClients . get ( id ) ;
remoteClients . remove ( id ) ;
client . element . remove ( ) ;
}
}
2024-05-27 12:56:49 +01:00
let isAfkLocal = false ;
let lastSendAfkState = false
window . onfocus = ( e ) => {
isAfkLocal = false ;
}
window . onblur = ( e ) => {
isAfkLocal = true ;
}
2024-04-18 23:18:49 +01:00
let rawMouseX = 0 ;
let rawMouseY = 0 ;
window . onmousemove = ( e ) => {
2024-05-02 16:33:50 +01:00
currentMouseX = ( rawMouseX = e . clientX ) + window . scrollX ;
2024-10-09 22:57:18 +01:00
currentMouseY = ( rawMouseY = e . clientY ) + window . scrollY ;
2024-04-20 17:20:23 +01:00
if ( selfCursor ) {
selfCursor . rawSetPosInit ( currentMouseX / clientWidth , currentMouseY ) ;
selfCursor . updateCursor ( ) ;
}
2024-04-18 23:18:49 +01:00
}
window . onscroll = ( ) => {
2024-10-09 22:57:18 +01:00
bodyBoundingRect = document . documentElement . getBoundingClientRect ( ) ;
2024-05-02 16:33:50 +01:00
currentMouseX = rawMouseX + window . scrollX ;
2024-10-09 22:57:18 +01:00
currentMouseY = rawMouseY + window . scrollY ;
2024-04-20 17:20:23 +01:00
if ( selfCursor ) {
selfCursor . rawSetPosInit ( currentMouseX / clientWidth , currentMouseY ) ;
selfCursor . updateCursor ( ) ;
}
2024-04-18 23:18:49 +01:00
}
2024-05-27 12:56:49 +01:00
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 } ` ) ;
}
2024-05-06 15:41:26 +01:00
2024-04-20 17:20:23 +01:00
let allowedPings = 10 ;
2024-04-18 23:18:49 +01:00
window . onkeypress = ( e ) => {
if ( e . key === "p" ) {
if ( ws && ready ) {
2024-04-20 17:20:23 +01:00
if ( allowedPings > 0 ) {
allowedPings -- ;
2024-10-09 22:57:18 +01:00
ws . send ( createWriter ( Endian . LE , 9 ) . writeUByte ( MessageType . Ping ) . writeFloat ( ( rawMouseX + window . scrollX - 32 ) / clientWidth ) . writeInt ( rawMouseY + window . scrollY - 32 ) . toBuffer ( ) ) ;
2024-04-20 17:20:23 +01:00
}
2024-04-18 23:18:49 +01:00
}
2024-04-20 17:34:32 +01:00
} else if ( e . key === "n" ) {
if ( ws && ready && selfCursor ) {
localStorage [ "t00mp_cursorStyle" ] = selfCursor . element . style . visibility = selfCursor . element . style . visibility === "hidden" ? "" : "hidden" ;
}
2024-04-26 17:34:34 +01:00
} else if ( e . key === "g" ) {
if ( ws && ready && selfCursor ) {
groupPopper . click ( ) ;
}
2024-04-18 23:18:49 +01:00
}
}
2024-05-07 21:46:52 +01:00
window . onkeydown = ( e ) => {
if ( ! windowContainer && e . key === "F1" ) {
2024-10-08 22:55:01 +01:00
e . preventDefault ( ) ;
2024-05-07 21:46:52 +01:00
localStorage [ "mpshowfirsttime" ] = "true" ;
createFirstTimeDialog ( ) ;
2024-10-08 22:55:01 +01:00
return false ;
}
if ( e . key === "F3" ) {
e . preventDefault ( ) ;
window . multiprobe _debug = ! window . multiprobe _debug ;
debugMessageContainer . style . display = window . multiprobe _debug ? "block" : "none" ;
return false ;
2024-05-07 21:46:52 +01:00
}
} ;
2024-04-18 23:18:49 +01:00
function lerp ( value1 , value2 , amount ) {
return value1 + ( value2 - value1 ) * amount ;
}
let timeLastFrame = performance . now ( ) ;
let frameDeltaTime = 0 ;
2024-04-20 17:20:23 +01:00
let timeSinceLastPingReset = performance . now ( ) ;
2024-05-08 01:02:18 +01:00
try {
function animate ( ) {
frameDeltaTime = ( performance . now ( ) - timeLastFrame ) * 0.001 ;
2024-10-02 01:08:03 +01:00
2024-05-08 01:02:18 +01:00
if ( ws && ready ) {
if ( currentMouseX !== oldMouseX || currentMouseY !== oldMouseY ) {
if ( ( performance . now ( ) - lastSendTime ) >= 41 ) {
lastSendTime = performance . now ( ) ;
oldMouseX = currentMouseX ;
oldMouseY = currentMouseY ;
2024-05-27 12:56:49 +01:00
ws . send ( createWriter ( Endian . LE , 9 ) . writeUByte ( MessageType . CursorPos ) . writeFloat ( oldMouseX / clientWidth ) . writeInt ( currentMouseY ) . toBuffer ( ) ) ;
2024-05-08 01:02:18 +01:00
}
2024-04-18 23:18:49 +01:00
}
2024-05-27 12:56:49 +01:00
if ( isAfkLocal !== lastSendAfkState ) {
lastSendAfkState = isAfkLocal ;
ws . send ( createWriter ( Endian . LE , 2 ) . writeUByte ( MessageType . HonkShoe ) . writeBool ( isAfkLocal ) . toBuffer ( ) ) ;
}
2024-04-18 23:18:49 +01:00
}
2024-10-02 01:08:03 +01:00
2024-05-08 01:02:18 +01:00
if ( ( performance . now ( ) - timeSinceLastPingReset ) >= 1000 ) {
allowedPings = 10 ;
timeSinceLastPingReset = performance . now ( ) ;
}
2024-10-02 01:08:03 +01:00
2024-05-08 01:02:18 +01:00
remoteClients . forEach ( remoteUser => {
remoteUser . actualX = lerp ( remoteUser . actualX , remoteUser . targetX , 20 * frameDeltaTime ) ;
remoteUser . actualY = lerp ( remoteUser . actualY , remoteUser . targetY , 20 * frameDeltaTime ) ;
remoteUser . updateCursor ( ) ;
} ) ;
2024-10-02 01:08:03 +01:00
2024-05-08 01:02:18 +01:00
requestAnimationFrame ( animate ) ;
timeLastFrame = performance . now ( ) ;
2024-04-18 23:18:49 +01:00
}
2024-05-08 01:02:18 +01:00
animate ( ) ;
} catch ( e ) {
console . error ( e ) ;
2024-04-18 23:18:49 +01:00
}
2024-04-25 10:12:17 +01:00
let windowContainer = null ;
2024-10-13 12:41:45 +01:00
let preventReconnect = false ;
2024-04-25 10:12:17 +01:00
function doConnect ( apiKey ) {
2024-10-13 12:41:45 +01:00
if ( preventReconnect ) {
return ;
}
2024-04-25 10:12:17 +01:00
const Buffer = getBufferClass ( ) ;
2024-05-07 22:17:30 +01:00
console . log ( "[MP] Connecting to realtime server..." ) ;
2024-10-02 22:25:08 +01:00
ws = new WebSocket ( ( windowLocation . includes ( "//localhost:" ) || window . multiprobe _connectLocal ) ? "wss://ws.eusv.net/t00mpdev" : "wss://ws.eusv.net/t00mp" ) ;
2024-04-25 10:12:17 +01:00
let keepAliveInterval ;
2024-10-08 22:55:01 +01:00
let pingStartTime = - 1 ;
let lastPing = 0 ;
2024-04-25 10:12:17 +01:00
ws . onopen = ( ) => {
2024-05-07 22:17:30 +01:00
console . log ( "[MP] Connected! Authenticating..." ) ;
2024-10-08 22:55:01 +01:00
debugConnectedTo . innerText = ` Connected to ${ ws . url } ` ;
2024-04-25 10:12:17 +01:00
otherCursors . innerHTML = "" ;
2024-05-27 12:56:49 +01:00
selfCursor = new RemoteClient ( localStorage [ "t00mp_username" ] , false , true ) ;
2024-04-25 10:12:17 +01:00
selfCursor . probeImage . style . visibility = "hidden" ;
selfCursor . element . style . visibility = localStorage [ "t00mp_cursorStyle" ] ? ? "hidden" ;
selfCursor . hasBeenMoved = true ;
2024-05-27 12:56:49 +01:00
const currentPage = windowLocation . split ( "/" ) . slice ( 2 ) . join ( "/" ) ;
2024-10-13 12:41:45 +01:00
const key = window . ebfohezwACBL8DU8sqvedw ? ? "" ;
2024-10-08 22:55:01 +01:00
ws . send ( keepAlivePacket ) ;
2024-10-13 12:41:45 +01:00
ws . send ( createWriter ( Endian . LE , 8 + apiKey . length + currentPage . length + key . length )
2024-05-27 12:56:49 +01:00
. writeUByte ( MessageType . ClientDetails )
2024-04-25 10:12:17 +01:00
. writeShortString ( apiKey )
. writeString ( currentPage )
2024-10-13 12:41:45 +01:00
. writeUShort ( PROTOCOL _VERSION )
. writeString ( key )
2024-04-25 10:12:17 +01:00
. toBuffer ( ) ) ;
2024-10-12 11:52:13 +01:00
pingStartTime = performance . now ( ) ;
2024-04-25 10:12:17 +01:00
keepAliveInterval = setInterval ( ( ) => {
2024-10-08 22:55:01 +01:00
pingStartTime = performance . now ( ) ;
2024-04-25 10:12:17 +01:00
ws . send ( keepAlivePacket ) ;
2024-10-08 22:55:01 +01:00
} , 2000 ) ;
2024-04-25 10:12:17 +01:00
}
function onMessage ( buf ) {
const reader = createReader ( Endian . LE , Buffer . from ( buf ) ) ;
switch ( reader . readByte ( ) ) {
2024-10-08 22:55:01 +01:00
case MessageType . KeepAlive :
{
lastPing = performance . now ( ) - pingStartTime ;
debugPing . innerText = ` Ping: ${ lastPing } ms ` ;
break ;
}
2024-04-25 10:12:17 +01:00
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 ( ) ;
2024-05-27 12:56:49 +01:00
const isAfk = reader . readBool ( ) ;
remoteClients . set ( clientId , new RemoteClient ( clientName , isAfk ) ) . rawSetPosInit ( clientX , clientY ) ;
2024-04-25 10:12:17 +01:00
}
2024-05-06 15:41:26 +01:00
if ( window . multiprobe _debug ) {
log ( "RECV" , ` Initial client packet, got ${ clientCount } clients. ` ) ;
}
2024-04-25 10:12:17 +01:00
ready = true ;
if ( windowContainer ) {
windowContainer . remove ( ) ;
windowContainer = null ;
}
2024-04-26 17:34:34 +01:00
if ( ! needsToUpdate ) {
createFirstTimeDialog ( ) ;
}
2024-10-08 22:55:01 +01:00
debugAuthedAs . innerText = ` Authenticated as ${ localStorage [ "t00mp_username" ] } ` ;
2024-05-07 22:17:30 +01:00
console . log ( "[MP] Authenticated!" ) ;
2024-04-25 10:12:17 +01:00
break ;
}
case MessageType . ClientJoined :
{
const clientId = reader . readUInt ( ) ;
const clientName = reader . readShortString ( ) ;
2024-05-27 12:56:49 +01:00
remoteClients . set ( clientId , new RemoteClient ( clientName , false ) ) ;
2024-05-06 15:41:26 +01:00
if ( window . multiprobe _debug ) {
2024-05-27 12:56:49 +01:00
log ( "RECV" , ` New client joined page: ${ clientName } ID= ${ clientId } ` ) ;
2024-05-06 15:41:26 +01:00
}
2024-04-25 10:12:17 +01:00
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 ) ;
2024-05-06 15:41:26 +01:00
if ( window . multiprobe _debug ) {
2024-05-27 12:56:49 +01:00
log ( "RECV" , ` Cursor position update for ${ clientId } , X= ${ cursorX } , Y= ${ cursorY } ` ) ;
2024-05-06 15:41:26 +01:00
}
2024-04-25 10:12:17 +01:00
}
break ;
}
case MessageType . ClientLeft :
2024-04-18 23:18:49 +01:00
{
const clientId = reader . readUInt ( ) ;
removeClient ( clientId ) ;
2024-05-06 15:41:26 +01:00
if ( window . multiprobe _debug ) {
2024-05-27 12:56:49 +01:00
log ( "RECV" , ` Client ${ clientId } left or switched pages ` ) ;
2024-05-06 15:41:26 +01:00
}
2024-04-25 10:12:17 +01:00
break ;
2024-04-18 23:18:49 +01:00
}
case MessageType . Ping :
{
const cursorX = reader . readFloat ( ) ;
const cursorY = reader . readInt ( ) ;
2024-04-20 17:20:23 +01:00
createPing ( cursorX * clientWidth , cursorY ) ;
2024-05-06 15:41:26 +01:00
if ( window . multiprobe _debug ) {
2024-05-27 12:56:49 +01:00
log ( "RECV" , ` Got a ping, X= ${ cursorX } , Y= ${ cursorY } ` ) ;
2024-05-06 15:41:26 +01:00
}
2024-04-25 10:12:17 +01:00
break ;
}
case MessageType . GroupData :
{
2024-04-26 03:01:06 +01:00
groupUIBase . style = "" ;
groupUsers . innerHTML = "" ;
groupTitle . innerText = reader . readShortString ( ) ;
2024-05-27 12:56:49 +01:00
const groupUserCount = reader . readUShort ( ) ;
2024-05-06 15:41:26 +01:00
if ( window . multiprobe _debug ) {
2024-05-27 12:56:49 +01:00
log ( "RECV" , ` Server sent group information for " ${ groupTitle . innerText } " ( ${ groupUserCount } clients) ` ) ;
2024-05-06 15:41:26 +01:00
}
2024-04-26 03:01:06 +01:00
for ( let i = 0 ; i < groupUserCount ; i ++ ) {
const groupUsername = reader . readShortString ( ) ;
const groupUserLocation = reader . readString ( ) ;
2024-05-06 15:41:26 +01:00
if ( window . multiprobe _debug ) {
2024-05-27 12:56:49 +01:00
log ( "RECV" , ` [GROUP USER] USERNAME= ${ groupUsername } , LOCATION= ${ groupUserLocation } ` ) ;
2024-05-06 15:41:26 +01:00
}
2024-04-26 03:01:06 +01:00
createGroupUser ( groupUsername , groupUserLocation ) ;
}
2024-04-25 10:12:17 +01:00
break ;
2024-04-18 23:18:49 +01:00
}
2024-05-27 12:56:49 +01:00
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 ;
}
2024-10-02 01:08:03 +01:00
case MessageType . BadgeUnlock :
{
2024-10-03 00:32:38 +01:00
const badgeTitle = reader . readString16 ( ) ;
const badgeDescription = reader . readString16 ( ) ;
const badgeIconUrl = reader . readString16 ( ) ;
2024-10-02 01:08:03 +01:00
createBadgePopup ( badgeTitle , badgeDescription , badgeIconUrl ) ;
2024-10-13 12:41:45 +01:00
break ;
}
case MessageType . TooOutOfDate :
{
ws . close ( ) ;
preventReconnect = true ;
break ;
2024-10-02 01:08:03 +01:00
}
2024-04-18 23:18:49 +01:00
}
}
ws . onmessage = ( e ) => {
e . data . arrayBuffer ( ) . then ( onMessage ) ;
}
function onCloseAndError ( ) {
if ( keepAliveInterval ) {
clearInterval ( keepAliveInterval ) ;
keepAliveInterval = undefined ;
2024-10-08 22:55:01 +01:00
debugConnectedTo . innerText = "Disconnected" ;
debugAuthedAs . innerText = "Not Authenticated" ;
2024-04-18 23:18:49 +01:00
}
ws = undefined ;
ready = false ;
2024-04-25 02:37:37 +01:00
setTimeout ( ( ) => doConnect ( localStorage [ "mpapikey" ] ) , 5000 ) ;
2024-04-18 23:18:49 +01:00
}
ws . onclose = onCloseAndError ;
2024-05-08 01:02:18 +01:00
ws . onerror = ( e ) => {
console . error ( e ) ;
}
2024-04-18 23:18:49 +01:00
}
2024-04-25 10:12:17 +01:00
function createOnlineDialog ( ) {
2024-10-02 01:08:03 +01:00
const bg = document . createElement ( "mp_container" ) ;
2024-04-25 10:12:17 +01:00
windowContainer = bg ;
2024-05-07 21:46:52 +01:00
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;" ;
2024-10-02 01:08:03 +01:00
const dialog = document . createElement ( "mp_container" ) ;
2024-04-25 10:12:17 +01:00
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 ) ;
2024-10-13 12:41:45 +01:00
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" ;
2024-04-25 10:12:17 +01:00
dialog . appendChild ( title ) ;
const subtitle = document . createElement ( "h5" ) ;
subtitle . innerText = ` Logged in as ${ localStorage [ "t00mp_username" ] } ` ;
subtitle . style . marginTop = ".5rem" ;
dialog . appendChild ( subtitle ) ;
2024-10-02 01:08:03 +01:00
const buttons = document . createElement ( "mp_container" ) ;
2024-04-25 10:12:17 +01:00
buttons . style . marginTop = "1rem" ;
2024-10-02 19:11:33 +01:00
const disconnectButton = document . createElement ( "mp_button" ) ;
2024-04-26 03:01:06 +01:00
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" ;
2024-10-02 19:11:33 +01:00
window . location . reload ( ) ;
2024-04-26 03:01:06 +01:00
} ;
buttons . appendChild ( disconnectButton ) ;
const manageAccountLink = document . createElement ( "a" ) ;
manageAccountLink . style . display = "none" ;
manageAccountLink . href = "https://multiprobe.eusv.net/" ;
manageAccountLink . target = "_blank" ;
buttons . appendChild ( manageAccountLink ) ;
2024-10-02 19:11:33 +01:00
const manageAccount = document . createElement ( "mp_button" ) ;
2024-04-26 03:01:06 +01:00
manageAccount . style . marginLeft = "1rem" ;
manageAccount . innerText = "Manage Account" ;
manageAccount . onclick = ( ) => {
manageAccountLink . click ( ) ;
}
buttons . appendChild ( manageAccount ) ;
2024-10-02 19:11:33 +01:00
const closeButton = document . createElement ( "mp_button" ) ;
2024-04-26 03:01:06 +01:00
closeButton . innerText = "Close" ;
closeButton . onclick = ( ) => {
bg . remove ( ) ;
windowContainer = null ;
} ;
closeButton . style . marginLeft = "1rem" ;
buttons . appendChild ( closeButton ) ;
2024-04-25 10:12:17 +01:00
dialog . appendChild ( buttons ) ;
2024-10-02 01:08:03 +01:00
document . documentElement . appendChild ( bg ) ;
2024-04-25 10:12:17 +01:00
}
2024-04-26 10:21:27 +01:00
function createUpdateDialog ( versionNumber ) {
2024-10-02 01:08:03 +01:00
const bg = document . createElement ( "mp_container" ) ;
2024-05-07 21:46:52 +01:00
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;" ;
2024-10-02 01:08:03 +01:00
const dialog = document . createElement ( "mp_container" ) ;
2024-04-26 10:21:27 +01:00
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 ) ;
2024-10-02 01:08:03 +01:00
const updateText = document . createElement ( "mp_text" ) ;
2024-04-26 10:21:27 +01:00
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 ) ;
2024-10-02 01:08:03 +01:00
const buttons = document . createElement ( "mp_container" ) ;
2024-04-26 10:21:27 +01:00
buttons . style . marginTop = "1rem" ;
2024-10-02 19:11:33 +01:00
const gotIt = document . createElement ( "mp_button" ) ;
2024-04-26 10:21:27 +01:00
gotIt . onclick = ( ) => {
bg . remove ( ) ;
}
gotIt . innerText = "Later" ;
buttons . appendChild ( gotIt ) ;
dialog . appendChild ( buttons ) ;
2024-10-02 01:08:03 +01:00
document . documentElement . appendChild ( bg ) ;
2024-04-26 10:21:27 +01:00
}
2024-04-25 10:12:17 +01:00
function createFirstTimeDialog ( ) {
if ( localStorage [ "mpshowfirsttime" ] && localStorage [ "mpshowfirsttime" ] === "false" ) {
return ;
}
2024-10-02 01:08:03 +01:00
const bg = document . createElement ( "mp_container" ) ;
2024-04-25 10:12:17 +01:00
windowContainer = bg ;
2024-05-07 21:46:52 +01:00
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;" ;
2024-10-02 01:08:03 +01:00
const dialog = document . createElement ( "mp_container" ) ;
2024-04-25 10:12:17 +01:00
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 ) ;
2024-04-26 17:34:34 +01:00
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 ) ;
2024-05-07 21:46:52 +01:00
const helpTip = document . createElement ( "li" ) ;
helpTip . innerHTML = "If you press <kbd>F1</kbd> you can open this help again." ;
actionList . appendChild ( helpTip ) ;
2024-10-02 01:08:03 +01:00
const buttons = document . createElement ( "mp_container" ) ;
2024-04-25 10:12:17 +01:00
buttons . style . marginTop = "1rem" ;
2024-10-02 19:11:33 +01:00
const gotIt = document . createElement ( "mp_button" ) ;
2024-04-25 10:12:17 +01:00
gotIt . onclick = ( ) => {
localStorage [ "mpshowfirsttime" ] = "false" ;
bg . remove ( ) ;
windowContainer = null ;
}
gotIt . innerText = "Got It" ;
buttons . appendChild ( gotIt ) ;
dialog . appendChild ( buttons ) ;
2024-10-02 01:08:03 +01:00
document . documentElement . appendChild ( bg ) ;
}
2024-10-02 22:25:08 +01:00
const badgeQueue = [ ] ;
let displaying = false ;
2024-10-02 01:08:03 +01:00
function createBadgePopup ( title , description , image ) {
2024-10-02 22:25:08 +01:00
if ( displaying ) {
badgeQueue . push ( [ title , description , image ] ) ;
return ;
}
displaying = true ;
2024-10-02 01:08:03 +01:00
const popup = document . createElement ( "mp_badge" ) ;
popup . classList . add ( "badgepop" ) ;
popup . classList . add ( "badgepopcreate" ) ;
2024-10-13 00:34:49 +01:00
const baseRow = document . createElement ( "mp_row" ) ;
popup . appendChild ( baseRow ) ;
const imageCol = document . createElement ( "mp_col_auto" ) ;
baseRow . appendChild ( imageCol ) ;
2024-10-02 01:08:03 +01:00
const badgeImage = new Image ( 32 , 32 ) ;
badgeImage . src = image ;
badgeImage . style = "image-rendering:pixelated" ;
badgeImage . onload = ( ) => {
popup . classList . remove ( "badgepopcreate" ) ;
popup . classList . add ( "badgepopin" ) ;
2024-10-02 22:25:08 +01:00
setTimeout ( ( ) => popupSound . play ( ) , 100 ) ;
2024-10-02 01:08:03 +01:00
} ;
2024-10-13 00:34:49 +01:00
imageCol . appendChild ( badgeImage ) ;
const textRowCol = document . createElement ( "mp_col" ) ;
baseRow . appendChild ( textRowCol ) ;
2024-10-02 01:08:03 +01:00
const badgeTitle = document . createElement ( "mp_text" ) ;
badgeTitle . innerHTML = ` <b> ${ title } </b> ` ;
2024-10-13 00:34:49 +01:00
textRowCol . appendChild ( badgeTitle ) ;
2024-10-02 01:08:03 +01:00
const badgeDescription = document . createElement ( "mp_text" ) ;
2024-10-13 00:34:49 +01:00
badgeDescription . innerHTML = description . replaceAll ( "\r" , "" ) . replaceAll ( "\n" , "<br>" ) ;
textRowCol . appendChild ( badgeDescription ) ;
2024-10-02 01:08:03 +01:00
popupRoot . appendChild ( popup ) ;
setTimeout ( ( ) => {
popup . classList . remove ( "badgepopin" ) ;
popup . classList . add ( "badgepopout" ) ;
setTimeout ( ( ) => {
popup . remove ( ) ;
} , 500 ) ;
2024-10-02 22:25:08 +01:00
displaying = false ;
if ( badgeQueue . length > 0 ) {
const badgeData = badgeQueue . splice ( 0 , 1 ) [ 0 ] ;
setTimeout ( ( ) => createBadgePopup ( badgeData [ 0 ] , badgeData [ 1 ] , badgeData [ 2 ] ) , 0 ) ;
}
2024-10-02 01:08:03 +01:00
} , 5350 ) ;
2024-04-25 10:12:17 +01:00
}
2024-10-02 22:25:08 +01:00
window . createBadgePopup = createBadgePopup ;
2024-04-25 10:12:17 +01:00
2024-04-25 02:37:37 +01:00
function createLoginDialog ( ) {
2024-10-02 01:08:03 +01:00
const bg = document . createElement ( "mp_container" ) ;
2024-04-25 10:12:17 +01:00
windowContainer = bg ;
2024-05-07 21:46:52 +01:00
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;" ;
2024-10-02 01:08:03 +01:00
const dialog = document . createElement ( "mp_container" ) ;
2024-04-25 02:37:37 +01:00
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" ;
2024-04-18 23:18:49 +01:00
bg . appendChild ( dialog ) ;
const title = document . createElement ( "h4" ) ;
2024-04-25 02:37:37 +01:00
title . innerText = "MultiProbe" ;
2024-04-26 03:01:06 +01:00
title . style . marginBottom = ".5rem" ;
2024-04-18 23:18:49 +01:00
dialog . appendChild ( title ) ;
2024-04-26 03:01:06 +01:00
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 ) ;
2024-04-25 02:37:37 +01:00
const loginForm = document . createElement ( "form" ) ;
dialog . appendChild ( loginForm ) ;
2024-04-18 23:18:49 +01:00
const username = document . createElement ( "input" ) ;
username . placeholder = "Enter Username" ;
username . maxLength = 32 ;
username . style . width = "12rem" ;
2024-04-25 02:37:37 +01:00
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 ) ;
2024-10-02 01:08:03 +01:00
const buttons = document . createElement ( "mp_container" ) ;
2024-04-18 23:18:49 +01:00
buttons . style . marginTop = "1rem" ;
2024-04-25 02:37:37 +01:00
loginForm . appendChild ( buttons ) ;
2024-10-02 19:11:33 +01:00
const submitButton = document . createElement ( "mp_button" ) ;
2024-04-25 02:37:37 +01:00
submitButton . innerText = "Connect" ;
2024-04-18 23:18:49 +01:00
buttons . appendChild ( submitButton ) ;
2024-10-02 19:11:33 +01:00
const doNotButton = document . createElement ( "mp_button" ) ;
2024-04-18 23:18:49 +01:00
doNotButton . innerText = "Close" ;
doNotButton . onclick = ( ) => bg . remove ( ) ;
doNotButton . style . marginLeft = "1rem" ;
buttons . appendChild ( doNotButton ) ;
2024-04-25 02:37:37 +01:00
2024-10-03 09:07:44 +01:00
function performLogin ( ) {
2024-04-25 02:37:37 +01:00
username . disabled = true ;
password . disabled = true ;
submitButton . disabled = true ;
doNotButton . disabled = true ;
2024-04-25 10:12:17 +01:00
title . innerText = "Authenticating..." ;
2024-10-02 22:25:08 +01:00
fetch ( ( windowLocation . replace ( "127.0.0.1" , "localhost" ) . includes ( "//localhost:" ) || window . multiprobe _connectLocal ) ? "http://localhost:38194/api/login" : "https://multiprobe.eusv.net/api/login" , {
2024-04-25 02:37:37 +01:00
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 => {
2024-04-25 10:12:17 +01:00
title . innerText = "Connecting to realtime server..." ;
2024-04-25 02:37:37 +01:00
localStorage [ "t00mp_username" ] = username . value ;
localStorage [ "mpapikey" ] = apiKey ;
doConnect ( apiKey ) ;
} ) ;
} else {
2024-04-25 10:12:17 +01:00
title . innerText = "Incorrect Login" ;
2024-04-25 02:37:37 +01:00
username . disabled = false ;
password . disabled = false ;
submitButton . disabled = false ;
2024-10-02 01:08:03 +01:00
doNotButton . disabled = false ;
2024-04-25 02:37:37 +01:00
}
} ) . catch ( err => {
console . error ( err ) ;
username . disabled = false ;
password . disabled = false ;
submitButton . disabled = false ;
doNotButton . disabled = false ;
} ) ;
}
2024-10-03 09:07:44 +01:00
submitButton . onclick = ( ) => performLogin ( ) ;
loginForm . onsubmit = ( e ) => {
e . preventDefault ( ) ;
performLogin ( ) ;
}
2024-10-02 01:08:03 +01:00
document . documentElement . appendChild ( bg ) ;
2024-04-18 23:18:49 +01:00
}
2024-10-02 19:11:33 +01:00
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" ;
2024-04-25 10:12:17 +01:00
openMenuButton . onclick = ( ) => {
2024-04-26 03:01:06 +01:00
if ( ws || localStorage [ "mpapikey" ] ) {
2024-04-25 10:12:17 +01:00
createOnlineDialog ( ) ;
} else {
createLoginDialog ( ) ;
}
} ;
2024-10-02 01:08:03 +01:00
document . documentElement . appendChild ( openMenuButton ) ;
2024-04-18 23:18:49 +01:00
2024-04-26 03:01:06 +01:00
if ( localStorage [ "mpapikey" ] && localStorage [ "mpapikey" ] !== "" && localStorage [ "mpconnectonload" ] === "true" ) {
2024-04-25 02:37:37 +01:00
doConnect ( localStorage [ "mpapikey" ] ) ;
2024-04-18 23:18:49 +01:00
}
2024-05-07 22:17:30 +01:00
console . log ( "[MP] Init complete." ) ;
2024-04-18 23:18:49 +01:00
} ) ( ) ;