Allows for people to join the private server whenever it's open.
// ==UserScript==
// @name MacPS Button
// @author Mac
// @description Allows for people to join the private server whenever it's open.
// @version 1.34
// @namespace MacPS
// @license MIT
// @antifeature remote-config
// @match https://cavegame.io/*
// @match https://mineroyale.io/*
// @match http://localhost:8080/*
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @connect drive.google.com
// @connect drive.usercontent.google.com
// @connect trycloudflare.com
// @connect *.trycloudflare.com
// ==/UserScript==
(() => {
'use strict';
const CONFIG_URL =
'https://drive.google.com/uc?export=download&id=1F8AfmLdeXXcpdEGCm04hKYY2T22gbVmz';
const REQUEST_TIMEOUT = 5000;
const REFRESH_COOLDOWN = 1500;
const CONNECT_TIMEOUT = 10000;
const STATE = Object.freeze({
CHECKING: 'Checking...',
ONLINE: 'Online',
IDLE: 'Idle',
OFFLINE: 'Offline',
ERROR: 'Error',
CONNECTING: 'Connecting...'
});
let currentState = STATE.CHECKING;
let websocketTarget = '';
let button = null;
let refreshBtn = null;
let refreshCooldown = 0;
let refreshBusy = false;
const NativeWebSocket = unsafeWindow.WebSocket;
let wsHookActive = false;
let wasMenuOpen = false;
let connectTimer = null;
function isDarkMode() {
const btn =
document.getElementById('play-cavegame-io') ||
document.getElementById('play-mineroyale-io');
return btn?.classList.contains('dark');
}
function isLocked() {
return currentState === STATE.CHECKING || currentState === STATE.CONNECTING;
}
function isMainDisabled() {
return (
isDarkMode() ||
currentState === STATE.ERROR ||
currentState === STATE.IDLE ||
currentState === STATE.OFFLINE ||
isLocked()
);
}
function isRefreshDisabled() {
return (
isDarkMode() ||
refreshBusy ||
isLocked() ||
Date.now() < refreshCooldown
);
}
function getStateColor(state) {
switch (state) {
case STATE.ONLINE:
return '#22c55e';
case STATE.IDLE:
return '#f59e0b';
case STATE.CHECKING:
case STATE.CONNECTING:
return '#9ca3af';
default:
return '#ef4444';
}
}
function stopWSHook() {
wsHookActive = false;
unsafeWindow.WebSocket = NativeWebSocket;
}
function enableWSHook() {
wsHookActive = true;
unsafeWindow.WebSocket = function (...args) {
let url = args[0];
if (!wsHookActive || currentState !== STATE.CONNECTING) {
return new NativeWebSocket(url, ...args.slice(1));
}
if (
typeof url === 'string' &&
websocketTarget &&
(url.startsWith('ws://') || url.startsWith('wss://'))
) {
url = websocketTarget;
}
return new NativeWebSocket(url, ...args.slice(1));
};
unsafeWindow.WebSocket.prototype = NativeWebSocket.prototype;
}
function resetConnectionState() {
stopWSHook();
if (connectTimer) {
clearTimeout(connectTimer);
connectTimer = null;
}
if (currentState === STATE.CONNECTING) {
currentState = STATE.IDLE;
}
updateUI();
}
function applyMainButtonState() {
if (!button) return;
const disabled = isMainDisabled();
const status = button.querySelector('.macps-status');
if (status) {
status.textContent = currentState;
status.style.color = getStateColor(currentState);
status.style.fontWeight = '700';
}
button.style.background =
'linear-gradient(-225deg,#4da3ff,#1e66d0)';
button.style.color = '#fff';
button.style.border = 'none';
button.style.opacity = disabled ? '0.6' : '1';
button.style.pointerEvents = disabled ? 'none' : 'auto';
button.style.cursor = disabled ? 'not-allowed' : 'pointer';
button.style.filter = disabled ? 'grayscale(0.25)' : 'none';
}
function updateRefresh() {
if (!refreshBtn) return;
const locked = isRefreshDisabled();
refreshBtn.style.background = '#e5e7eb';
refreshBtn.style.color = '#111';
refreshBtn.style.border = 'none';
refreshBtn.style.width = '55px';
refreshBtn.style.height = '42px';
refreshBtn.style.display = 'flex';
refreshBtn.style.alignItems = 'center';
refreshBtn.style.justifyContent = 'center';
refreshBtn.style.cursor = locked ? 'not-allowed' : 'pointer';
refreshBtn.style.opacity = locked ? '0.6' : '1';
refreshBtn.style.filter = locked ? 'grayscale(1)' : 'none';
refreshBtn.style.pointerEvents = locked ? 'none' : 'auto';
}
function updateUI() {
applyMainButtonState();
updateRefresh();
}
function setState(state, target = '') {
currentState = state;
if (target) {
websocketTarget = target;
}
if (state !== STATE.CONNECTING) {
stopWSHook();
} else {
if (connectTimer) {
clearTimeout(connectTimer);
}
connectTimer = setTimeout(() => {
if (currentState === STATE.CONNECTING) {
resetConnectionState();
}
}, CONNECT_TIMEOUT);
}
updateUI();
}
function isMenuOpen() {
const overlay = document.getElementById('menu-overlay');
return overlay && getComputedStyle(overlay).display !== 'none';
}
function watchMenu() {
const open = isMenuOpen();
if (open && !wasMenuOpen) {
refreshServerStatus();
}
if (!open && wasMenuOpen) {
resetConnectionState();
}
wasMenuOpen = open;
}
function request(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url,
timeout: REQUEST_TIMEOUT,
onload: resolve,
onerror: reject,
ontimeout: reject
});
});
}
function isValidWebSocketURL(url) {
try {
const u = new URL(url);
return u.protocol === 'wss:' && u.hostname.length > 0;
} catch {
return false;
}
}
async function fetchServerAddress() {
try {
const res = await request(CONFIG_URL);
const text = (res.responseText || '').trim();
return isValidWebSocketURL(text) ? text : null;
} catch {
return null;
}
}
async function verifyServer(url) {
try {
if (!url || !url.trim()) {
return STATE.OFFLINE;
}
const hostname = new URL(url).hostname;
const httpsURL = `https://${hostname}`;
const res = await request(httpsURL);
const text = (res.responseText || '').trim();
if (text === 'OK') {
return STATE.ONLINE;
}
if (
text.includes('502 Bad Gateway') ||
text.includes('Unable to reach the origin service')
) {
return STATE.IDLE;
}
return STATE.OFFLINE;
} catch {
return STATE.OFFLINE;
}
}
async function refreshServerStatus() {
if (isDarkMode()) return;
refreshBusy = true;
setState(STATE.CHECKING);
const url = await fetchServerAddress();
if (!url) {
setState(STATE.OFFLINE);
refreshBusy = false;
refreshCooldown = Date.now() + REFRESH_COOLDOWN;
updateUI();
return;
}
const state = await verifyServer(url);
setState(
state,
state === STATE.ONLINE ? url : ''
);
refreshBusy = false;
refreshCooldown = Date.now() + REFRESH_COOLDOWN;
updateUI();
}
function onMainClick(e) {
e.preventDefault();
e.stopPropagation();
if (isMainDisabled()) return;
if (!websocketTarget) return;
setState(STATE.CONNECTING);
enableWSHook();
const play =
document.getElementById('play-mineroyale-io') ||
document.getElementById('play-cavegame-io');
play?.click();
}
function onRefreshClick(e) {
e.preventDefault();
e.stopPropagation();
if (isRefreshDisabled()) return;
refreshServerStatus();
}
function createUI() {
const container = document.getElementById('sub-play-btn-cont');
if (!container || document.getElementById('cg-macps-wrap')) {
return;
}
const wrap = document.createElement('div');
wrap.id = 'cg-macps-wrap';
wrap.style.display = 'inline-flex';
wrap.style.alignItems = 'center';
wrap.style.width = '94%';
button = document.createElement('button');
button.className = 'play-btn';
button.innerHTML = `
MacPS
<div class="macps-status">${currentState}</div>
`;
button.addEventListener('click', onMainClick);
refreshBtn = document.createElement('button');
refreshBtn.textContent = '↻';
refreshBtn.addEventListener('click', onRefreshClick);
wrap.appendChild(button);
wrap.appendChild(refreshBtn);
container.appendChild(wrap);
updateUI();
}
new MutationObserver(() => {
if (!document.getElementById('cg-macps-wrap')) {
createUI();
}
}).observe(document.body, {
childList: true,
subtree: true
});
setInterval(() => {
watchMenu();
updateUI();
}, 50);
createUI();
})();