FPS, Ping, Time info
// ==UserScript==
// @name TT Session Stats
// @namespace http://tampermonkey.net/
// @version 1.1
// @description FPS, Ping, Time info
// @author Aurelion-X9
// @match https://tanktrouble.com/*
// @match https://*.tanktrouble.com/*
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
'use strict';
let pings = [];
let start = Date.now();
let lastF = performance.now();
let fTimes = [];
let curPing = 0;
let lastServerId = null;
const div = document.createElement('div');
div.id = 'tt-stats';
div.innerHTML = '<span id="sf">FPS:--</span> <span id="sp">Ping:...</span> <span id="st">Time:0:00:00</span>';
div.style.cssText = 'position:fixed;bottom:8px;left:8px;z-index:2147483647;background:rgba(0,0,0,0.85);color:#fff;padding:6px 12px;border-radius:6px;font:12px monospace;display:flex;gap:12px;pointer-events:none;';
function init() {
if (document.body && !document.getElementById('tt-stats')) {
document.body.appendChild(div);
}
}
init();
setTimeout(init, 50);
setTimeout(init, 200);
setTimeout(init, 500);
setTimeout(init, 1000);
// Get current selected server ID
function getCurrentServerId() {
try {
if (typeof Cookies !== 'undefined') {
return Cookies.get('multiplayerserverid');
}
if (typeof ClientManager !== 'undefined' && ClientManager.getSelectedServer) {
return ClientManager.getSelectedServer();
}
} catch(e) {}
return null;
}
// Use TT's actual server ping measurement
function measurePing() {
try {
if (typeof ClientManager === 'undefined') return;
// Method 1: Get stats for all servers, use current one
if (ClientManager.getAvailableServerStats) {
const currentServer = getCurrentServerId();
ClientManager.getAvailableServerStats(function(success, serverId, latency, gameCount, playerCount, message) {
if (success && latency > 0) {
// If this is our current server, use it
if (currentServer && serverId === currentServer) {
pings.push(latency);
if (pings.length > 10) pings.shift();
curPing = median();
lastServerId = serverId;
} else if (!lastServerId) {
// Use first available server's ping if we don't have one
pings.push(latency);
if (pings.length > 10) pings.shift();
curPing = median();
}
}
});
}
// Method 2: Check if ClientManager has direct latency property
if (ClientManager._latency) {
pings.push(ClientManager._latency);
curPing = median();
}
if (ClientManager.latency) {
pings.push(ClientManager.latency);
curPing = median();
}
} catch(e) {}
}
// Also try to read ping from UI if available
function getPingFromUI() {
try {
// Check settings dropdown for ping text
const settingsSelect = document.querySelector('#settings select option:not([disabled])');
if (settingsSelect) {
const desc = settingsSelect.getAttribute('data-description');
if (desc) {
const match = desc.match(/(\d+)\s*ms/);
if (match) return parseInt(match[1]);
}
}
} catch(e) {}
return 0;
}
function median() {
if (!pings.length) return curPing;
const s = pings.slice().sort((a,b) => a-b);
return s[Math.floor(s.length / 2)];
}
function fps() {
const now = performance.now();
const d = now - lastF;
lastF = now;
if (d > 0 && d < 500) {
fTimes.push(d);
if (fTimes.length > 30) fTimes.shift();
}
requestAnimationFrame(fps);
}
function getFPS() {
try {
if (typeof GameManager !== 'undefined' && GameManager.getGame) {
const g = GameManager.getGame();
if (g && g.time && g.time.fps > 0) return Math.round(g.time.fps);
}
} catch(e) {}
if (!fTimes.length) return 0;
return Math.round(1000 / (fTimes.reduce((a,b) => a+b, 0) / fTimes.length));
}
function fmt(ms) {
const s = Math.floor(ms/1000);
return Math.floor(s/3600) + ':' + String(Math.floor((s%3600)/60)).padStart(2,'0') + ':' + String(s%60).padStart(2,'0');
}
// Wait for ClientManager to be ready
function waitForClientManager(callback) {
let attempts = 0;
const check = setInterval(function() {
attempts++;
if (typeof ClientManager !== 'undefined') {
clearInterval(check);
callback();
} else if (attempts > 50) {
clearInterval(check);
}
}, 200);
}
waitForClientManager(function() {
// Start tracking
fps();
// Measure ping
measurePing();
setInterval(measurePing, 2000);
// Also check UI periodically
setInterval(function() {
if (curPing === 0) {
const uiPing = getPingFromUI();
if (uiPing > 0) {
curPing = uiPing;
}
}
}, 1000);
// Update display
setInterval(function() {
const f = document.getElementById('sf');
const p = document.getElementById('sp');
const t = document.getElementById('st');
if (f) {
const v = getFPS();
f.textContent = 'FPS:' + (v || '--');
f.style.color = v > 100 ? '#4CAF50' : v > 60 ? '#FFC107' : '#f44336';
}
if (p) {
p.textContent = 'Ping:' + (curPing > 0 ? curPing + 'ms' : '...');
p.style.color = curPing > 0 && curPing < 80 ? '#4CAF50' : curPing > 0 && curPing < 150 ? '#FFC107' : '#f44336';
}
if (t) t.textContent = 'Time:' + fmt(Date.now() - start);
}, 100);
});
})();