Dylko's Bumpy Mod

Unlimiter, chat hotkeys, and chat panel script

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name Dylko's Bumpy Mod
// @description Unlimiter, chat hotkeys, and chat panel script
// @author Dylko
// @version 1.24.1
// @match https://www.pucks.io/*
// @match https://www.bumpyball.io/*
// @icon https://www.google.com/s2/favicons?domain=bumpyball.io
// @run-at document-end
// @namespace https://greasyfork.org/users/1574974
// @grant none
// @license MIT
// @require https://greasyfork.org/scripts/438620-workertimer/code/WorkerTimer.js
// @require https://greasyfork.org/scripts/448029-rblu/code/RBLU.js
// ==/UserScript==

// hi
document.documentElement.style.overflow = 'hidden';
document.body.style.overflow = 'hidden';
document.addEventListener('DOMContentLoaded', e => document.body.style.overflow = 'hidden');
window.logout = () => {
    window.indexedDB.databases().then(d=>d[2]).then(d=>window.indexedDB.deleteDatabase(d.name))
};
// try { window.logout(); } catch (e) {}
const r7bit = arr => {let r=0,e=0;for(;35!=e;){let f=arr[e/7];if(r|=(127&f)<<e,0==(128&f))return[r,e/7+1];e+=7}};
const int7 = (arr, start) => {
    let [value, bytes] = r7bit(arr.slice(start));
    return [value, start+bytes];
};
const float = (arr, start) => {
    let value = new Float32Array(new Uint8Array(arr.slice(start, start+4)).buffer)[0]
    return [value, start+4];
};
const w7bit = num => {let r=[];for(;num>=128;num>>=7)r.push((128|num)%256);return r.push(num%256),r};
const client = window.client = {
    bots: [],
    num: 15,
    team: 2,
    enabled: false,
    chatEnabled: false,
    moveEnabled: true,
    antiAfk: WorkerTimer.setInterval(()=>{
        const hiddenForward = new Uint8Array([8,7,18,10,18,5,13,0,0,128,63,24,200,1]).buffer;
        if (client.ws && client.ws.readyState === 1 && client.ws.id > -1) client.ws._send(hiddenForward);
        client.bots.forEach(b => {
            if (b.readyState === 1 && b.id > -1) b._send(hiddenForward);
        });
    }, 1000),
    sendMessage(text='') {
        if (!this.ws || !this.ws.constId) return;
        const msg = new TextEncoder().encode(text);
        const body = [8,...w7bit(this.ws.constId), 18,...w7bit(msg.length),...msg];
        const head = [8,5, 18,...w7bit(body.length)];
        const data = new Uint8Array([...head, ...body]);
        this.ws.send(data);
    },
    bot(num=15, team=2){
        this.team = team;
        this.num = num;
        return this.enabled = !this.enabled
    },
    move(on = null){
        if (on == null) return this.moveEnabled = !this.moveEnabled;
        return this.moveEnabled = !!on;
    },
    chatBot(on = null){
        if (on == null) return this.chatEnabled = !this.chatEnabled;
        return this.chatEnabled = !!on;
    },
    // 0 - blue, 1 - red
    close(team=2){
        this.bots.forEach(b=>{
            if (team === 2 || team === b.team) {
                WorkerTimer.clearInterval(b.inter);
                b.close();
            }
        });
        this.bots = this.bots.filter(b => team !== b.team);
        if (team === 2) this.bots = [];
    },
    setup(data, num=this.num){
        if (!this.ws.url || !this.enabled) return false;
        for(;num--;){
            WorkerTimer.setTimeout(()=>{
                const ws = new WebSocket(this.ws.url);
                this.bots.push(ws);
                ws.query = [data];
                ws.onopen=function(){
                    ws.inter = WorkerTimer.setInterval(()=>{
                        if (!ws.query.length) return;
                        let data = [...new Uint8Array(ws.query.shift())];
                        ws.send(new Uint8Array(data).buffer);
                    }, 17);
                }
            }, 57*num);
        }
    }
};
const packet = {};
packet.open = (arr, pos=0) => {
    packet.cancel = false;
    packet.arr = arr;
    packet.value = undefined;
    packet.pos = pos;
    return packet;
}
packet.null = () => {
    packet.cancel = true;
    packet.value = null;
    return packet;
}
packet.len = () => {
    if (packet.cancel) return packet;
    let {arr, pos, value} = packet;
    if (arr[pos++] !== 10) return packet.null();
    [value, pos] = int7(arr, pos);
    return Object.assign(packet, {pos, value});
}
packet.code = () => {
    if (packet.cancel) return packet;
    let {arr, pos, value} = packet;
    if (arr[pos++] !== 8) return packet.null();
    [value, pos] = int7(arr, pos);
    return Object.assign(packet, {pos, value});
}
packet.player = () => {
    if (packet.cancel) return packet;
    let {arr, pos, value} = packet;
    let data = {
        id: null,
        constId: null,
        team: null,
        x: null,
        z: null
    };
    if (arr[pos++] !== 10) return packet.null();
    [value, pos] = int7(arr, pos);
    if (arr[pos++] !== 8) return packet.null();
    if (arr[pos++] !== 8) return packet.null();
    if (arr[pos++] !== 18) return packet.null();
    [value, pos] = int7(arr, pos);
    if (arr[pos++] !== 16) return packet.null();
    [value, pos] = int7(arr, pos);
    data.id = value;
    if (arr[pos++] !== 26) return packet.null();
    [value, pos] = int7(arr, pos);
    if (arr[pos++] !== 10) return packet.null();
    [value, pos] = int7(arr, pos);
    if (arr[pos++] !== 13) return packet.null();
    [value, pos] = float(arr, pos);
    data.x = value;
    if (arr[pos++] !== 21) return packet.null();
    [value, pos] = float(arr, pos);
    data.z = value;
    if (arr[pos++] !== 24) return packet.null();
    [value, pos] = int7(arr, pos);
    data.constId = value;
    if (arr[pos] === 42) { //42,0
        data.team = 0;
        pos += 2;
        packet.value = data;
        return Object.assign(packet, {pos});
    }
    //37,219,15,73,192, 42,0
    data.team = 1;
    pos += 5+2;
    packet.value = data;
    return Object.assign(packet, {pos});
}
packet.players = () => {
    if (packet.cancel) return packet;
    let {arr, pos, value} = packet;
    const players = [];
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] !== 10) continue;
        packet.pos = i;
        let data = packet.player().value;
        if (data === null) {
            packet.cancel = false;
            continue;
        }
        players.push(data);
        i = packet.pos-1;
    }
    packet.value = players;
    return packet;
}
const isConnected = arr => {
    packet.open(arr);
    const code = packet.len().code().value;
    if (code === null) return false;
    if (code === 3) return true;
    return false;
}
const isJoinOther = arr => {
    packet.open(arr);
    const code = packet.len().code().value;
    if (code === null) return false;
    if (code === 2) return true;
    return false;
}
const isSpawn = arr => {
    packet.open(arr);
    const code = packet.len().code().value;
    if (code === null) return false;
    if (code === 8) return packet.player().value;
    return false;
}
const getId = arr => {
    // 10,x, 8,8,18,x, 16,id, 26,x, 10,10,13,... ,0,42];
    if (isConnected(arr)) return packet.id(arr).value;
    if (isJoinOther(arr)) return null;
    if (isSpawn(arr)) return packet.id(arr).value;
}
window.r7bit = r7bit;
window.w7bit = w7bit;
window.getId = getId;
(()=>{
    const proto = WebSocket.prototype;
    proto._send = proto.send;
    proto.send = function (data) {
        if (!this.event) {
            this.event = true;
            this.addEventListener('message', async function(e){
                const data = [...new Uint8Array(await e.data.arrayBuffer())];
                const players = packet.open(data).players().value;
                if (players == null) return;
                if (players.length) {
                    if (this.constId == null) Object.assign(this, players[0]);
                    const player = players.find(p => p.constId === this.constId);
                    if (player) Object.assign(this, player);
                }
                if (client.team !== 2 && client.bots.every(b => b.team > -1)) client.close(+(!client.team));
            });
        }
        if (!client.ws) {
            client.ws = this;
            this.addEventListener('close', e=>{
                delete client.ws;
                client.close();
            });
            this.own = true;
            client.setup(data);
        }
        if (this.own) {
            const d = [...new Uint8Array(data)];
            let move = false;
            let packet = d;
            if (
                (d[0]==8&&d[1]==7&&d[2]==18) // move
            ) {
                move = true;
                packet = d.slice(0, d.length-w7bit(this.id||0).length);
            }
            if (
                client.moveEnabled&&
                !(!client.chatEnabled&&d[0]==8&&d[1]==5&&d[2]==18) // chat
            ) {
                client.bots.forEach(b=>{
                    if (move && b.id == null) return;
                    if (b.readyState===3) return;
                    if (b.readyState!==1) return b.query.push(packet);
                    if (move) b.send(new Uint8Array([...packet, ...w7bit(b.id)]));
                    else b.send(new Uint8Array(packet));
                })
            }
        }
        //if (this.own) console.log('own', new Uint8Array(data));
        //else console.log('bot', new Uint8Array(data));
        this._send(data);
    }
})();
window.genMatrix = () => {
    const steps = 10;
    const fps = 1000/20;
    const time = performance.now()/100;
    const step = time/fps>>0;
    const light = num => {
        const gradient = "_O";
        const max = gradient.length-1;
        num = Math.min(100, Math.max(num, 0));
        const x = max*num/100>>0;
        return gradient[x];
    }
    const matrix = Array(10).fill(0).map((a,y)=>{
        //y = y + step%steps >> 0;
        return Array(10).fill(0).map((a,x)=>{
            //x = x + step%steps >> 0;
            const dxy = x^y;
            const drawIf = dxy>time%25 || x==0 || x == 9 || y == 0 || y == 9;
            const formula = drawIf*100;
            const color = light(formula);
            return color;
        }).join('');
    });
    return matrix;
};
const numpadMessages = {
    // Directional
    NumpadDivide: 'On your left!',
    NumpadMultiply: 'On your right!',
    NumpadAdd: 'gg',
    NumpadSubtract: 'stop trying to score on me it wont work',
    // Tactical
    Numpad0: 'Push!',
    Numpad1: 'Pass!',
    Numpad2: 'Get in goal!',
    Numpad3: "I've got the net.",
    Numpad4: "I've got it!",
    Numpad5: "Take it!",
    Numpad6: 'Cover me!',
    Numpad7: 'where going',
    Numpad8: 'Careful.',
    Numpad9: 'Be aggressive!'
};
const keyCooldown = 150;
const lastKeyUse = {};
document.addEventListener('keydown', e => {
    if (e.repeat) return;
    const now = Date.now();
    if (lastKeyUse[e.code] && now - lastKeyUse[e.code] < keyCooldown) return;
    lastKeyUse[e.code] = now;
    const msg = numpadMessages[e.code];
    if (msg) {
        client.sendMessage(msg);
    }
});
(function() {
    'use strict';

    let playername = "";
    let usernamearray = [];
    let loaded = false;

    // ─── WebSocket hook ────────────────────────────────────────────────────────
    const OriginalWebSocket = window.WebSocket;
    window.WebSocket = function(...args) {
        const ws = new OriginalWebSocket(...args);
        client.ws = ws;

        const originalSend = ws.send;
        ws.send = function(data) {
            if (data instanceof ArrayBuffer && !loaded) {
                loaded = true;
                const uint8 = new Uint8Array(data);
                const decoder = new TextDecoder();
                let start = 6;
                let end = start;
                while (end < uint8.length && uint8[end] >= 32 && uint8[end] <= 126) end++;
                const nameBytes = uint8.slice(start, end);
                usernamearray = Array.from(nameBytes);
                playername = decoder.decode(nameBytes).trim().replace(/[^\x20-\x7E]/g, '');
                console.log("[Chat Panel] Username detected:", playername);
            }
            originalSend.apply(this, arguments);
        };

        return ws;
    };

    // ─── Helpers ────────────────────────────────────────────────────────────────
    const id = el => document.getElementById(el);
    const crt = tag => document.createElement(tag);

    // ─── Chat functions ─────────────────────────────────────────────────────────
    const cheat = {
        unknownmessage: (prefix) => {
            const message = id('cheatinput')?.value?.trim();
            if (!message || !client.ws) return;
            const fullText = prefix + message;
            const encoder = new TextEncoder();
            const encoded = encoder.encode(fullText);
            const len = encoded.length;
            const header = [8, 5, 18, len + 5, 8, 223, 18, 18, len];
            const packet = new Uint8Array(header.length + len);
            packet.set(header);
            packet.set(encoded, header.length);
            client.ws.send(packet);
            if (id('cheatinput')) id('cheatinput').value = '';
        },

        clearchat: () => {
            if (!client.ws) return;
            const clear = [8, 5, 18, 9, 8, 223, 18, 18, 4, 32, 227, 133, 164];
            const arr = new Uint8Array(clear);
            for (let i = 0; i < 13; i++) client.ws.send(arr);
        }
    };

    // ─── Styles ─────────────────────────────────────────────────────────────────
    const styles = `
        #chatWindow {
            position: fixed; top: 20px; left: 1080px; transform: none);
            width: 440px; height: 260px; background: #1a1b16; border: 1px solid #646464;
            border-radius: 15px; box-shadow: 0 0 10px #000; z-index: 99999; color: white;
            font-family: Arial; display: none; overflow: hidden;
        }
        #titleBar {
            height: 36px; background: #161616; text-align: center; line-height: 36px;
            font-size: 18px; border-top-left-radius: 15px; border-top-right-radius: 15px;
            user-select: none; position: relative;
        }
        #closeBtn {
            position: absolute; right: 0; top: 0; width: 36px; height: 36px;
            background: #c00; color: white; font-size: 22px; cursor: pointer;
            border-top-right-radius: 15px;
        }
        #cpage2 {
            padding: 16px; height: calc(100% - 36px); box-sizing: border-box;
        }
        #cheatinput {
            width: 100%; max-width: 380px; padding: 10px; margin: 12px 0 16px 0;
            border-radius: 8px; background: #252525; border: none; color: white;
            font-size: 15px; display: block;
        }
        .button-row {
            display: flex; gap: 12px; flex-wrap: wrap; margin: 12px 0;
        }
        .cheatBtn {
            padding: 10px 24px; border: none; border-radius: 8px;
            color: white; cursor: pointer; font-size: 14px; flex: 1;
            min-width: 140px; text-align: center;
        }
        .cheatBtn:hover { opacity: 0.9; }
        #sendBtn { background: #0066cc; }
        #clearBtn { background: #555; }
        #chkDiv { margin: 12px 0; font-size: 14px; }
    `;

    const styleEl = crt('style');
    styleEl.textContent = styles;
    document.head.appendChild(styleEl);

    // ─── Build UI ───────────────────────────────────────────────────────────────
    const win = crt('div');
    win.id = 'chatWindow';
    document.body.appendChild(win);

    const title = crt('div');
    title.id = 'titleBar';
    title.textContent = 'Chat Panel';
    win.appendChild(title);

    const close = crt('div');
    close.id = 'closeBtn';
    close.textContent = 'X';
    close.onclick = () => win.style.display = 'none';
    title.appendChild(close);

    const page = crt('div');
    page.id = 'cpage2';
    win.appendChild(page);

    const input = crt('input');
    input.id = 'cheatinput';
    input.placeholder = 'Enter message...';
    page.appendChild(input);

    // Row with both buttons side by side
    const buttonRow = crt('div');
    buttonRow.className = 'button-row';
    page.appendChild(buttonRow);

    const send = crt('button');
    send.id = 'sendBtn';
    send.className = 'cheatBtn';
    send.textContent = 'Send Message';
    buttonRow.appendChild(send);

    const clearBtn = crt('button');
    clearBtn.id = 'clearBtn';
    clearBtn.className = 'cheatBtn';
    clearBtn.textContent = 'Clear Chat';
    buttonRow.appendChild(clearBtn);

    // ─── Team Command Row ─────────────────────────────────────────────
const teamRow = crt('div');
teamRow.className = 'button-row';
page.appendChild(teamRow);

const redBtn = crt('button');
redBtn.className = 'cheatBtn';
redBtn.style.background = '#b30000';
redBtn.textContent = 'Join Red';
teamRow.appendChild(redBtn);

const blueBtn = crt('button');
blueBtn.className = 'cheatBtn';
blueBtn.style.background = '#0033cc';
blueBtn.textContent = 'Join Blue';
teamRow.appendChild(blueBtn);

const specBtn = crt('button');
specBtn.className = 'cheatBtn';
specBtn.style.background = '#444';
specBtn.textContent = 'Spectate';
teamRow.appendChild(specBtn);

    const chkDiv = crt('div');
    chkDiv.id = 'chkDiv';
    page.appendChild(chkDiv);

    const chk = crt('input');
    chk.type = 'checkbox';
    chk.id = 'checkbox';
    chkDiv.appendChild(chk);

    const label = crt('label');
    label.htmlFor = 'checkbox';
    label.textContent = 'Send With Name';
    chkDiv.appendChild(label);

    // ─── Event listeners ────────────────────────────────────────────────────────
    send.onclick = () => {
        const prefix = chk.checked && playername ? playername + ": " : "";
        cheat.unknownmessage(prefix);
    };

    clearBtn.onclick = cheat.clearchat;
    redBtn.onclick = () => client.sendMessage('/team red');
    blueBtn.onclick = () => client.sendMessage('/team blue');
    specBtn.onclick = () => client.sendMessage('/team spectator');

    input.addEventListener('keydown', e => {
        if (e.key === 'Enter') {
            e.preventDefault();
            send.click();

        }
    });

    document.addEventListener('keydown', e => {
        if (e.key === '[' && !e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey) {
            e.preventDefault();
            win.style.display = win.style.display === 'block' ? 'none' : 'block';
            if (win.style.display === 'block') input.focus();
        }
    });

    console.log("[Chat Panel] Ready – press [ to open");
})();