Greasy Fork

Ultra Mobs Alert

Show spawn timer, sharing another players. Work In Progress https://discord.gg/4nPjXjVf4t Dont use your real discrod you can banned

当前为 2023-05-28 提交的版本,查看 最新版本

// ==UserScript==
// @name         Ultra Mobs Alert
// @namespace    http://tampermonkey.net/
// @version      9
// @description  Show spawn timer, sharing another players. Work In Progress https://discord.gg/4nPjXjVf4t Dont use your real discrod you can banned
// @author       @jmatg1
// @license MIT
// @match        https://florr.io
// @icon         https://www.google.com/s2/favicons?sz=64&domain=florr.io
// @grant        none
// @require            https://openuserjs.org/src/libs/sizzle/GM_config.js
// ==/UserScript==
const VERSION = "9";
alert('Detected. Wait update')
return;
GM_config.init(
{
  'id': 'MyConfig', // The id used for this instance of GM_config
  'fields': // Fields object
  {
    'filterNames': // This is the id of the field
    {
      'label': 'Filter Names, Example: Rock, Bush', // Appears next to field
      'type': 'text',
      'default': '' // Default value if user doesn't change it
    },
    'spawnSound': // This is the id of the field
    {
      'label': 'Spawn Sound', // Appears next to field
      'type': 'checkbox',
      'default': true // Default value if user doesn't change it
    },
    'spawnSoundUrl': // This is the id of the field
    {
      'label': 'spawnSoundUrl', // Appears next to field
      'type': 'text',
      'default': "https://zvukogram.com/index.php?r=site/download&id=47315" // Default value if user doesn't change it
    },
      'Button':
      {
          'label': 'Test Sound', // Appears on the button
          'type': 'button', // Makes this setting a button input
          'size': 100, // Control the size of the button (default is 25)
          'click': function() { // Function to call when button is clicked
              playSound();
          }
      }
  }
});


try {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', "https://greasyfork.org/ru/scripts/465457.json");
    xhr.send();

    xhr.onload = function() {
        let obj = JSON.parse(xhr.response)
        if (obj.version !== VERSION){
            let t = "New Version!!!" + " (" + VERSION + " -> " + obj.version + ") https://greasyfork.org/ru/scripts/465457-ultra-mobs-alert"
            window.alert(t)
        }
    };
} catch (e){}

function playSound() {
    try{
    new Audio(GM_config.get('spawnSoundUrl')).play()
    }
    catch(er) {
    alert(er)
    }
}

function createInput(array, id) {
    var selectList = document.createElement("select");
    selectList.style.pointerEvents = "auto";
    selectList.id = id;
    for (var i = 0; i < array.length; i++) {
        var option = document.createElement("option");
        option.value = array[i];
        option.text = array[i];
        selectList.appendChild(option);
    }
    return selectList;
}

class GUI {
    constructor(serv) {
        this.data = {};
        this.bosses = [];
        this.filterName = [];
        const div = document.createElement('div');
        div.appendChild(createInput(["All", "NA", "EU", "AS"], "serverIdSelect"));
        const fl = document.createElement('span')
        fl.appendChild(document.createTextNode('Settings'));
        fl.onclick = () => {GM_config.open()}
        fl.style['pointer-events'] = 'auto';
        div.appendChild(fl);

        this.menu = document.body.appendChild(document.createElement('div'));
        this.menu.appendChild(div);
        this.menu.style.padding = '15px';
        this.menu.style.background = '#ffffff3f';
        this.menu.style['border-radius'] = '10px';
        this.menu.style.display = 'block';
        this.menu.style.position = 'absolute';
        this.menu.style['pointer-events'] = 'none';
        this.menu.style.bottom = '5px';
        this.menu.style.right = '5px';

        this.table = this.menu.appendChild(document.createElement('table'));
        this.table.style['font-family'] = 'Ubuntu';
        this.table.style['border-collapse'] = 'collapse';
        this.table.style.width = `100%`;
        setInterval(() => {
            this.changeServer(document.getElementById('serverIdSelect').value);
        }, 10 * 1000)
    }

