Diep.io Server Selector + Restore Player Count

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

2022-03-05 या दिनांकाला. सर्वात नवीन आवृत्ती पाहा.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         Diep.io Server Selector + Restore Player Count
// @namespace    http://tampermonkey.net/
// @version      2.7.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 = ["lnd-sfo", "lnd-atl", "lnd-fra", "lnd-syd"];
    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=${regions[0]} 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=${regions[1]} 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}'>ATL</button>
    <button class='child' type='button' id=${regions[2]} 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=${regions[3]} 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}'>SYD</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: modes[0],
        region: regions[0],
    }
    const serverWithoutCSS = {};

    modes.forEach(function(gamemode) {
        serverWithoutCSS[gamemode] = {};
        regions.forEach(function(region) {
            serverWithoutCSS[gamemode][region] = { 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
        }) {
            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 lnd'))
                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 lnd'))
                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 lnd'))
                args[0] += ` ‖ ${PLAYER_COUNT} players`;
            return f.apply(_this, args);
        }
    });
})();