Greasy Fork is available in English.

Dylko's Bumpy Mod

Unlimiter, chat hotkeys, and chat panel script

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==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");
})();