    updateTable(bosses = this.bosses) {
        this.bosses = bosses;
        const filtered =  GM_config.get('filterNames') === '' ? [] : GM_config.get('filterNames').split(',').map(el => el.trim());
        while (this.table.rows.length > 0) {
            this.table.deleteRow(0);
        }
        bosses.sort((a, b) => {
            return new Date(a.date).getTime() - new Date(b.date).getTime();
        });

        bosses = bosses.filter((x) => {
            const minutes = Math.floor((Date.now() - new Date(x.date).getTime()) / 1000 / 60);
            if (minutes > 25) {
                return false
            }
            return true;
        });
        bosses.forEach((x, i) => {
        if(filtered.length && !filtered.find(el => x.name.search(el) !== -1)) {
            return
        }
            const tr = this.table.insertRow();

            // Mob
            const td1 = tr.insertCell();
            if (i !== 0) {
                td1.style['padding-top'] = '5px';
            }
            if (i !== bosses.length - 1) {
                td1.style['padding-bottom'] = '5px';
            }
            td1.style['padding-right'] = '20px';
            td1.style['color'] = x?.l ? 'green' : 'black';
            td1.style['pointer-events'] = 'none';

            td1.appendChild(document.createTextNode(x.name));

            // minutes ago
            const td2 = tr.insertCell();
            if (i !== 0) {
                td2.style['padding-top'] = '5px';
            }
            if (i !== bosses.length - 1) {
                td2.style['padding-bottom'] = '5px';
            }
            td2.style['pointer-events'] = 'none';
            td2.appendChild(document.createTextNode(this.toTimeSpanString(x.date)));
        });
    }

    toTimeSpanString(timestamp) {
        if (timestamp == null) {
            return 'N/A';
        }

        let minutes = Math.floor((Date.now() - new Date(timestamp).getTime()) / 1000 / 60);
        minutes = minutes <= 0 ? 0 : minutes;
        return `${minutes}m ago`;
    }
    changeServer(id = 'All') {
        let data = []
        if (id === 'All') {
            for(let key of Object.keys(this.data)) {
                this.data[key].forEach(el => {
                    data.push({
                        ...el,
                        name:  el.name + ' [' + key + ']',
                    });
                })
            }
        } else {
            data = this.data[id];
        }
        this.updateTable(data);
    }
}

class ServerId {
    constructor() {
        this.servers = [];
        this.url;
        this.regionName;
        const nativeWebSocket = window.WebSocket;
        var totalServers = 17 - 1;
        var allServers = [];
        var _this = this;
        window.WebSocket = function (...args) {
            const socket = new nativeWebSocket(...args);
            if (socket.url.search('cyber-glorious') === -1){
                _this.url = socket.url;
            }
            return socket;
        };
        const urls = [];
        for (let i = 0; i <= totalServers; i++) {
            urls.push(`https://api.n.m28.io/endpoint/florrio-map-${i}-green/findEach/`);
        }
        Promise.all(urls.map(url =>
                             fetch(url).then(resp => resp.text())
                            )).then(texts => {
            if (texts.length === 17) {
                texts.forEach(el => {
                    const data = JSON.parse(el)
                    this.servers.push(`${data.servers["vultr-miami"].id} ${data.servers["vultr-frankfurt"].id} ${data.servers["vultr-tokyo"].id}`);
                })
            }
        })
    }


    getId() {
        var wssUrl = this.url.slice(6, 9)
        var codeA = [];
        this.servers.forEach((x, index) => {
            if (x.includes(wssUrl)) {
                codeA = x.split(" ")
                if (wssUrl == codeA[0]) this.regionName = "NA"
                else if (wssUrl == codeA[1]) this.regionName = "EU"
                else if (wssUrl == codeA[2]) this.regionName = "AS"
            }
        });
        return this.regionName;
    }
}

class WebSocketMy {
    constructor(gui, serv) {
        this.socket;
        this.gui = gui;
        this.serv = serv;
        this.intervalREconnect = false;
        this.intervalPingconnect = false;
        this.init();
    }

    init() {
        this.socket = new WebSocket("wss://cyber-glorious-ferry.glitch.me");
        this.socket.onmessage = (event, isBinary) => {
            const message = JSON.parse(event.data);
            if (message.message === "PONG") {
                return
            }
            this.gui.data = message;
            this.gui.changeServer(document.getElementById('serverIdSelect').value);

        };

        this.socket.onopen = (e) => {
            console.log("[open] Соединение установлено");
            if(this.intervalREconnect) {
                clearInterval(this.intervalREconnect)
                this.intervalREconnect = false
            }
            if(this.intervalPingconnect) {
            clearInterval(this.intervalPingconnect)
            }
            this.intervalPingconnect = setInterval(() => {
                if (this.socket.readyState === 1) {
                    this.socket.send(JSON.stringify({message: "PING", serverName: 'EU'}));
                }
            }, 60 * 1000)
        };



        this.socket.onclose = (event) => {
            if(event.reason === "null") {return}
            if(this.intervalREconnect) {
                clearInterval(this.intervalREconnect)
                this.intervalREconnect = false
            }
            this.intervalREconnect = setInterval(() =>{
                if (this.socket.readyState !== 1 && this.socket.readyState !==0 && this.socket.readyState !== 2) {
                    this.init()
                }
            }, 1000)
            if (event.wasClean) {
                console.log(`[close] Соединение закрыто чисто, код=${event.code} причина=${event.reason}`);
            } else {
                console.log('[close] Соединение прервано');
            }
        };

        this.socket.onerror = (error) => {
            console.log(`[error]`);
            this.init();
        };

    }
    send(text) {
        this.socket.send(text)
    }
}

