Embed diep lobby selector into diep.io page
// ==UserScript==
// @name Embedded diep lobby selector
// @namespace http://tampermonkey.net/
// @version 3.1
// @description Embed diep lobby selector into diep.io page
// @author Eclipsia discord@cz_eclipsia reddit@Ok-Wing4342
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @match *://*.diep.io/*
// @license MIT
// @grant none
// @run-at document-start
// ==/UserScript==
/*
TODO:
make the inner div contained in a space (done)
stop execution if display is none (done)
add a way to choose a team in teams and 4teams (done)
edit css of buttons (done)
test future events and beta data (done)
game version button switcher mostly done ready to test (done)
add options to enable and disable render latency and net predict (done)
add special to sandboxes
highlighted mode now matches the color of your team
same with score and background
// https://stackoverflow.com/questions/79614720/how-do-i-add-hover-and-active-events-to-my-tampermonkey-injected-script
*/
(async () => {
'use strict';
// region Top
// Top level variables, change TOGGLE_KEYBIND to whatever you want (to change display visibility)
const TOGGLE_KEYBIND = 'q';
const UPDATE_DELAY = 1000; // miliseconds
const BASE_API = "https://lb.diep.io/api/lb/"
const BETA_API = "https://master-dev.diep.io/api/lb/"
const storageKey = "diepioLobbyselect_"
const re_isBeta = /https?:\/\/(?<beta>beta\.)?diep\.io\/?.*/
// region local storage management
const storage = {
get(key) {
return localStorage.getItem(storageKey + key)
},
async set(key, value) {
localStorage.setItem(storageKey + key, value);
console.log('set', key, 'to', value);
},
getBool(key, defaultValue) {
let value = this.get(key, null);
return value === null ? defaultValue : JSON.parse(value)
},
/**
* @returns {number}
*/
getNumber(key, defaultValue) {
let value = this.get(key, null);
return value === null ? defaultValue : parseFloat(value);
},
}
// region unfuck diep
const PREDICT_MOVEMENT = "net_predict_movement";
const REN_LATENCY = "ren_latency";
const consoleLog = console.log;
let [backgroundSuccess, windowSuccess, scoreSuccess] = [false, false, false];
let renderLatency = storage.getBool("renLatency") ?? true;
let predictMovement = storage.getBool("predictMovement") ?? false;
storage.set("predictMovement", predictMovement);
storage.set("renLatency", renderLatency);
async function unfuckDiep() {
if (!windowSuccess && window.input) {
// force predict_movement off, bring back latency statistic
input.inGameNotification
window.input.set_convar(REN_LATENCY, renderLatency);
window.input.set_convar(PREDICT_MOVEMENT, predictMovement);
windowSuccess = true;
}
let backgroundElem;
if (!backgroundSuccess && (backgroundElem = document.getElementById("game-over-screen"))) {
// remove blurry deathscreen background
backgroundElem.style.setProperty("backdrop-filter", "none", "important");
backgroundSuccess = true;
}
let scoreTextElem;
if (!scoreSuccess && (scoreTextElem = document.getElementById("game-over-stats-player-score"))) {
// unfuck the score
let oldValue = scoreTextElem.textContent;
const callback = () => {
let newValue = scoreTextElem.textContent;
if (newValue == oldValue) return;
scoreTextElem.textContent = scoreTextElem.textContent.replace(/\B(?=(\d{3})+(?!\d))/g, ",")
oldValue = newValue;
}
new MutationObserver(callback).observe(scoreTextElem, {
childList: true,
subtree: true,
characterData: true
})
scoreSuccess = true
}
if (backgroundSuccess && windowSuccess && scoreSuccess) {
consoleLog(`Finished unfucking job.`)
return;
}
await Promise.resolve(new Promise(r => setTimeout(r, 500)));
return await unfuckDiep();
};
await unfuckDiep();
// region Colors
const intToTeam = [
"blue",
"red",
"purple",
"green",
];
const intToColor = [
"rgba(33, 143, 221, 1)", // blue
"rgba(232, 27, 27, 1)", // red
"rgba(184, 13, 207, 1)", // purple
"rgba(81, 220, 34, 1)", // green
];
const GREEN = "rgba(0, 255, 0, 1)"
const RED = "rgba(255, 0, 0, 1)"
let colors = {
fra: "rgba(255, 0, 0, 0.5)",
atl: "rgba(255, 255, 0, 0.5)",
sgp: "rgba(50, 50, 200, 0.5)",
syd: "rgba(0, 255, 0, 0.5)"
}
const defaultColor = "rgba(255, 255, 255, 0.5)"
function offsetRGBA(rgba = "rgba(0, 0, 0, 0)", offset = 0, alphaOffset = 0.0) {
const regex = /rgba\((?<r>\d{1,3})\D*,\D*(?<g>\d{1,3})\D*,\D*(?<b>\d{1,3})\D*,\D*(?<a>[0-1](\.\d)?)\)/
const res = regex.exec(rgba);
if (!res?.groups) throw Error(`failed regex: '${rgba}'`);
const grps = res.groups;
const r = parseInt(grps.r) + offset;
const g = parseInt(grps.g) + offset;
const b = parseInt(grps.b) + offset;
const a = parseFloat(grps.a) + alphaOffset;
return `rgba(${r}, ${g}, ${b}, ${a})`
}
const commonlySharedButtonCSS = `
padding: 5px;
border: 1px solid black;
background-color: rgba(255, 255, 255, 0.5);
cursor: pointer;
transition: background-color 0.2s ease;
margin-left: 5px;
margin-bottom: 2px;
border-radius: 5px;
`;
const thisURL = new URL(document.URL)
// TODO: sandboxes
let currentLobbyIp = thisURL.searchParams.get("ip")
const lobbyInfo = thisURL.searchParams.get("l")?.split("x").map(v => parseInt(v))
const [lobbyId, currentTeamIndex] = lobbyInfo ?? [0, 0];
// region storage get
/** @type {"verbose" | "concise"} */
let layoutStyle = storage.get("layoutStyle") ?? "verbose"
let buttonIndex = storage.getNumber('teamIndex', 0);
let apiDevice = storage.get('device') || (thisURL.host.startsWith("mobile") ? "mobile" : "pc");
let showUI = true;
storage.set("layoutStyle", layoutStyle);
storage.set("teamIndex", buttonIndex);
storage.set("device", apiDevice);
/** @type {"beta" | "main"} */
let APItarget = !!re_isBeta.exec(document.URL)?.groups?.beta ? "beta" : "main"
if (APItarget == "main") {
APItarget = storage.get('APItarget') ?? "main"
console.log(APItarget)
storage.set('APItarget', APItarget)
}
// region helper funcs
function getAPIEndpoint(device) {
return (APItarget == "beta" ? BETA_API : BASE_API) + (device ?? apiDevice)
}
function getTextForNumPlayers(numPlayers) {
// this function was vibe coded
if (numPlayers < 850) return "Very Low";
if (numPlayers < 900) return "Low";
if (numPlayers < 950) return "Below Average";
if (numPlayers < 1050) return "Average";
if (numPlayers < 1100) return "Above Average";
if (numPlayers < 1200) return "High";
return "Very High";
}
function formatSeconds(seconds) {
const days = Math.floor(seconds / (3600 * 24));
const hours = Math.floor(seconds / 3600 % 24);
const minutes = Math.floor(seconds / 60 % 60);
seconds = Math.floor(seconds % 60);
let output = "";
if (days) output += `${days}d `
if (hours) output += `${hours}h `
if (minutes) output += `${minutes}m `
if (seconds) output += `${seconds}s`
return output.trim();
}
// region createLobbyButton
function createLobbyButton(region, lobby) {
const button = document.createElement('button');
button.style.cssText = commonlySharedButtonCSS;
;
let gamemodeName;
if (layoutStyle == "verbose") {
gamemodeName = lobby.gamemodeName || lobby.gamemode;
} else {
gamemodeName = lobby.gamemode;
if (
lobby.gamemodeName.toLowerCase().includes("maze")
) gamemodeName = lobby.gamemodeName.toLowerCase();
}
button.textContent = `${gamemodeName} ${lobby.numPlayers}p`;
let futureEventButton;
if (lobby.nextGamemodeName) {
futureEventButton = document.createElement("button")
futureEventButton.style.cssText = commonlySharedButtonCSS;
const secondsUntilText = formatSeconds(lobby.secondsUntilNextGamemode);
futureEventButton.textContent =
`${lobby.gamemodeName ?? lobby.gamemode} => ${lobby.nextGamemodeName} in ${secondsUntilText}`
}
const buttonProxyHandler = {
set(target, p, newValue, receiver) {
button.style[p] = newValue
if (futureEventButton) futureEventButton.style[p] = newValue
return true
},
}
const bothButtonStyle = new Proxy({}, buttonProxyHandler);
const bgColor = colors[region.region] || defaultColor
button.style.backgroundColor = bgColor;
if (lobby.ip === currentLobbyIp) {
bothButtonStyle.border = "5px solid black";
if (lobby.gamemode.includes("teams") || currentTeamIndex > 0) {
bothButtonStyle.backgroundColor = intToColor[currentTeamIndex];
button.textContent += ` (${intToTeam[currentTeamIndex]})`;
} else {
bothButtonStyle.backgroundColor = offsetRGBA(bgColor, -40)
}
} else {
bothButtonStyle.border = "1px solid black";
}
if (futureEventButton) {
futureEventButton.style.backgroundColor = offsetRGBA(bgColor, 120)
}
return {button, futureEventButton};
}
// region async update()
async function update() {
const response = await fetch(getAPIEndpoint());
const value = await response.json();
/** @type {object[]} */
const data = value.regions;
const count = data.map(region => region.numPlayers).reduce((prv, cur) => prv + cur, 0);
numPlayers.textContent = `Players Active: ${count} (${getTextForNumPlayers(count)})`
if (!count) {
buttons.style.display = "none"
return
}
buttons.style.display = "block";
// https://stackoverflow.com/questions/3955229/remove-all-child-elements-of-a-dom-node-in-javascript
buttons.textContent = "";
for (const region of data) {
const countryCode = region.countryCode ? ` (${region.countryCode})` : ""
const playerCount = document.createElement('p');
playerCount.textContent = `${region.regionName}${countryCode} ${region.numPlayers} Players`
playerCount.style.cssText = `
margin-right: 0px;
background-color: ${colors[region.region] ?? defaultColor};
`;
playerCount.style.color = offsetRGBA(playerCount.style.backgroundColor, 200)
buttons.appendChild(playerCount);
const futureEventButtons = [];
for (let index = 0; index < region.lobbies.length; index++) {
// put every sixth lobby on a new row
if (index % 5 == 1 && index > 5) {
buttons.appendChild(document.createElement('br'));
}
const lobby = region.lobbies[index];
const {button, futureEventButton} = createLobbyButton(region, lobby);
button.addEventListener('click', () => {
let teamIndex = buttonIndex;
if (lobby.gamemode === "teams"
&& teamIndex > 1
|| !lobby.gamemode.includes("teams")
&& lobby.gamemode !== "event"
) {
teamIndex = 0;
}
// https://diep.io/?lobby=fra_teams_fra-43ce658e19c456d0.diep.io:2001_55544_0
const searchParams = new URLSearchParams();
searchParams.set("lobby", `${region.region}_${lobby.gamemode}_${lobby.ip}_${"x" || "lobbyid"}_${teamIndex}`)
window.open(`https://${apiDevice == 'mobile' ? 'mobile.' : ''}diep.io/?${searchParams.toString()}`)
});
buttons.appendChild(button);
if (futureEventButton) futureEventButtons.push(futureEventButton);
}
if (layoutStyle == "verbose") {
buttons.appendChild(document.createElement('br'));
futureEventButtons.forEach(button => buttons.appendChild(button))
}
buttons.appendChild(document.createElement('br'));
}
}
update()
let IntervalID = setInterval(update, UPDATE_DELAY);
// toggle display visibility
async function toggle() {
const display = container.style.display;
container.style.display = display == 'block' ? 'none' : 'block';
showUI = !showUI;
if (showUI) {
update();
IntervalID = setInterval(update, UPDATE_DELAY);
} else {
clearInterval(IntervalID);
}
}
document.addEventListener('keydown', (ev) => {
if (ev.key === TOGGLE_KEYBIND) {
toggle();
}
})
// region elements
// seperate each element with 2 empty lines
// region text
const container = document.createElement("div");
container.style.cssText = `
position: absolute;
left: 0%;
top: 0%;
z-index: 999999;
background-color: rgba(255, 255, 255, 0.5);
padding: 5px;
border: 1px solid black;
border-radius: 5px;
display: block;
`;
document.body.append(container);
const title = document.createElement("p");
title.textContent = 'Diep lobby selector by Eclipsia';
title.style.cssText = `
position: relative;
z-index: 999999;
padding: 0px;
margin: 0px;
color: black;
`;
container.appendChild(title);
const numPlayers = document.createElement('p');
numPlayers.textContent = "Players Active: ?";
numPlayers.style.cssText = `
position: relative;
z-index: 999999;
padding: 0px;
margin: 0px;
margin-bottom: 3px;
color: black;
`;
container.appendChild(numPlayers);
// region button row
const buttonDiv = document.createElement('div');
buttonDiv.style.cssText = `
right: 0%;
display: flex;
`;
container.appendChild(buttonDiv);
const apiDeviceBtn = document.createElement("button");
apiDeviceBtn.textContent = "device: " + apiDevice;
apiDeviceBtn.style.cssText = `
border: 3px solid black;
background-color: rgba(255, 255, 255, 0.8);
margin-right: 3px;
padding: 2px;
`;
apiDeviceBtn.addEventListener("click", () => {
apiDevice = apiDevice === "mobile" ? "pc" : "mobile";
apiDeviceBtn.textContent = "device: " + apiDevice;
storage.set('device', apiDevice);
});
buttonDiv.appendChild(apiDeviceBtn);
const gameReleaseBtn = document.createElement('button');
gameReleaseBtn.textContent = "API: " + APItarget;
gameReleaseBtn.style.cssText = `
border: 3px solid black;
background-color: rgba(255, 255, 255, 0.8);
display: flex;
padding: 2px;
margin-right: 3px;
`;
gameReleaseBtn.addEventListener('click', () => {
APItarget = APItarget == "beta" ? "main" : "beta";
gameReleaseBtn.textContent = "API: " + APItarget;
storage.set('APItarget', APItarget);
});
buttonDiv.appendChild(gameReleaseBtn);
const layoutStyleBtn = document.createElement("button");
layoutStyleBtn.textContent = "style: " + layoutStyle;
layoutStyleBtn.style.cssText = `
border: 3px solid black;
background-color: rgba(255, 255, 255, 0.8);
display: flex;
padding: 2px;
`;
layoutStyleBtn.addEventListener("click", () => {
layoutStyle = layoutStyle == "verbose" ? "concise" : "verbose";
layoutStyleBtn.textContent = "style: " + layoutStyle;
storage.set("layoutStyle", layoutStyle);
})
buttonDiv.appendChild(layoutStyleBtn)
// region config buttons
const teamDiv = document.createElement('div');
teamDiv.style.cssText = `
z-index: 999999;
background-color: rgba(25, 160, 205, 1);
border: 1px solid black;
border-radius: 5px;
display: flex;
height: 20px;
`;
container.appendChild(teamDiv);
const teamTitle = document.createElement('p');
teamTitle.textContent = "target team: ";
teamTitle.style.cssText = `
margin-top: 0px;
margin-left: 3px;
`;
teamDiv.appendChild(teamTitle);
const teamButton = document.createElement('button');
teamButton.textContent = intToTeam[buttonIndex];
teamButton.style.cssText = `
margin-left: 5px;
background-color: ${intToColor[buttonIndex]};
`;
teamButton.addEventListener('click', () => {
buttonIndex = (buttonIndex + 1) % intToTeam.length;
teamButton.textContent = intToTeam[buttonIndex];
teamButton.style.backgroundColor = intToColor[buttonIndex];
storage.set('teamIndex', buttonIndex);
});
teamDiv.appendChild(teamButton);
// region net_predict
const divPredictMovement = document.createElement("div");
divPredictMovement.style.cssText = `
z-index: 999999;
background-color: rgba(222, 52, 248, 0.55);
border: 1px solid black;
border-radius: 5px;
display: flex;
height: 20px;
`;
container.appendChild(divPredictMovement)
const netPredictLabel = document.createElement("p")
netPredictLabel.textContent = "net_predict_movement: "
netPredictLabel.style.cssText = `
margin-top: 0px;
margin-left: 3px;
`;
divPredictMovement.appendChild(netPredictLabel)
const netPredictBtn = document.createElement("button");
netPredictBtn.textContent = predictMovement
netPredictBtn.style.cssText = `
background-color: ${predictMovement ? GREEN : RED};
margin-left: 5px;
`;
netPredictBtn.addEventListener('click', () => {
predictMovement = !predictMovement;
netPredictBtn.textContent = predictMovement;
netPredictBtn.style.backgroundColor = predictMovement ? GREEN : RED;
input.set_convar(PREDICT_MOVEMENT, predictMovement);
storage.set("predictMovement", predictMovement);
});
divPredictMovement.appendChild(netPredictBtn);
// render latency
const divRenderLatency = document.createElement("div");
divRenderLatency.style.cssText = `
z-index: 999999;
background-color: rgba(200, 200, 40, 0.55);
border: 1px solid black;
border-radius: 5px;
display: flex;
height: 20px;
`;
container.appendChild(divRenderLatency);
const renLatencyLabel = document.createElement("p")
renLatencyLabel.textContent = "render_latency: "
renLatencyLabel.style.cssText = `
margin-top: 0px;
margin-left: 3px;
`;
divRenderLatency.appendChild(renLatencyLabel)
const renderLatencyBtn = document.createElement("button");
renderLatencyBtn.textContent = renderLatency
renderLatencyBtn.style.cssText = `
background-color: ${renderLatency ? GREEN : RED};
margin-left: 5px;
`;
renderLatencyBtn.addEventListener('click', () => {
renderLatency = !renderLatency;
renderLatencyBtn.textContent = renderLatency;
renderLatencyBtn.style.backgroundColor = renderLatency ? GREEN : RED;
input.set_convar(REN_LATENCY, renderLatency);
storage.set("renLatency", renderLatency);
});
divRenderLatency.appendChild(renderLatencyBtn);
// region buttons div
const buttons = document.createElement('div');
buttons.style.cssText = `
right: 0%;
top: 0%;
z-index: 999999;
background-color: rgba(141, 24, 187, 1);
padding: 5px;
border: 1px solid black;
border-radius: 5px;
display: block;
`;
container.appendChild(buttons);
// region end
// synchronise graphics
window.console.log = consoleLog
console.log("Initial showUi is", showUI);
toggle()
toggle()
})();