diep.io Spade Script - Public

Press TAB to show Server Selector.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name		diep.io Spade Script - Public
// @namespace	spade-squad.com
// @author		DiE ♠ BiTCH // ♠-squad
// @version		1.0.0
// @description	Press TAB to show Server Selector.
// @homepage	http://spade-squad.com
// @icon		http://spade-squad.com/pub/userscript/icon64.png
// @match		*://*.diep.io/*
// @run-at      document-start
// @grant		GM_addStyle
// @grant		GM_getResourceText
// @grant		GM_setClipboard
// @grant		GM_notification
// @grant		unsafeWindow
// @require     http://code.jquery.com/jquery-3.2.1.slim.min.js
// @resource	spadeCSS http://spade-squad.com/pub/userscript/spadeuserscript.css
// ==/UserScript==

//TODO: handle keypress fix partycache

/* eslint-disable */
let spadeCSS = GM_getResourceText("spadeCSS");
GM_addStyle(spadeCSS);
/* eslint-enable */

(function () {
    "use strict";

    let defaultConfig = {
        "hotkey": {
            "connectUI": "\t" // TAB
        },
        "gameModeName": {
            "ffa": "FFA",
            "survival": "Survival",
            "teams": "2TDM",
            "4teams": "4TDM",
            "dom": "Domination",
            "maze": "Maze",
            "tag": "Tag",
            "sandbox": "Sandbox"
        },
        "team": {
            "blue": [[0, 178, 225, 255], [76, 201, 234, 255]],
            "red": [[241, 78, 84, 255], [245, 131, 135, 255]],
            "green": [[0, 225, 110, 255], [76, 234, 153, 255]],
            "purple": [[191, 127, 245, 255], [210, 165, 248, 255]]
        },
        "settings": {
            "firstRunDisable": false
        },
        "script": {
            "currentServer": {},
            "debugging": false
        }
    };

    const isObject = (obj) => {
        return obj instanceof Object && obj.constructor === Object;
    };

    const dataStorage = {
        set (key, value) {
            localStorage.setItem(key, JSON.stringify(value));
        },
        get (key) {
            const value = localStorage.getItem(key);
            return value && JSON.parse(value);
        }
    };

    (function () {
        let privateConfig;
        unsafeWindow.Config = {};
        const proxify = (obj) => {
            for (const subkey in obj) {
                if (Object.prototype.hasOwnProperty.call(obj, subkey)) {
                    unsafeWindow.Config[subkey] = new Proxy(obj[subkey], {
                        get (target, propKey, receiver) {
                            if (propKey in target) {
                                return Reflect.get(target, propKey, receiver);
                            }
                            throw new ReferenceError("Unknown property: " + propKey);
                        },
                        set (target, propKey, value, receiver) {
                            target[propKey] = value;
                            dataStorage.set("spadepublic", obj);
                            return Reflect.set(target, propKey, value, receiver);
                        }
                    });
                }
            }
        };

        if (dataStorage.get("spadepublic")) {
            privateConfig = dataStorage.get("spadepublic");
        } else {
            dataStorage.set("spadepublic", defaultConfig);
            privateConfig = defaultConfig;
        }

        proxify(privateConfig);

        unsafeWindow.resetConfig = () => {
            dataStorage.set("spadepublic", defaultConfig);
            unsafeWindow.Config = {};
            privateConfig = defaultConfig;
            proxify(privateConfig);
        };
    })();

    let playing = () => {
        return false;
    };

    $(window).on("load", () => {
        (function setBack () {
            try {
                if (unsafeWindow.input.should_prevent_unload) {
                    playing = () => {
                        return !!unsafeWindow.input.should_prevent_unload();
                    };
                }
            } catch (error) {
                setTimeout(() => {
                    setBack();
                }, 100);
            }
        })();
    });

    let canvas, ctx;
    $(() => {
        canvas = $("#canvas").get(0);
        ctx = canvas.getContext("2d");
    });

    HTMLElement.prototype.focus = () => {};
    HTMLElement.prototype.blur = () => {};

    const capitalizeFirstLetter = (string) => {
        return string && string[0].toUpperCase() + string.slice(1);
    };

    const createEl = (elObj, parent) => {
        let element;
        if (typeof elObj === "string") {
            element = $(document.createTextNode(elObj));
        } else {
            element = $(`<${elObj.node}>`);
            if (elObj.att) {
                let attributes = elObj.att;
                for (let key in attributes) {
                    if (attributes.hasOwnProperty(key)) {
                        if (key.charAt(0) === "@") {
                            element.attr(key.substring(1), attributes[key]);
                        } else {
                            element.text(attributes[key]);
                        }
                    }
                }
            }
            if (elObj.evl) {
                element.on(elObj.evl.type, elObj.evl.f);
            }
            if (elObj.child) {
                elObj.child.forEach((node) => {
                    createEl(node, element.get(0));
                });
            }
        }
        if (parent) {
            parent.append(element.get(0));
        }
        return element;
    };

    const scriptBody = $("<body>").get(0);
    createEl({
        node: "div", att: {"@id": "main", "@class": "base"},
        child: [ {
            node: "div", att: {"@class": "top"},
            child: [ {
                node: "h2", att: {"@class": "title"},
                child: [ {
                    node: "span", att: {"@class": "spadesymbol", textContent: "♠"}
                }, " Select diep.io Server ", {
                    node: "span", att: {"@class": "spadesymbol", textContent: "♠"}
                }, {
                } ]
            }, {
                node: "span", att: {"@class": "menu"},
                child: [ {
                    node: "a", att: {"@class": "menuButton close", textContent: "X"},
                    evl: {
                        type: "click",
                        f: () => {
                            $(".appear").removeClass("appear");
                        }}
                } ]
            }]
        }, {
            node: "lable", att: {textContent: "Gamemode"},
            child: [ {
                node: "select", att: {"@id": "gamemode"}
            } ]
        }, {
            node: "lable", att: {textContent: "Server"},
            child: [ {
                node: "select", att: {"@id": "server"}
            } ]
        }, {
            node: "span", att: {"@id": "more", textContent: "+"}
        }, {
            node: "div",
            child: [ {
                node: "button", att: {"@type": "button", "@id": "connect", "@class": "commandButton", textContent: "Connect"},
                evl: {
                    type: "click",
                    f: () => {
                        connectServer();
                        setTimeout(() => {
                            $(".appear").removeClass( "appear" );
                        }, 800);
                    }}
            }, {
                node: "button", att: {"@type": "button", "@id": "disconnect", "@class": "commandButton", textContent: "Disconnect"},
                evl: {
                    type: "click",
                    f: () => {
                        unsafeWindow.m28nOverride = false;
                        unsafeWindow.input.execute("lb_reconnect");
                    }}
            } ]
        }, {
            node: "p", att: {"@class": "ctag", textContent: "// © "},
            child: [ {
                node: "a", att: {"@class": "spadeweb", "@href": "http://spade-squad.com", "@target": "_blank", textContent: "spade-squad.com"}
            } ]
        } ]
    }, scriptBody);

    $(() => {
        // View script info only on firstRun
        if (!unsafeWindow.Config.settings.firstRunDisable) {
            createEl({
                node: "div", att: {"@id": "firstrun", "@class": "base appear"},
                child: [ {
                    node: "div", att: {"@class": "top"},
                    child: [ {
                        node: "h2", att: {"@class": "title"},
                        child: [ {
                            node: "span", att: {"@class": "spadesymbol", textContent: "♠"}
                        }, " Spade Script - First Run ", {
                            node: "span", att: {"@class": "spadesymbol", textContent: "♠"}
                        } ]
                    }, {
                        node: "span", att: {"@class": "menu"},
                        child: [ {
                            node: "a", att: {"@class": "menuButton close", textContent: "X"},
                            evl: {
                                type: "click",
                                f: () => {
                                    $("#firstrun").removeClass("appear");
                                    unsafeWindow.Config.settings.firstRunDisable = true;
                                }}
                        } ]
                    } ]
                }, {
                    node: "ul", att: {"@class": "settingslist"},
                    child: [ {
                        node: "h3", att: {"@class": "intro", textContent: "Spade Script was successfully installed and started"}
                    }, {
                        node: "li",
                        child: ["- Press TAB to toggle the Server Selector."]
                    }, {
                        node: "li",
                        child: ["- In the top right corner of a window is a X-Button to close it and O-Button so accesss aditional options."]
                    }, {
                        node: "li",
                        child: ["- You are using the PUBLIC version with reduced features. Check out our Website/Discord for updates and please report any bugs."]
                    }, {
                        node: "li",
                        child: ["- This info is only shown on the first start. Just close it and it won't appear again."]
                    }
                    ]
                } ]
            }, scriptBody);
        }
    });

    $("body").after(scriptBody);

    /* jshint ignore:start */
    const fetchServer = async (mode, times, ids = []) => {
        const url = "https://api.n.m28.io";
        const $serverSelect = $("#server");
        const $moreButton = $("#more");
        $moreButton.addClass("spin");

        for (let i = 0; i < times; i++) {
            try {
                const response = await fetch(`${url}/endpoint/diepio-${mode}/findEach/`);
                const body = await response.json();
                if (body.hasOwnProperty("servers")) {
                    Object.entries(body.servers).forEach(([key, val]) => {
                        if (!ids.some((id) => {
                            return id === val.id;
                        })) {
                            ids.push(val.id);
                            const txt = key.replace(/(linode-|vultr-)/, "") + ` - ${val.id.toUpperCase()}`;
                            $serverSelect.append($("<option>", {
                                "value": JSON.stringify(val),
                                "text": capitalizeFirstLetter(txt)
                            }));
                        }
                    });
                }
            } catch (err) {
                console.error(err);
            }
        }
        $("#server option").detach().sort((a, b) => {
            a = $(a);
            b = $(b);
            return ((a.text() > b.text()) ?
                1 :
                (a.text() < b.text()) ?
                    -1 :
                    0);
        }).appendTo($serverSelect).filter(":first").attr("selected", true);
        $moreButton.on("click", () => {
            fetchServer(mode, 4, ids);
        }).removeClass("spin");
    };
    /* jshint ignore:end */

    $(() => {
        const $gamemode = $("#gamemode");
        Object.entries(unsafeWindow.Config.gameModeName).forEach(([key, val]) => {
            $gamemode.append($("<option>", {
                "value": key,
                "text": val
            }));
        });
        $gamemode.change((event) => {
            $("#server").empty();
            fetchServer($(event.currentTarget).val(), 8, []);
        }).trigger("change");
    });

    $(() => {
        unsafeWindow.m28n.findServerPreference = (endpoint, options, cb) => {
            if (unsafeWindow.m28nOverride)
                options(null, [JSON.parse($( "#server option:selected" ).val())]);
            if (typeof options == "function") {
                cb = options;
                options = {};
            }
            unsafeWindow.m28n.findServers(endpoint, (err, r) => {
                if (err)
                    return cb(err);
                var availableRegions = [];
                for (var region in r.servers) {
                    availableRegions.push(region);
                }
                if (availableRegions.length === 0) {
                    cb("Couldn't find any servers in any region");
                    return;
                }
                if (availableRegions.length === 1) {
                    for (var region in r.servers) {
                        cb(null, [r.servers[region]]);
                        return;
                    }
                }
                unsafeWindow.m28n.findRegionPreference(availableRegions, options, (err, regionList) => {
                    if (err)
                        return cb(err);
                    var serverList = regionList.map((region) => {
                        return r.servers[region];
                    });
                    cb(null, serverList);
                });
            });
        };
    });

    const connectServer = () => {
        if ($("#server option:selected").length === 1) {
            const $autojoin = $("#autojoin");
            const $connect = $("#connect");

            let Observer = new MutationObserver(mutation => {
                mutation.forEach(mutation => {
                    if (mutation.target.style.display === "block") {
                        if ($autojoin.prop("checked")) {
                            const sequence = ["keydown", "keyup"];
                            sequence.forEach(event => {
                                $(canvas).trigger($.Event(event, {
                                    "keyCode": "\r".charCodeAt(0)
                                }));
                            });
                            $(".appear").removeClass("appear");
                        }
                        $connect.removeClass("connecting");
                    } else if (mutation.target.style.display === "none") {
                        if (playing()) {
                            Observer.disconnect();
                        }
                        unsafeWindow.m28nOverride = false;
                    }
                });
            });
            $connect.addClass("connecting");
            unsafeWindow.m28nOverride = true;
            unsafeWindow.input.execute("lb_reconnect");
            Observer.observe($("#textInputContainer").get(0), {
                "attributes": true,
                "attributeFilter": ["style"]
            });
        }
    };

    const WebSocketProxy = new Proxy(unsafeWindow.WebSocket, {
        construct (Target, args) {
            const instance = new Target(...args);

            const messageHandler = (event) => {
                const buffer = new DataView(event.data);
                const opcode = buffer.getUint8(0);
                switch (opcode) {
                case 4:
                    if (typeof unsafeWindow.Config.script.currentServer === "object") {
                        const decoded = new TextDecoder("utf-8").decode(event.data);
                        unsafeWindow.Config.script.currentServer = (/\W*(\w+).?((linode|vultr)-(\w+))/).exec(decoded);
                        unsafeWindow.Config.script.currentServer[4] = capitalizeFirstLetter(unsafeWindow.Config.script.currentServer[4]);
                    }
                    break;
                default:
                    break;
                }
            };

            instance.addEventListener("message", messageHandler);
            return instance;
        }
    });

    unsafeWindow.WebSocket = WebSocketProxy;

    const drawServer = () => {
        const x = window.innerWidth * window.devicePixelRatio / 2;
        const y = window.innerHeight * window.devicePixelRatio * 0.575;
        if (unsafeWindow.Config.script.currentServer.length === 5) {
            ctx.textAlign = "center";
            ctx.font = "25px Ubuntu";
            ctx.lineWidth = 5;
            ctx.strokeStyle = "rgba(0, 0, 0, 1)";
            ctx.strokeText("Server:", x, y);
            ctx.fillStyle = "rgba(255, 255, 255, 1)";
            ctx.fillText("Server:", x, y);

            ctx.font = "35px Ubuntu";
            ctx.lineWidth = 5;
            ctx.strokeStyle = "rgba(0, 0, 0, 1)";
            ctx.strokeText(unsafeWindow.Config.script.currentServer[2], x, y + 45);
            ctx.fillStyle = "rgba(255, 255, 255, 1)";
            ctx.fillText(unsafeWindow.Config.script.currentServer[2], x, y + 45);
        }
    };

    unsafeWindow.requestAnimFrame = (function () {
        return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback, element) {
                window.setTimeout(callback, 1000 / 60);
            };
    })();

    $(window).on("load", function animate () {
        if ($("#textInputContainer").css("display") === "block" && !playing()) {
            drawServer();
        }
        unsafeWindow.requestAnimFrame(animate);
    });

    const handleKeypress = (event) => {
        const key = String.fromCharCode(event.keyCode);
        switch (key) {
        case unsafeWindow.Config.hotkey.connectUI:
            event.preventDefault();
            event.stopPropagation();
            $("#main").toggleClass("appear");
            break;
        }
    };

    $(document).keydown((event) => {
        handleKeypress(event);
    });

})();