TT Session Stats

FPS, Ping, Time info

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

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