Skip to content

Instantly share code, notes, and snippets.

@maanimis
Last active February 27, 2025 13:01
Show Gist options
  • Select an option

  • Save maanimis/42dd254450a10f8f087ae8447d8c91ee to your computer and use it in GitHub Desktop.

Select an option

Save maanimis/42dd254450a10f8f087ae8447d8c91ee to your computer and use it in GitHub Desktop.
Reconnecting WebSocket for territorial.io-Violentmonkey Scripts
// ==UserScript==
// @name Reconnecting WebSocket-territorial.io
// @namespace Violentmonkey Scripts
// @match https://territorial.io/*
// @grant none
// @version 3.2
// @description Reconnecting WebSocket for territorial.io with improved stability.
// @run-at document-start
// ==/UserScript==
console.log("Running WebSocket injector...");
const OriginalWebSocket = WebSocket;
let shouldReconnect = true; // Track whether we should attempt to reconnect
let reconnectAttempts = 0;
const maxReconnectAttempts = 30;
const baseReconnectDelay = 1000; // Initial delay (1s)
let wsInstance = null;
window.WebSocket = function (url, protocols) {
const ws = new OriginalWebSocket(url, protocols);
wsInstance = ws;
const eventListeners = {
open: [],
message: [],
close: [],
error: [],
};
const reconnect = () => {
if (!shouldReconnect || reconnectAttempts >= maxReconnectAttempts) {
console.warn("Max reconnection attempts reached or manual close detected.");
return;
}
const reconnectDelay = baseReconnectDelay * Math.pow(2, reconnectAttempts);
console.info(`Reconnecting in ${reconnectDelay / 1000} seconds... (Attempt ${reconnectAttempts + 1})`);
setTimeout(() => {
reconnectAttempts++;
const newSocket = new OriginalWebSocket(url, protocols);
wsInstance = newSocket;
// Restore event listeners
for (const eventType in eventListeners) {
eventListeners[eventType].forEach(listener => newSocket.addEventListener(eventType, listener));
}
newSocket.addEventListener("close", (event) => {
console.warn(`WebSocket closed: Code ${event.code}, Reason: ${event.reason || "No reason provided."}`);
if (shouldReconnect) reconnect();
});
newSocket.addEventListener("error", () => {
console.error("WebSocket encountered an error. Attempting to reconnect...");
reconnect();
});
reconnectAttempts = 0; // Reset attempts on successful reconnection
console.info("Reconnected successfully.");
}, reconnectDelay);
};
ws.addEventListener("error", () => {
console.error("WebSocket error occurred. Reconnecting...");
reconnect();
});
ws.addEventListener("close", (event) => {
console.warn(`WebSocket closed: Code ${event.code}, Reason: ${event.reason || "No reason provided."}`);
if (shouldReconnect) reconnect();
});
// Intercept addEventListener to store custom listeners
const originalAddEventListener = ws.addEventListener.bind(ws);
ws.addEventListener = (type, listener, options) => {
if (eventListeners[type]) {
eventListeners[type].push(listener);
}
originalAddEventListener(type, listener, options);
};
// Intercept removeEventListener to properly clean up
const originalRemoveEventListener = ws.removeEventListener.bind(ws);
ws.removeEventListener = (type, listener, options) => {
if (eventListeners[type]) {
eventListeners[type] = eventListeners[type].filter(l => l !== listener);
}
originalRemoveEventListener(type, listener, options);
};
// Override close method to track manual closures
const originalClose = ws.close.bind(ws);
ws.close = (code, reason) => {
shouldReconnect = false; // Stop reconnecting if manually closed
console.info(`WebSocket manually closed: Code ${code}, Reason: ${reason || "No reason provided."}`);
originalClose(code, reason);
};
console.log("New WebSocket connection established!");
return ws;
};
/*
// ==UserScript==
// @name Reconnecting WebSocket-territorial.io
// @namespace Violentmonkey Scripts
// @match https://territorial.io/*
// @grant none
// @version 3.2
// @description Reconnecting WebSocket for territorial.io with backoff and cleanup.
// @run-at document-start
// ==/UserScript==
console.log("Running WebSocket injector...");
const OriginalWebSocket = WebSocket;
window.WebSocket = function (url, protocols) {
let ws = new OriginalWebSocket(url, protocols);
let reconnectAttempts = 0;
const maxReconnectAttempts = 30;
const baseReconnectDelay = 1000; // Starting delay in ms
const maxReconnectDelay = 30000; // Cap delay at 30 seconds
let isReconnecting = false;
let reconnectTimeout = null;
const customListeners = {
open: [],
message: [],
close: [],
error: [],
};
const calculateDelay = () => {
// Exponential backoff: 1s, 2s, 4s, 8s, ..., capped at maxReconnectDelay
// const delay = Math.min(baseReconnectDelay * Math.pow(2, reconnectAttempts), maxReconnectDelay);
// return delay + Math.random() * 1000; // Add jitter to avoid synchronized retries
return this.baseReconnectDelay
};
const reconnect = () => {
if (isReconnecting || reconnectAttempts >= maxReconnectAttempts || ws.readyState === WebSocket.OPEN) {
if (reconnectAttempts >= maxReconnectAttempts) {
console.warn("Max reconnection attempts reached.");
}
return;
}
isReconnecting = true;
reconnectAttempts++;
const delay = calculateDelay();
console.info(`Reconnecting... Attempt ${reconnectAttempts} after ${Math.round(delay)}ms`);
reconnectTimeout = setTimeout(() => {
// Clean up the old WebSocket
if (ws && ws.readyState !== WebSocket.CLOSED) {
ws.close();
}
// Create new WebSocket
const newSocket = new OriginalWebSocket(url, protocols);
newSocket.onopen = ws.onopen;
newSocket.onmessage = ws.onmessage;
newSocket.onclose = ws.onclose;
newSocket.onerror = ws.onerror;
// Reattach custom listeners
for (const eventType in customListeners) {
customListeners[eventType].forEach((listener) => {
newSocket.addEventListener(eventType, listener);
});
}
// Replace the old WebSocket reference
ws = newSocket;
// Reset state on successful reconnection
newSocket.addEventListener("open", () => {
reconnectAttempts = 0;
isReconnecting = false;
console.info("Reconnected successfully.");
});
// Handle errors on the new socket
newSocket.addEventListener("error", handleError);
reconnectTimeout = null;
}, delay);
};
const handleError = () => {
console.error("WebSocket encountered an error. Attempting to reconnect...");
reconnect();
};
ws.addEventListener("error", handleError);
const originalAddEventListener = ws.addEventListener.bind(ws);
ws.addEventListener = (type, listener, options) => {
if (customListeners[type]) {
customListeners[type].push(listener);
}
originalAddEventListener(type, listener, options);
};
const originalRemoveEventListener = ws.removeEventListener.bind(ws);
ws.removeEventListener = (type, listener, options) => {
if (customListeners[type]) {
customListeners[type] = customListeners[type].filter((l) => l !== listener);
}
originalRemoveEventListener(type, listener, options);
};
const originalClose = ws.close.bind(ws);
ws.close = (code, reason) => {
console.warn(`WebSocket closed: Code ${code}, Reason: ${reason || "No reason provided."}`);
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
reconnectTimeout = null;
}
originalClose(code, reason);
if (!isReconnecting) {
console.info("WebSocket closed manually.");
reconnect();
}
};
console.log("New WebSocket connection opened!");
// Add a method to disable reconnection
ws.disableReconnect = () => {
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
reconnectTimeout = null;
}
reconnectAttempts = maxReconnectAttempts; // Prevent further attempts
console.info("Reconnection disabled.");
};
return ws;
};
*/
@hadikhah
Copy link
Copy Markdown

How should I use this code? I tried running the code in the console while the game was loading, but it didn't work. After 4 and a half minutes, the game still closes the WS connection.

@maanimis
Copy link
Copy Markdown
Author

How should I use this code? I tried running the code in the console while the game was loading, but it didn't work. After 4 and a half minutes, the game still closes the WS connection.

Yes, unfortunately, the code has a bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment