Performance HUD - Valhalla

Interactive cyber HUD: FPS, CPS/KPS, ping, latency, human check, keyboard heatmap, session stats, graphs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Performance HUD - Valhalla
// @namespace    http://tampermonkey.net/
// @version      4.0
// @description  Interactive cyber HUD: FPS, CPS/KPS, ping, latency, human check, keyboard heatmap, session stats, graphs
// @match        *://*/*
// @grant        none
// @run-at       document-end
// @license      All rights reserved. Copyright © 2026 Valhalla_Vikings
// You may not copy, distribute, or modify this script without permission.
// ==/UserScript==

(function () {
    'use strict';

    // =========================
    // STATE
    // =========================
    let fps = 0, frames = [], fpsHistory = [];
    let clickTimes = [], cps = 0, cpsHistory = [];
    let keyTimes = [], kps = 0;
    let clickIntervals = [];
    let totalClicks = 0, peakCPS = 0;

    let latency = 0;
    let clickLatency = 0;

    const MAX_FPS = 240;

    const keyHeat = {};

    // =========================
    // FPS TRACKER
    // =========================
    function frameLoop(now) {
        frames.push(now);
        if (frames.length > 30) frames.shift();

        if (frames.length > 1) {
            const duration = frames.at(-1) - frames[0];
            fps = Math.round((frames.length - 1) * 1000 / duration);
        }

        fpsHistory.push(fps);
        if (fpsHistory.length > 60) fpsHistory.shift();

        requestAnimationFrame(frameLoop);
    }
    requestAnimationFrame(frameLoop);

    // =========================
    // INPUT TRACKING
    // =========================
    document.addEventListener("pointerdown", () => {
        const now = performance.now();

        clickTimes.push(now);
        clickTimes = clickTimes.filter(t => now - t < 1000);
        cps = clickTimes.length;

        cpsHistory.push(cps);
        if (cpsHistory.length > 60) cpsHistory.shift();

        totalClicks++;
        if (cps > peakCPS) peakCPS = cps;

        if (clickTimes.length > 1) {
            const interval = clickTimes.at(-1) - clickTimes.at(-2);
            clickIntervals.push(interval);
            if (clickIntervals.length > 50) clickIntervals.shift();
        }

        requestAnimationFrame(() => {
            clickLatency = Math.round(performance.now() - now);
        });

        keyHeat['Mouse'] = 1.0;
    });

    document.addEventListener("keydown", (e) => {
        const now = performance.now();
        keyTimes.push(now);
        keyTimes = keyTimes.filter(t => now - t < 1000);
        kps = keyTimes.length;

        keyHeat[e.key.toUpperCase()] = 1.0;
    });

    document.addEventListener("keyup", (e) => { keyHeat[e.key.toUpperCase()] = 0; });

    // =========================
    // LATENCY
    // =========================
    async function measureLatency() {
        const start = performance.now();
        try {
            await fetch(location.origin, { method: "HEAD", cache: "no-store" });
            latency = Math.round(performance.now() - start);
        } catch { latency = -1; }
    }
    setInterval(measureLatency, 5000);

    // =========================
    // ANALYSIS
    // =========================
    function getHumanScore() {
        if (clickIntervals.length < 5) return "N/A";
        const avg = clickIntervals.reduce((a,b)=>a+b)/clickIntervals.length;
        const variance = clickIntervals.reduce((a,b)=>a+(b-avg)**2,0)/clickIntervals.length;
        const stdDev = Math.sqrt(variance);

        const hasOutliers = clickIntervals.some(i => Math.abs(i - avg) > avg * 0.5);

        if (stdDev < 5 && !hasOutliers) return "BOT";
        if (stdDev < 15) return "SUSPICIOUS";
        return "HUMAN";
    }

    function getAvgCPS() {
        if (!cpsHistory.length) return 0;
        return Math.round(cpsHistory.reduce((a,b)=>a+b)/cpsHistory.length);
    }

    function colorHuman(score) {
        return score==="HUMAN"?"#00ff9c":score==="SUSPICIOUS"?"#ffd166":"#ff4d6d";
    }

    function colorPing(p) {
        return p<80?"#00ff9c":p<150?"#ffd166":"#ff4d6d";
    }

    // =========================
    // UI
    // =========================
    function createUI() {
        if (document.getElementById("cyberHUD")) return;

        const ui = document.createElement("div");
        ui.id = "cyberHUD";

        const saved = JSON.parse(localStorage.getItem("cyberHUD")||"{}");
        ui.style.left = saved.x || "20px";
        ui.style.top = saved.y || "20px";

        ui.innerHTML = `
            <div id="hdr">⚡ CYBER HUD ▼</div>
            <div id="content">
                <div class="row"><span>FPS</span><span id="fps"></span></div>
                <canvas id="fpsGraph" width="240" height="20"></canvas>

                <div class="row"><span>CPS</span><span id="cps"></span></div>
                <canvas id="cpsGraph" width="240" height="20"></canvas>

                <div class="row"><span>KPS</span><span id="kps"></span></div>
                <div class="row"><span>Latency</span><span id="lat"></span></div>
                <div class="row"><span>Human</span><span id="human"></span></div>

                <hr>
                <div class="row"><span>Peak CPS</span><span id="peak"></span></div>
                <div class="row"><span>Avg CPS</span><span id="avg"></span></div>
                <div class="row"><span>Total Clicks</span><span id="total"></span></div>

                <div id="heatmap"></div>
            </div>
        `;

        document.body.appendChild(ui);

        const style = document.createElement("style");
        style.textContent = `
            #cyberHUD{position:fixed;width:240px;font-family:monospace;background:linear-gradient(135deg,#021a1a,#031f2e);border:1px solid #00ffc8;border-radius:12px;color:#d9fff8;box-shadow:0 0 20px #00ffc855,inset 0 0 10px #00ffc822;z-index:999999;}
            #hdr{padding:8px;text-align:center;font-weight:bold;background:linear-gradient(90deg,#00ff9c,#00cfff);color:#002;cursor:grab;}
            #content{padding:10px;font-size:12px;}
            .row{display:flex;justify-content:space-between;margin:4px 0;}
            canvas{margin-bottom:4px;}
            #heatmap{display:flex;gap:2px;margin-top:4px;flex-wrap:wrap;}
            .keyBlock{width:20px;height:20px;background:#004d40;border-radius:3px;text-align:center;font-size:10px;color:#d9fff8;line-height:20px;transition:background 0.2s;}
        `;
        document.head.appendChild(style);

        const header = ui.querySelector("#hdr");
        const content = ui.querySelector("#content");

        let collapsed = saved.collapsed ?? true;
        content.style.display = collapsed?"none":"block";

        header.onclick = () => {
            collapsed = !collapsed;
            content.style.display = collapsed ? "none" : "block";
            header.textContent = collapsed ? "⚡ CYBER HUD ▼" : "⚡ CYBER HUD ▲";
            localStorage.setItem("cyberHUD",JSON.stringify({x:ui.style.left,y:ui.style.top,collapsed}));
        };

        let dragging = false, ox = 0, oy = 0;
        header.addEventListener("mousedown", e => { dragging = true; ox = e.clientX - ui.offsetLeft; oy = e.clientY - ui.offsetTop; });
        document.addEventListener("mousemove", e => {
            if(!dragging) return;
            ui.style.left = (e.clientX - ox) + "px";
            ui.style.top = (e.clientY - oy) + "px";
            localStorage.setItem("cyberHUD",JSON.stringify({x:ui.style.left,y:ui.style.top,collapsed}));
        });
        document.addEventListener("mouseup", ()=> dragging=false);

        // Cache elements for updates
        const fpsEl = ui.querySelector("#fps");
        const cpsEl = ui.querySelector("#cps");
        const kpsEl = ui.querySelector("#kps");
        const latEl = ui.querySelector("#lat");
        const humanEl = ui.querySelector("#human");
        const peakEl = ui.querySelector("#peak");
        const avgEl = ui.querySelector("#avg");
        const totalEl = ui.querySelector("#total");
        const fpsCanvas = ui.querySelector("#fpsGraph");
        const cpsCanvas = ui.querySelector("#cpsGraph");
        const fpsCtx = fpsCanvas.getContext("2d");
        const cpsCtx = cpsCanvas.getContext("2d");

        // Heatmap keys
        const keysToShow = ["W","A","S","D","Mouse"];
        keysToShow.forEach(k => {
            if(!keyHeat[k]) keyHeat[k]=0;
            const div = document.createElement("div");
            div.id = "key_"+k;
            div.className="keyBlock";
            div.textContent=k;
            ui.querySelector("#heatmap").appendChild(div);
        });

        setInterval(()=>{
            fpsEl.textContent = fps;
            cpsEl.textContent = cps;
            kpsEl.textContent = kps;
            latEl.textContent = latency;
            humanEl.textContent = getHumanScore();
            peakEl.textContent = peakCPS;
            avgEl.textContent = getAvgCPS();
            totalEl.textContent = totalClicks;

            // FPS Graph
            fpsCtx.clearRect(0,0,fpsCanvas.width,fpsCanvas.height);
            fpsCtx.beginPath();
            fpsHistory.forEach((v,i)=>{
                const x = i*(fpsCanvas.width/fpsHistory.length);
                const y = fpsCanvas.height - (v/MAX_FPS)*fpsCanvas.height;
                i===0?fpsCtx.moveTo(x,y):fpsCtx.lineTo(x,y);
            });
            fpsCtx.strokeStyle="#00ffc8";
            fpsCtx.stroke();

            // CPS Graph
            cpsCtx.clearRect(0,0,cpsCanvas.width,cpsCanvas.height);
            cpsCtx.beginPath();
            cpsHistory.forEach((v,i)=>{
                const x = i*(cpsCanvas.width/cpsHistory.length);
                const y = cpsCanvas.height - Math.min(v/30,1)*cpsCanvas.height;
                i===0?cpsCtx.moveTo(x,y):cpsCtx.lineTo(x,y);
            });
            cpsCtx.strokeStyle="#ffd166";
            cpsCtx.stroke();

            // Heatmap
            keysToShow.forEach(k=>{
                if(keyHeat[k]>0) keyHeat[k]-=0.05;
                const div=document.getElementById("key_"+k);
                div.style.background=`rgba(0, 200, 255, ${0.3+keyHeat[k]*0.7})`;
            });
        }, 200);
    }

    const wait = setInterval(()=>{if(document.body){clearInterval(wait);createUI();}},100);

})();