Powerline Server Switcher

Server switcher for powerline

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 or Violentmonkey 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         Powerline Server Switcher
// @author       Rumini - Discord: rumini & zaynbieber
// @description  Server switcher for powerline
// @version      1.0
// @match        *://powerline.io/*
// @icon         https://i.imgur.com/9k4SFr0.png
// @license      MIT
// @grant        none
// @run-at       document-start
// @namespace https://greasyfork.org/users/1356205
// ==/UserScript==

if (window.location.href === 'https://powerline.io/') {
    window.location.href = 'https://powerline.io/maindev.html';
}

(function () {
    'use strict';

    // Configuration
    const CONFIG = {
        regions: ['Europe', 'Asia', 'America'],
        countryCodes: { Europe: 'DE', Asia: 'JP', America: 'US' },
        euCountries: new Set(['AL', 'AD', 'AT', 'BY', 'BE', 'BA', 'BG', 'HR', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IS', 'IE', 'IT', 'LV', 'LI', 'LT', 'LU', 'MT', 'MC', 'NL', 'NO', 'PL', 'PT', 'RO', 'RU', 'SM', 'RS', 'SK', 'SI', 'ES', 'SE', 'CH', 'UA', 'GB', 'VA']),
        asiaCountries: new Set(['AF', 'AM', 'AZ', 'BH', 'BD', 'BT', 'BN', 'KH', 'CN', 'CY', 'GE', 'IN', 'ID', 'IR', 'IQ', 'IL', 'JP', 'JO', 'KZ', 'KW', 'KG', 'LA', 'LB', 'MY', 'MV', 'MN', 'MM', 'NP', 'KP', 'OM', 'PK', 'PS', 'PH', 'QA', 'SA', 'SG', 'KR', 'LK', 'SY', 'TW', 'TJ', 'TH', 'TR', 'TM', 'AE', 'UZ', 'VN', 'YE']),
        updateInterval: 1000,
        hideDelay: 1000,
        switchDelay: 1000,
        tipDuration: 2000,
    };

    // Utility functions
    const util = {
        waitForGame: (callback) => {
            if (typeof network !== 'undefined' && typeof countryCode !== 'undefined') {
                callback();
            } else {
                setTimeout(() => util.waitForGame(callback), 100);
            }
        },
        getRegion: () => {
            if (CONFIG.euCountries.has(countryCode)) return 'Europe';
            if (CONFIG.asiaCountries.has(countryCode)) return 'Asia';
            return 'America';
        },
        createElement: (tag, options = {}) => {
            const element = document.createElement(tag);
            Object.assign(element, options);
            if (options.style) element.style.cssText = options.style;
            return element;
        },
    };

    // Main class
    class ServerSwitcher {
        constructor() {
            this.elements = {};
            this.state = {
                isRegionSelectorOpen: false,
                isRoomCodeInputActive: false,
            };
            this.hideTimeout = null;
        }

        init() {
            this.createUI();
            this.attachEventListeners();
            this.updateServerInfo();
            setInterval(() => this.updateServerInfo(), CONFIG.updateInterval);
        }

        createUI() {
            this.elements = {
                container: util.createElement('div', {
                    id: 'server-info-container',
                    style: `
                        position: absolute;
                        top: 0px;
                        right: 0px;
                        display: flex;
                        align-items: center;
                        z-index: 1000;
                        opacity: 0;
                    `
                }),
                switcherContainer: util.createElement('div', {
                    id: 'region-switcher-container',
                    style: `
                        position: relative;
                        display: flex;
                        align-items: center;
                        margin-right: 10px;
                    `
                }),
                switcherButton: util.createElement('button', {
                    id: 'region-switcher',
                    innerHTML: `
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="30" height="30">
                            <path fill="#00ffff" d="M0 96C0 43 43 0 96 0L384 0l32 0c17.7 0 32 14.3 32 32l0 320c0 17.7-14.3 32-32 32l0 64c17.7 0 32 14.3 32 32s-14.3 32-32 32l-32 0L96 512c-53 0-96-43-96-96L0 96zM64 416c0 17.7 14.3 32 32 32l256 0 0-64L96 384c-17.7 0-32 14.3-32 32zM247.4 283.8c-3.7 3.7-6.2 4.2-7.4 4.2s-3.7-.5-7.4-4.2c-3.8-3.7-8-10-11.8-18.9c-6.2-14.5-10.8-34.3-12.2-56.9l63 0c-1.5 22.6-6 42.4-12.2 56.9c-3.8 8.9-8 15.2-11.8 18.9zm42.7-9.9c7.3-18.3 12-41.1 13.4-65.9l31.1 0c-4.7 27.9-21.4 51.7-44.5 65.9zm0-163.8c23.2 14.2 39.9 38 44.5 65.9l-31.1 0c-1.4-24.7-6.1-47.5-13.4-65.9zM368 192a128 128 0 1 0 -256 0 128 128 0 1 0 256 0zM145.3 208l31.1 0c1.4 24.7 6.1 47.5 13.4 65.9c-23.2-14.2-39.9-38-44.5-65.9zm31.1-32l-31.1 0c4.7-27.9 21.4 51.7 44.5 65.9c-7.3 18.3-12 41.1-13.4 65.9zm56.1-75.8c3.7-3.7 6.2-4.2 7.4-4.2s3.7 .5 7.4 4.2c3.8 3.7 8 10 11.8 18.9c6.2 14.5 10.8 34.3 12.2 56.9l-63 0c1.5-22.6 6-42.4 12.2-56.9c3.8-8.9 8-15.2 11.8-18.9z"/>
                        </svg>
                    `,
                    style: `
                        background: none;
                        border: none;
                        cursor: pointer;
                        padding: 5px;
                        display: flex;
                        align-items: center;
                    `
                }),
                roomCodeInput: util.createElement('input', {
                    type: 'text',
                    placeholder: 'Enter room code',
                    style: `
                        position: absolute;
                        top: 50%;
                        right: 0;
                        transform: translateY(-50%);
                        width: 0;
                        padding: 5px;
                        border: #05ffff solid;
                        border-width: 2px;
                        border-radius: 5px;
                        background-color: #003a3a;
                        color: #05ffff;
                        font-family: 'Arial', sans-serif;
                        font-size: 14px;
                        transition: width 0.3s ease-out, opacity 0.3s ease-out;
                        opacity: 0;
                        outline: none;
                    `
                }),
                infoDiv: util.createElement('div', {
                    id: 'server-info',
                    style: `
                        background-color: #003a3a;
                        border-radius: 5px;
                        padding: 3px 6px;
                        border: #05ffff solid;
                        border-width: 2px;
                        border-radius: 0 0 0 5px;
                        color: #05ffff;
                        font-family: 'Arial Black', sans-serif;
                        font-size: 14px;
                        user-select: none;
                        cursor: pointer;
                    `
                }),
                regionOptions: []
            };

            this.createRegionOptions();

            this.elements.switcherContainer.append(this.elements.switcherButton, this.elements.roomCodeInput);
            this.elements.container.append(this.elements.switcherContainer, this.elements.infoDiv);
            document.body.appendChild(this.elements.container);
        }

        createRegionOptions() {
            CONFIG.regions.forEach((region, index) => {
                const option = util.createElement('div', {
                    textContent: region,
                    style: `
                        position: absolute;
                        right: -70px;
                        top: 50%;
                        transform: translateY(-50%) translateX(0);
                        padding: 5px;
                        cursor: pointer;
                        color: #05ffff;
                        background: rgba(0, 58, 58, 0.7);
                        backdrop-filter: blur(5px);
                        border: 1px solid rgba(5, 255, 255, 0.3);
                        border-radius: 5px;
                        opacity: 0;
                        transition: opacity 0.3s ease-out, transform 0.3s ease-out;
                        z-index: ${1000 - index};
                        width: 70px;
                        text-align: center;
                    `
                });
                option.addEventListener('click', (e) => {
                    e.stopPropagation();
                    this.switchRegion(index);
                });
                this.elements.switcherContainer.appendChild(option);
                this.elements.regionOptions.push(option);
            });
        }

        attachEventListeners() {
            this.elements.infoDiv.addEventListener('click', () => this.copyRoomLink());
            this.elements.switcherContainer.addEventListener('mouseenter', () => this.handleContainerMouseEnter());
            this.elements.switcherContainer.addEventListener('mouseleave', () => this.handleContainerMouseLeave());
            this.elements.switcherButton.addEventListener('click', (e) => this.handleSwitcherButtonClick(e));
            this.elements.roomCodeInput.addEventListener('keydown', (e) => this.handleRoomCodeInputKeydown(e));
            document.addEventListener('click', (e) => this.handleDocumentClick(e));
        }

        updateServerInfo() {
            const roomCode = network.roomID ? `#${network.roomID}` : 'Not in a room';
            const region = util.getRegion();

            if (typeof localPlayer === 'undefined' || localPlayerID === 0) {
                this.elements.infoDiv.innerHTML = `Room: ${roomCode}<br>Region: ${region}`;
                this.elements.container.style.cssText += `
                    transition: opacity ease-in 0.25s;
                    transition-delay: 0.3s;
                    opacity: 1;
                `;
            } else {
                this.elements.container.style.cssText += `
                    transition: none;
                    opacity: 0;
                `;
            }
        }

        switchRegion(index) {
            const newRegion = CONFIG.regions[index];
            const newCC = CONFIG.countryCodes[newRegion];

            countryCode = newCC;
            window.localStorage.wingsCC = newCC;
            window.localStorage.wingsCCTime = Date.now();

            network.disconnect();
            setTimeout(() => network.getServerAndConnect(), CONFIG.switchDelay);

            hud.showTip(`Switched to region: ${newRegion}`, CONFIG.tipDuration);
        }

        toggleRegions() {
            this.state.isRegionSelectorOpen = !this.state.isRegionSelectorOpen;
            this.state.isRegionSelectorOpen ? this.showRegions() : this.hideRegions();
        }

        showRegions() {
            clearTimeout(this.hideTimeout);
            this.elements.regionOptions.forEach((option, index) => {
                option.style.cssText += `
                    opacity: 1;
                    transform: translateY(-50%) translateX(${-120 - index * 90}px);
                `;
            });
        }

        hideRegions() {
            this.elements.regionOptions.forEach((option) => {
                option.style.cssText += `
                    opacity: 0;
                    transform: translateY(-50%) translateX(-80%);
                `;
            });
        }

        showRoomCodeInput() {
            this.state.isRoomCodeInputActive = true;
            this.elements.roomCodeInput.style.cssText += `
                width: 120px;
                opacity: 1;
                pointer-events: auto;
            `;
            this.elements.switcherButton.style.cssText += `
                opacity: 0;
                pointer-events: none;
            `;
            this.elements.roomCodeInput.focus();
            this.hideRegions();
        }

        hideRoomCodeInput() {
            this.state.isRoomCodeInputActive = false;
            this.elements.roomCodeInput.style.cssText += `
                width: 0;
                opacity: 0;
                pointer-events: none;
            `;
            this.elements.switcherButton.style.cssText += `
                opacity: 1;
                pointer-events: auto;
            `;
            this.showRegions();
        }

        connectToRoom(roomCode) {
            if (roomCode) {
                network.disconnect();
                setTimeout(() => {
                    window.location.hash = roomCode;
                    network.getServerAndConnect();
                }, CONFIG.switchDelay);
                hud.showTip(`Connecting to room: ${roomCode}`, CONFIG.tipDuration);
                this.updateServerInfo();
            }
        }

        copyRoomLink() {
            const roomCode = network.roomID ? `#${network.roomID}` : '';
            const url = `http://powerline.io/${roomCode}`;
            navigator.clipboard.writeText(url)
                .then(() => console.log('Copied to clipboard:', url))
                .catch(err => console.error('Failed to copy to clipboard:', err));
        }

        handleContainerMouseEnter() {
            clearTimeout(this.hideTimeout);
            if (!this.state.isRegionSelectorOpen && !this.state.isRoomCodeInputActive) {
                this.showRegions();
            }
        }

        handleContainerMouseLeave() {
            if (!this.state.isRegionSelectorOpen && !this.state.isRoomCodeInputActive) {
                this.hideTimeout = setTimeout(() => this.hideRegions(), CONFIG.hideDelay);
            }
        }

        handleSwitcherButtonClick(e) {
            e.stopPropagation();
            this.state.isRoomCodeInputActive ? this.hideRoomCodeInput() : this.showRoomCodeInput();
        }

        handleRoomCodeInputKeydown(e) {
            if (e.key === 'Enter') {
                this.connectToRoom(this.elements.roomCodeInput.value);
                this.hideRoomCodeInput();
            } else if (e.key === 'Escape') {
                this.hideRoomCodeInput();
            }
        }

        handleDocumentClick(e) {
            if (!this.elements.switcherContainer.contains(e.target)) {
                if (this.state.isRegionSelectorOpen) {
                    this.toggleRegions();
                }
                if (this.state.isRoomCodeInputActive) {
                    this.hideRoomCodeInput();
                }
            }
        }
    }

    // Initialize the ServerSwitcher when the game is ready
    util.waitForGame(() => new ServerSwitcher().init());
})();