// ==UserScript==
// @name Diep.io Server Selector + Restore Player Count
// @namespace http://tampermonkey.net/
// @version 2.1.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
// @grant GM_setClipboard
// @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"];
var PLAYER_COUNT = 0;
var _resp = await fetch(`https://api-game.rivet.gg/v1/matchmaker/lobbies/list`);
var lists = await _resp.json();
if (!localStorage.lists) {
localStorage.lists = JSON.stringify({
timestamp: Date.now(),
list: lists,
});
}
setInterval(async function() {
if (!localStorage.lists) {
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();
localStorage.lists = JSON.stringify({
timestamp: Date.now(),
list: lists,
});
console.log('Refreshed server list.');
}
}, 5000);
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 show server selector
<br>
<p style="font-size:12px">Created by Altanis and Bismuth</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 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].length; n++) {
json += `<button class='child' type='button' id='choice${n}'value='${n}'style='width:6vw; height:3vh; border-radius: 0.5vw; font-family:Ubuntu; opacity:60%; background:${colors[n % 8]}; color:#FFFFFF; font-style:normal; font-size:0.9vw; ${textShadow}'> ${currentServer.includes(serverWithoutCSS[choices.mode][choices.region][n].slice(0,8))? '>': ''}${serverWithoutCSS[choices.mode][choices.region][n].slice(0,8)}</button>`;
}
document.getElementById('choice').innerHTML = `${json}`;
for (let n = 0; n < serverWithoutCSS[choices.mode][choices.region].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 if (button.value === 'copy') {
GM_setClipboard("diep.io/#" + getServerLink(currentServer.split("://")[1].split("-80.lobby")[0]).toUpperCase());
}
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%'
}
const choices = {
mode: 'ffa',
region: 'do-sfo'
}
const serverWithoutCSS = {
"ffa": {
"do-sfo": [],
"do-nyc": [],
"do-fra": [],
"do-sgp": []
},
"survival": {
"do-sfo": [],
"do-nyc": [],
"do-fra": [],
"do-sgp": []
},
"teams": {
"do-sfo": [],
"do-nyc": [],
"do-fra": [],
"do-sgp": []
},
"4teams": {
"do-sfo": [],
"do-nyc": [],
"do-fra": [],
"do-sgp": []
},
"dom": {
"do-sfo": [],
"do-nyc": [],
"do-fra": [],
"do-sgp": []
},
"tag": {
"do-sfo": [],
"do-nyc": [],
"do-fra": [],
"do-sgp": []
},
"maze": {
"do-sfo": [],
"do-nyc": [],
"do-fra": [],
"do-sgp": []
},
"sandbox": {
"do-sfo": [],
"do-nyc": [],
"do-fra": [],
"do-sgp": []
},
};
const GAMEMODES_MAP = ['survival', 'tag', '4teams', 'sandbox', 'maze', 'dom', 'ffa', 'teams'];
const REGIONS_MAP = ['do-fra', 'do-sgp', 'do-sfo', 'do-nyc'];
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')) return alert('API of Rivet is down!');
if (body.message === 'too many requests') return alert('Ratelimit reached! Wait 15 minutes.');
PLAYER_COUNT = 0;
body.game_modes.forEach(function(g) { g.regions.forEach(function(r) { r.lobbies.forEach(function(l) { PLAYER_COUNT += l.total_player_count }); }); });
const lobbies = body.game_modes[GAMEMODES_MAP.indexOf(mode)]?.regions[REGIONS_MAP.indexOf(region)]?.lobbies;
if (!lobbies) return alert('Very strange... no lobbies exist...');
lobbies.forEach(function({ lobby_id }) {
lobby_id = `${lobby_id}-80.lobby.${region}.hiss.io:443`;
if (!serverWithoutCSS[mode][region].some(function(host) { return host === lobby_id })) {
serverWithoutCSS[mode][region].push(lobby_id);
}
});
refreshHTML();
}
function appendServers(mode, region) {
fetchServer(mode, region);
}
function connectTo(mode, region, number) {
if (!serverWithoutCSS[mode][region][number]) return;
currentServer = "wss://" + serverWithoutCSS[mode][region][number];
do_connect = true;
unsafeWindow.input.execute('lb_reconnect');
}
unsafeWindow.servers = serverWithoutCSS;
unsafeWindow.WebSocket = new Proxy(WebSocket,{
construct(t, args) {
if (do_connect) args[0] = currentServer;
currentServer = args[0];
unsafeWindow.serverURL = args[0];
do_connect = false;
override = true;
return Reflect.construct(t, args)}
});
var _fetch = unsafeWindow.fetch;
unsafeWindow.fetch = function(endpoint, opts) {
return _fetch(endpoint, opts)
.then(function(response) {
return new Promise(function(res) {
response.clone().json()
.then(function(body) {
if (!override) return res(response);
body.lobby.lobby_id = currentServer.split('wss://')[1].split('-80.lobby')[0];
body.lobby.region.region_id = choices.region;
const blob = new Blob([JSON.stringify(body)], { type: 'application/json' });
return res(new Response(blob));
override = false;
})
.catch(function(er) {
return res(response);
});
});
});
}
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";
}
}
}
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')) return alert('API of Rivet is down!');
if (body.message === 'too many requests') return alert('Ratelimit reached! Wait 15 minutes.');
PLAYER_COUNT = 0;
body.game_modes.forEach(function(g) { g.regions.forEach(function(r) { r.lobbies.forEach(function(l) { PLAYER_COUNT += l.total_player_count }); }); });
}
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);
}
});
})();