// ==UserScript==
// @name Diep.io Server Selector + Restore Player Count
// @namespace http://tampermonkey.net/
// @version 2.6.0
// @description Select servers from different gamemodes and regions for Diep. Also restores player count for Diep.io.
// @author Altanis + Bismuth
// @match *://diep.io/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant unsafeWindow
// @license MIT
// ==/UserScript==
(async function() {
// Special credits to Diep7444 for paving the way to an effective region changer.
const textShadow = 'text-shadow:black 0.18vh 0, black -0.18vh 0, black 0 -0.18vh, black 0 0.18vh, black 0.18vh 0.18vh, black -0.18vh 0.18vh, black 0.18vh -0.18vh, black -0.18vh -0.18vh, black 0.09vh 0.18vh, black -0.09vh 0.18vh, black 0.09vh -0.18vh, black -0.09vh -0.18vh, black 0.18vh 0.09vh, black -0.18vh 0.09vh, black 0.18vh -0.09vh, black -0.18vh -0.09vh'
const regions = ["do-sfo", "do-nyc", "do-fra", "do-sgp"];
const modes = ["ffa", "survival", "teams", "4teams", "dom", "tag", "maze", "sandbox"];
const colors = ["#E8B18A", "#E666EA", "#9566EA", "#6690EA", "#E7D063", "#EA6666", "#92EA66", "#66EAE6"];
let key = 'KeyT'; // Go to https://keycode.info, press desired key, copy event.code, paste into quotes.
let special = 'alt'; // alt, shift, ctrl, meta (Windows/Command)
let includeUncommon = true;
if (!['alt', 'shift', 'ctrl', 'meta'].includes(special)) special = 'alt'; // Default = Alt.
var PLAYER_COUNT = 0;
unsafeWindow.API_STATE = { code: 0, message: 'Connecting...'};
const modeHTML = document.createElement("div");
document.body.appendChild(modeHTML);
modeHTML.innerHTML = `
<div class='parent' id='ServerSelector' style='user-select:none; position:fixed; top:25%; right:0.5%; text-align:center; width:15vw; font-family:Ubuntu; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'><div class='child' style='line-height:2vh; opacity:75%'>
TAB to toggle server selector
<br>
<p style="font-size:12px;">Created by Altanis and Bismuth</p>
<p style="font-size:12px;" id="status">Status: ${unsafeWindow.API_STATE.message}</p>
<hr>
</div>
<p style="font-size:10px">Game Mode</p>
<button class='child' type='button' id='ffa' value='mode' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[7]}; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'>FFA</button>
<button class='child' type='button' id='survival' value='mode' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[6]}; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'>SURV</button>
<button class='child' type='button' id='teams' value='mode' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[5]}; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'>2TDM</button>
<button class='child' type='button' id='4teams' value='mode' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[4]}; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'>4TDM</button>
<button class='child' type='button' id='dom' value='mode' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[3]}; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'>DOM</button>
<button class='child' type='button' id='tag' value='mode' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[2]}; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'>TAG</button>
<button class='child' type='button' id='maze' value='mode' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[1]}; color:#FFFFFF; font-style:normal; font-size:0.9vw;${textShadow}'>MAZE</button>
<button class='child' type='button' id='sandbox' value='mode' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[0]}; color:#FFFFFF; font-style:normal; font-size:0.9vw;${textShadow}'>SBX</button>
<p style="font-size:10px">Region</p>
<button class='child' type='button' id='do-sfo' value='region' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[3]}; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'>SFO</button>
<button class='child' type='button' id='do-nyc' value='region' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[2]}; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'>NYC</button>
<button class='child' type='button' id='do-fra' value='region' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[6]}; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'>FRA</button>
<button class='child' type='button' id='do-sgp' value='region' style='width:3.5vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[5]}; color:#FFFFFF; font-style:normal; font-size:0.9vw;${textShadow}'>SGP</button>
<div class='parent' id='choice' style='user-select:none; position:relative; top:65%; left:0.5%; text-align:center; width:15vw; font-family:Ubuntu; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'>
<br>
</div>
</div>
`
document.getElementById('ServerSelector').style.display = 'block';
for (let mode of modes) {
addButtonListener(mode);
}
for (let region of regions) {
addButtonListener(region);
}
function refreshStatus() { document.getElementById('status').innerHTML = `Status: ${unsafeWindow.API_STATE.message}`; }
function refreshHTML() {
let json = `<div class='parent' id='choice' style='user-select:none; position:relative; top:65%; left:0.5%; text-align:center; width:15vw; font-family:Ubuntu; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'><p style="font-size:12px"> </p><div class='child' style='line-height:2vh; opacity:75%'> Servers for ${choices.mode} ${choices.region}<hr></div>`;
for (let n = 0; n < serverWithoutCSS[choices.mode][choices.region].lobbies.length; n++) {
json += `<button class='child' type='button' id='choice${n}'value='${n}'style='width:9vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[n % 8]}; color:#FFFFFF; font-style:normal; font-size:0.9vw; filter: ${currentServer.includes(serverWithoutCSS[choices.mode][choices.region].lobbies[n].slice(0,8))? 'brightness(100%)': 'brightness(50%)'}; ${textShadow}'>${serverWithoutCSS[choices.mode][choices.region].lobbies[n].slice(0,8)} (${serverWithoutCSS[choices.mode][choices.region].info[serverWithoutCSS[choices.mode][choices.region].lobbies[n].slice(0, 36)].total_player_count || '??'}/${serverWithoutCSS[choices.mode][choices.region].info[serverWithoutCSS[choices.mode][choices.region].lobbies[n].slice(0, 36)].max_players_direct || '??'})</button>`;
}
document.getElementById('choice').innerHTML = `${json}`;
for (let n = 0; n < serverWithoutCSS[choices.mode][choices.region].lobbies.length; n++) {
addButtonListener('choice' + n);
}
}
function buttonAction(id) {
let button = document.getElementById(id);
if (button.value === 'mode') {
choices.mode = id;
fetchServer(choices.mode, choices.region, 3);
} else if (button.value === 'region') {
choices.region = id;
fetchServer(choices.mode, choices.region, 3);
} else {
(connectTo(choices.mode, choices.region, button.value));
}
refreshHTML();
}
function addButtonListener(id) {
document.getElementById(id).addEventListener("click", function() {
buttonAction(id)
});
document.getElementById(id).addEventListener("mouseenter", function() {
lightenColor(id)
});
document.getElementById(id).addEventListener("mouseleave", function() {
resetColor(id)
});
}
function lightenColor(id) {
document.getElementById(id).style.opacity = '100%'
}
function resetColor(id) {
document.getElementById(id).style.opacity = '60%'
}
function getServerLink(server) {
let link = '';
for (const char of server) {
const code = char.charCodeAt(0);
const value = (`00${code.toString(16)}`).slice(-2);
link += value.split("").reverse().join("");
}
return link;
}
async function calcPlayerCount() {
const body = JSON.parse(localStorage.lists)?.list;
if (!body.hasOwnProperty('game_modes') && !body.hasOwnProperty('message')) {
unsafeWindow.API_STATE.code = 2; return;
};
if (body.message === 'too many requests') {
unsafeWindow.API_STATE.code = 3; return;
};
PLAYER_COUNT = 0;
body.game_modes.forEach(function(g) {
if (['dom', 'sandbox', 'survival', 'tag'].includes(g.game_mode_name) && !includeUncommon) return;
g.regions.forEach(function(r) {
r.lobbies.forEach(function(l) {
PLAYER_COUNT += l.total_player_count;
});
});
});
}
const choices = {
mode: 'ffa',
region: 'do-sfo'
}
const serverWithoutCSS = {
"ffa": {
"do-sfo": { lobbies: [], info: {} },
"do-nyc": { lobbies: [], info: {} },
"do-fra": { lobbies: [], info: {} },
"do-sgp": { lobbies: [], info: {} },
},
"survival": {
"do-sfo": { lobbies: [], info: {} },
"do-nyc": { lobbies: [], info: {} },
"do-fra": { lobbies: [], info: {} },
"do-sgp": { lobbies: [], info: {} },
},
"teams": {
"do-sfo": { lobbies: [], info: {} },
"do-nyc": { lobbies: [], info: {} },
"do-fra": { lobbies: [], info: {} },
"do-sgp": { lobbies: [], info: {} },
},
"4teams": {
"do-sfo": { lobbies: [], info: {} },
"do-nyc": { lobbies: [], info: {} },
"do-fra": { lobbies: [], info: {} },
"do-sgp": { lobbies: [], info: {} },
},
"dom": {
"do-sfo": { lobbies: [], info: {} },
"do-nyc": { lobbies: [], info: {} },
"do-fra": { lobbies: [], info: {} },
"do-sgp": { lobbies: [], info: {} },
},
"tag": {
"do-sfo": { lobbies: [], info: {} },
"do-nyc": { lobbies: [], info: {} },
"do-fra": { lobbies: [], info: {} },
"do-sgp": { lobbies: [], info: {} },
},
"maze": {
"do-sfo": { lobbies: [], info: {} },
"do-nyc": { lobbies: [], info: {} },
"do-fra": { lobbies: [], info: {} },
"do-sgp": { lobbies: [], info: {} },
},
"sandbox": {
"do-sfo": { lobbies: [], info: {} },
"do-nyc": { lobbies: [], info: {} },
"do-fra": { lobbies: [], info: {} },
"do-sgp": { lobbies: [], info: {} },
},
};
unsafeWindow.API_STATE = new Proxy(unsafeWindow.API_STATE, {
set: function(t, k, v) {
t[k] = v;
if ([0, 1, 2, 4].includes(v)) {
t.message = v === 0 ? 'Connecting...' : (v === 1 ? 'Ready!' : (v === 2 ? 'Endpoint is down.' : 'Lobbies don\'t exist.'))
} else if (v === 3) {
const date = new Date(Date.now());
const curMin = date.getMinutes();
const intervals = [0, 15, 30, 45];
intervals.sort((a, b) => {
return Math.abs(curMin - a) - Math.abs(curMin - b);
});
let retry_at = intervals[0] < curMin ? intervals[1] : intervals[0];
let time = `${retry_at === 0 ? date.getHours() + 1 : date.getHours()}:${retry_at.length === 1 ? `0${retry_at}` : retry_at}`;
t.message = `Ratelimited! Retry-At: ${time}.`;
const interval = setInterval(function() {
let [hours, minutes] = time.split(':').map(t => parseInt(t));
if (new Date(Date.now()).getHours() >= hours && new Date(Date.now()).getMinutes() >= minutes) {
t.code = 1;
t.message = 'Ready!';
refreshStatus();
clearInterval(interval);
}
}, 150);
}
refreshStatus();
return true;
}
});
if (!localStorage.lists) {
var _resp = await fetch(`https://api-game.rivet.gg/v1/matchmaker/lobbies/list`);
var lists = await _resp.json();
unsafeWindow.API_STATE.code = _resp.status === 200 ? 1 : (_resp.status === 429 ? 3 : 2);
localStorage.lists = JSON.stringify({
timestamp: Date.now(),
list: lists,
});
}
setInterval(async function() {
if (!localStorage.lists) {
var _resp = await fetch(`https://api-game.rivet.gg/v1/matchmaker/lobbies/list`);
var lists = await _resp.json();
unsafeWindow.API_STATE.code = _resp.status === 200 ? 1 : (_resp.status === 429 ? 3 : 2);
localStorage.lists = JSON.stringify({
timestamp: Date.now(),
list: lists,
});
}
if (Date.now() - JSON.parse(localStorage.lists)?.timestamp > 3e5) {
var _resp = await fetch(`https://api-game.rivet.gg/v1/matchmaker/lobbies/list`);
var lists = await _resp.json();
unsafeWindow.API_STATE.code = _resp.status === 200 ? 1 : (_resp.status === 429 ? 3 : 2);
localStorage.lists = JSON.stringify({
timestamp: Date.now(),
list: lists,
});
console.log('Refreshed server list.');
}
if (unsafeWindow.API_STATE.code === 0) unsafeWindow.API_STATE.code = 1;
}, 5000);
const GAMEMODES_MAP = [];
const REGIONS_MAP = [];
try {
JSON.parse(localStorage.lists).list.game_modes.forEach(function(data) {
GAMEMODES_MAP.push(data.game_mode_name);
if (REGIONS_MAP.length === 0) {
data.regions.forEach(function(data2) {
REGIONS_MAP.push(data2.region_name);
});
}
});
} catch (er) {
alert('API of Rivet is down!');
unsafeWindow.API_STATE.code = 2;
}
var do_connect = false;
var currentServer = '';
var override = false;
async function fetchServer(mode, region) {
const body = JSON.parse(localStorage.lists)?.list;
if (!body.hasOwnProperty('game_modes') && !body.hasOwnProperty('message')) {
unsafeWindow.API_STATE.code = 2;
return;
}
if (body.message === 'too many requests') {
unsafeWindow.API_STATE.code = 3;
return;
}
calcPlayerCount();
const lobbies = body.game_modes[GAMEMODES_MAP.indexOf(mode)]?.regions[REGIONS_MAP.indexOf(region)]?.lobbies;
if (!lobbies) {
unsafeWindow.API_STATE.code = 4;
return;
};
lobbies.forEach(function({
lobby_id, total_player_count, max_players_direct
}) {
if (mode === 'sandbox') {
total_player_count = '‽';
max_players_direct = '‽';
}
serverWithoutCSS[mode][region].info[lobby_id] = { total_player_count, max_players_direct };
if (!serverWithoutCSS[mode][region].lobbies.some(function(host) {
return host === `${lobby_id}-80.lobby.${region}.hiss.io:443`
})) {
serverWithoutCSS[mode][region].lobbies.push(`${lobby_id}-80.lobby.${region}.hiss.io:443`);
}
});
unsafeWindow.API_STATE.code = 1;
refreshHTML();
}
function appendServers(mode, region) {
fetchServer(mode, region);
}
function connectTo(mode, region, number) {
if (!serverWithoutCSS[mode][region].lobbies[number]) return;
currentServer = "wss://" + serverWithoutCSS[mode][region].lobbies[number];
do_connect = true;
unsafeWindow.input.execute('lb_reconnect');
}
unsafeWindow.servers = serverWithoutCSS;
var _WebSocket = unsafeWindow.WebSocket;
unsafeWindow.WebSocket = function(wss) {
let triggered = do_connect;
if (do_connect) wss = currentServer;
currentServer = wss;
const socket = new _WebSocket(wss);
socket.addEventListener('error', function(er) {
if (triggered)
alert('Connection to lobby failed, redirecting...'); // This is most likely due to the target server closing or reaching player limit.
});
do_connect = false;
return socket;
};
(function(realHTMLInputElement) {
Object.defineProperty(HTMLTextAreaElement.prototype, 'value', {
set: function(value) {
if (!value.startsWith('diep.io/#')) return realHTMLInputElement.set.call(this, value);
let [serverID, party] = value.replace('diep.io/#', '').split('00');
serverID = getServerLink(currentServer.split("://")[1].split("-80.lobby")[0]).toUpperCase();
value = `https://diep.io/#${serverID}00${party}`;
return realHTMLInputElement.set.call(this, value);
},
});
}(Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')));
document.body.onkeydown = function(e) {
if (String.fromCharCode(e.keyCode) === '\t') {
if (document.getElementById('ServerSelector').style.display === "none") {
document.getElementById('ServerSelector').style.display = "block";
document.getElementById('choice').style.display = "block";
} else {
document.getElementById('ServerSelector').style.display = "none";
document.getElementById('choice').style.display = "none";
}
}
if (e.code === key && e[`${special}Key`]) {
includeUncommon = !includeUncommon;
calcPlayerCount();
}
}
calcPlayerCount();
setInterval(calcPlayerCount, 60000);
const crx = CanvasRenderingContext2D.prototype;
crx.fillText = new Proxy(crx.fillText, {
apply(f, _this, args) {
if (args[0].includes('ms do'))
args[0] += ` ‖ ${PLAYER_COUNT} players`;
return f.apply(_this, args);
}
});
crx.strokeText = new Proxy(crx.strokeText, {
apply(f, _this, args) {
if (args[0].includes('ms do'))
args[0] += ` ‖ ${PLAYER_COUNT} players`;
return f.apply(_this, args);
}
});
crx.measureText = new Proxy(crx.measureText, {
apply(f, _this, args) {
if (args[0].includes('ms do'))
args[0] += ` ‖ ${PLAYER_COUNT} players`;
return f.apply(_this, args);
}
});
})();