Diep.io Server Selector + Restore Player Count

Select servers from different gamemodes and regions for Diep. Also restores player count for Diep.io.

2022-01-20 기준 버전입니다. 최신 버전을 확인하세요.

// ==UserScript==
// @name         Diep.io Server Selector + Restore Player Count
// @namespace    http://tampermonkey.net/
// @version      2.0.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==

// 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;

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 response = await fetch(`https://api-game.rivet.gg/v1/matchmaker/lobbies/list`);
    const body = await response.json();

    if (!body.hasOwnProperty('game_modes')) return alert('API of Rivet is down!');

    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' });
                console.log(res(new Response(blob)));
                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 response = await fetch(`https://api-game.rivet.gg/v1/matchmaker/lobbies/list`);
    const body = await response.json();

    if (!body.hasOwnProperty('game_modes')) return alert('API of Rivet is down!');
    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 }); }); });
    console.log(PLAYER_COUNT);
    refreshHTML();
}

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);
    }
});