"use client"; import { useMemo, useState } from "react"; import { Stethoscope, Wifi } from "lucide-react"; import { api, useApi, toast } from "@/lib/api"; import { Card, Button, Badge, Tabs, EmptyState, Modal } from "@/components/ui"; import { TimeSeriesChart, Sparkline, type Point } from "@/components/charts"; import { fmtBytes } from "@/lib/format"; import { usePublicMode } from "@/lib/publicmode"; type MetricRow = { bucket: number; cpu: number | null; mem_used: number | null; mem_limit: number | null; net_rx: number | null; net_tx: number | null; tps: number | null; mspt: number | null; players: number | null; disk_used: number | null; disk_free: number | null; }; type LagData = { tps: number | null; mspt: number | null; cpu: number | null; serverHealthy: boolean; verdict: "no-data" | "server-lagging" | "player-connections" | "all-good"; players: Array<{ name: string; ping: number | null; verdict: string; history: Array<{ ts: number; ping: number }>; }>; }; const RANGES = [ { id: "1h", label: "1 hour" }, { id: "6h", label: "6 hours" }, { id: "24h", label: "24 hours" }, { id: "7d", label: "7 days" }, ]; const VERDICTS: Record = { "all-good": { badge: "good", title: "Everything looks healthy", hint: "The server is ticking at full speed and every player's connection is fine.", }, "server-lagging": { badge: "crit", title: "The server itself is lagging", hint: "Tick rate is below target — everyone experiences this equally. Check CPU/memory, heavy redstone/farms, or too many loaded chunks.", }, "player-connections": { badge: "warn", title: "Server is fine — some connections are not", hint: "The server is ticking normally. The players flagged below have high ping, so lag is on their network path, not your machine.", }, "no-data": { badge: "muted", title: "No data yet", hint: "Start the server and give the sampler a minute to collect TPS and ping data.", }, }; export default function PerformancePage() { const publicMode = usePublicMode(); const [range, setRange] = useState("1h"); const { data: rows } = useApi(`/api/metrics?range=${range}`, 30000); const { data: lag, refresh: refreshLag } = useApi("/api/lag", 30000); const [report, setReport] = useState(null); const [busyReport, setBusyReport] = useState(false); const pts = useMemo(() => { const make = (key: keyof MetricRow): Point[] => (rows ?? []).map((r) => ({ ts: r.bucket, v: r[key] as number | null })); // network counters are cumulative; convert to per-second rates const rates = (key: "net_rx" | "net_tx"): Point[] => { const out: Point[] = []; const data = rows ?? []; for (let i = 1; i < data.length; i++) { const dv = (data[i][key] ?? 0) - (data[i - 1][key] ?? 0); const dt = (data[i].bucket - data[i - 1].bucket) / 1000; out.push({ ts: data[i].bucket, v: dv >= 0 && dt > 0 ? dv / dt : null }); } return out; }; return { make, rates }; }, [rows]); const runHealthReport = async () => { setBusyReport(true); try { const d = await api<{ report: string }>("/api/lag/healthreport", { method: "POST" }); setReport(d.report); } catch (err) { toast((err as Error).message, "error"); } finally { setBusyReport(false); } }; const verdict = VERDICTS[lag?.verdict ?? "no-data"]; return (

Performance & Lag

Server tick health, resource usage, and who's actually lagging

{/* lag verdict */} Full health report ) } >
{verdict.title}

{verdict.hint}

{[ ["TPS", lag?.tps != null ? lag.tps.toFixed(1) : "—"], ["MSPT", lag?.mspt != null ? `${lag.mspt.toFixed(1)} ms` : "—"], ["CPU", lag?.cpu != null ? `${lag.cpu.toFixed(0)}%` : "—"], ].map(([label, value]) => (
{label}
{value}
))}
{/* per-player connections */}
Player connections (last hour)
{!lag || lag.players.length === 0 ? (
Nobody online right now.
) : (
{lag.players.map((p) => (
{p.name} {p.ping !== null ? `${p.ping} ms` : "—"}
({ ts: h.ts, v: h.ping }))} color={ p.verdict === "bad-connection" ? "var(--color-crit)" : p.verdict === "weak-connection" ? "var(--color-warn)" : "var(--color-series2)" } height={28} />
{p.verdict === "bad-connection" ? "Their connection is struggling — it's not the server." : p.verdict === "weak-connection" ? "Somewhat high ping — their network, not the server." : p.verdict === "good" ? "Healthy connection." : "No ping data yet."}
))}
)}
{/* charts — one measure per chart (never dual-axis) */} {!rows || rows.length < 2 ? ( ) : (
fmtBytes(v)} /> `${fmtBytes(v)}/s`} /> String(Math.round(v))} />
)} {/* spark health report modal */} setReport(null)} title="spark health report" wide>
          {report}
        
); }