TT Session Stats

FPS, Ping, Time info

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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);
    });
})();