let inProgress = false;
let i = 20_000_000;
let length = 50_217_728;
let itt = Math.floor(1_000);
let start = Date.now();
function changeArray(arrText) {
    // перенесём планирование очередного вызова в начало
    if (i < length - itt) {
        setTimeout(() => changeArray(arrText)); // запланировать новый вызов
    }

    do {
        i+=1;
        if(!inProgress) {
            break
        }
        if(window.m.HEAPU8[i] === arrText[0]
           && window.m.HEAPU8[i+1] === arrText[1]
           && window.m.HEAPU8[i+2] === arrText[2]
           && window.m.HEAPU8[i+3] === arrText[3]
           && window.m.HEAPU8[i+4] === arrText[4]
          ) {
            window.m.HEAPU8[i] = 26;
            console.log(i)
            inProgress = false;
            break;
        }
    } while (i % itt != 0);
    if (i >= length) {
        console.log("Done in " + (Date.now() - start) + 'ms');
        inProgress = false;
    }

}


const initScript = () =>{ // Аналог $(document).ready(function(){
    console.log('DOMContentLoaded')
    'use strict';

    const serv = new ServerId();

    let intervalREconnect = false
    const gui = new GUI(serv);
    let socket = new WebSocketMy(gui, serv);

    document.getElementById('serverIdSelect').onchange = ev => {
        gui.changeServer(ev.target.value);
        console.log(ev.target.value)
    }



    setTimeout(() => {
        document.getElementById('serverIdSelect').value = serv.getId();
        gui.changeServer(serv.getId());
    }, 15000)

    function parseText(text) {
        socket.send(JSON.stringify({message: text, serverName: serv.getId()}));
        console.warn(JSON.stringify({message: text, serverName: serv.getId()}));
    }

    function getCompatibleCanvas() {
        if (typeof (OffscreenCanvasRenderingContext2D) == 'undefined') {
            return [CanvasRenderingContext2D]
        }
        return [OffscreenCanvasRenderingContext2D, CanvasRenderingContext2D];
    }

    for (const {prototype} of getCompatibleCanvas()) {
        if (prototype.rewriteStrokeText == undefined) {
            prototype.rewriteStrokeText = prototype.strokeText;
        }
        prototype.rewriteStrokeText = prototype.strokeText;
        prototype.rewriteFillText = prototype.fillText;
        prototype.rewriteMeasureText = prototype.measureText;
    }


    const wb = new TextDecoder("utf8")
    let encoder = new TextEncoder("utf8");
    let arrCodes = []



    const rewrite = Uint8Array.prototype.subarray;
    Uint8Array.prototype.subarray= function(z,x) {
        const text = wb.decode(rewrite.call(this, z, x));
        if((text.search(/((An Ultra)|(A Super)) (.*) has spawned!?( somewhere!?)?/i) !== -1
            || text.search(/((The Ultra)|(The Super)) (.*) was defeated/i) !== -1)){
            arrCodes.push(z,x);
        }
        return rewrite.call(this, z, x);
    }


    for (const {prototype} of getCompatibleCanvas()) {
        prototype.strokeText = function (text, x, y) {
            // (this.fillStyle === '#ff2b75' || this.fillStyle === '#2bffa3') &&
            if(
               (text.search(/((An Ultra)|(A Super)) (.*) has spawned!?( somewhere!?)?/i) !== -1
                || text.search(/((The Ultra)|(The Super)) (.*) was defeated/i) !== -1
               )
              ){
                arrCodes.forEach(el => {
                    window.m.HEAPU8[el] = 26;
                })
                arrCodes = [];
                if(this.fillStyle === '#ff2b75' || this.fillStyle === '#2bffa3'){
                    parseText(text);
                    if (GM_config.get('spawnSound') && text.search(/((An Ultra)|(A Super)) (.*) has spawned!?( somewhere!?)?/i) !== -1) {
                        playSound();
                    }
                }
            }
            return this.rewriteStrokeText(text, x, y);
        }
    }
}

    initScript();