您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Press Tab to open Server Selector.
当前为
- // ==UserScript==
- // @name Florr.io Server Selector
- // @namespace Violentmonkey Scripts
- // @version 2.2.5
- // @author ash & Guava_Thrower
- // @match *://florr.io/*
- // @grant unsafeWindow
- // @grant GM_addStyle
- // @grant GM_info
- // @description Press Tab to open Server Selector.
- // ==/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();
- }
- })();