您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds server selector to florr.io
当前为
// ==UserScript== // @name Florr.io Server Selector // @namespace Violentmonkey Scripts // @version 2.2.6 // @description Adds server selector to florr.io // @author ash & Guava_Thrower // @match *://florr.io/* // @grant unsafeWindow // @grant GM_addStyle // @grant GM_info // ==/UserScript== // Styles by @Guava_Thrower, ash GM_addStyle(` .container { display: block; width: 100%; height: 100%; position: fixed; top: 0; left: 0; pointer-events: none; font-family: Ubuntu; color: #fff; font-size: 16px; text-shadow: 0 0 2px #000, 0 0 2px #000, 0 0 2px #000, 0 0 2px #000; } .active { pointer-events: auto; } .hidden { display: none; } .btn, .modal { background: #5a9fdb; border: 4px solid #4981b1; border-radius: 6px; pointer-events: auto; } .btn { position: absolute; height: 34px; right: 6px; bottom: 6px; padding-left: 16px; padding-right: 16px; cursor: pointer; outline: 0; font-family: Ubuntu; color: #fff; font-size: 16px; font-weight: bold; text-shadow: 0 0 2px #000, 0 0 2px #000, 0 0 2px #000, 0 0 2px #000; } .container:not(.active) .btn:hover { background: #65aeef; } .active .btn { background: #518fc5; } .modal { position: absolute; bottom: 48px; right: 6px; padding: 8px; width: 300px; max-height: 400px; transition: 0.2s; transform: translate(0, calc(100% + 60px)); } .active .modal { transform: translate(0, 0); } .modal-title { font-size: 1.6em; text-align: center; margin-top: 0; } .modal-close-btn { position: absolute; right: 5px; top: 5px; width: 30px; height: 30px; border-radius: 4px; background: #bb5555; border: 5px solid #974545; cursor: pointer; outline: 0; } .modal-close-btn:hover { background: #da6868; } .modal-close-btn:after, .modal-close-btn:before { content: ""; position: absolute; left: 50%; top: 50%; width: 5px; height: 90%; background: #cccccc; border-radius: 4px; } .modal-close-btn:after { transform: translate(-50%, -50%) rotate(45deg); } .modal-close-btn:before { transform: translate(-50%, -50%) rotate(-45deg); } .modal-element { background: #4981b1; padding: 10px; border-radius: 5px; margin-bottom: 5px; cursor: pointer; } .modal-element:last-child { margin-bottom: 0; } .modal-element-active { box-shadow: inset 0 0 0 2px #376185; } .modal-loader { display: flex; flex-direction: column; align-items: center; padding: 10px; } .spinner { width: 40px; height: 40px; border: 10px solid rgba(0, 0, 0, 0.5); border-top-color: rgba(0, 0, 0, 0.8); border-radius: 50%; animation: spin 0.5s infinite; } .spinner-text { margin-top: 5px; font-size: 1.25em; } .modal-info { position: absolute; right: 40px; top: 5px; font-size: 1.20em; padding: 4px 5px; } .modal-info:hover .modal-info-content { opacity: 1; pointer-events: inherit; } .modal-info-content { position: absolute; bottom: 150%; right: 0; background: rgba(0, 0, 0, 0.5); border-radius: 4px; opacity: 0; padding: 6px; font-size: 0.85rem; width: 250px; transition: 0.2s; pointer-events: none; } .modal-info-title { margin: 0; margin-bottom: 5px; font-size: 1.40rem; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } `); (() => { 'use strict'; // UI component by @Guava_Thrower, ash const html = ` <button class="btn">Servers</button> <div class="modal"> <h1 class="modal-title">Servers</h1> <button class="modal-close-btn"></button> <span class="modal-info"> ? <div class="modal-info-content"> <div class="modal-info-title">Developers</div> <div>UI - Guava_Thrower, ash</div> <div>Server list - ash</div> <br> <div>v${GM_info.script.version}</div> </div> </span> <div class="modal-element modal-loader"> <div class="spinner"></div> <div class="spinner-text">Fetching...</div> </div> </div> `; const container = document.createElement("div"); container.classList.add('container'); container.innerHTML = html; document.body.appendChild(container); const modalEl = document.querySelector(".modal"); const btnEl = document.querySelector(".btn"); const closeBtnEl = document.querySelector(".modal-close-btn"); const loaderEl = document.querySelector(".modal-loader"); btnEl.onclick = () => { container.classList.toggle("active"); } closeBtnEl.onclick = () => { closeModal(); } const closeModal = () => { container.classList.remove("active"); } window.addEventListener('click', (evt) => { if (evt.target === container) { closeModal(); } }); window.addEventListener('keydown', (evt) => { if (evt.code === 'Tab') { container.classList.toggle('hidden'); if (container.classList.contains('active')) { container.classList.remove('active'); } } }); // listing servers by @ash const Config = { script: { m28nOverride: false, socket: null, currentId: '', ids: [] } }; const fetchServers = async () => { let url = "https://api.n.m28.io"; try { let response = await fetch(`${url}/endpoint/florrio/findEach/`); let body = await response.json(); if (body.hasOwnProperty("servers")) { loaderEl.style.display = "none"; Object.entries(body.servers).forEach(([key, val]) => { if (!Config.script.ids.includes(val.id)) { let el = document.createElement("div"); el.classList.add("modal-element"); el.innerHTML = key; Config.script.ids.push(val.id); el.setAttribute('data-value', JSON.stringify(val)); if (val.id === Config.script.currentId) { el.classList.add("modal-element-active"); } el.onclick = () => { connectServer(); let activeEl = document.querySelector(".modal-element-active"); activeEl.classList.remove("modal-element-active"); el.classList.add("modal-element-active"); closeModal(); } modalEl.appendChild(el); } }); } } catch (err) { console.error(err); } } const findServerPreferenceProxy = new Proxy(unsafeWindow.m28n.findServerPreference, { apply(_target, _thisArgs, args) { if (Config.script.m28nOverride) { var el = document.querySelector(".modal-element-active"); args[1](null, [JSON.parse(el.getAttribute("data-value"))]); return; } return Reflect.apply(...arguments); } }) unsafeWindow.m28n.findServerPreference = findServerPreferenceProxy; const WebSocketProxy = new Proxy(unsafeWindow.WebSocket, { construct(Target, args) { const instance = Reflect.construct(...arguments); const messageHandler = (e) => { let buffer = new DataView(e.data); if (buffer.getUint8(0) === 1) { instance.removeEventListener("message", messageHandler); Config.script.socket = instance; Config.script.currentId = instance.url.match(/wss:\/\/(\w{4})\./)[1]; fetchServers(); } } instance.addEventListener("message", messageHandler); return instance; } }); unsafeWindow.WebSocket = WebSocketProxy; const connectServer = () => { Config.script.m28nOverride = true; Config.script.socket.close(); } })();