Greasy Fork is available in English.
Unlimiter, chat hotkeys, and chat panel script
// ==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");
})();