Skip to content

nullstilles/Thread-Sight

Repository files navigation

ThreadSight

A lightweight, high-performance server performance monitoring plugin for Minecraft 1.21.1 (Paper / Folia). Collects and reports real-time server metrics — MSPT, entity count, and loaded chunk count — with negligible overhead and a clean architecture.


Features

  • Real-time MSPT tracking via a rolling average over the last 20 ticks
  • Entity count and loaded chunk count across all worlds
  • Per-world breakdown — entities and chunks per dimension
  • Metrics history — last 60 snapshots queryable in-game
  • Alert system — broadcasts warnings to ops when MSPT or entity count exceed configurable thresholds (60-second cooldown)
  • PlaceholderAPI support — expose metrics in scoreboards, tab lists, and chat
  • Folia support — auto-detects Folia at startup; uses GlobalRegionScheduler for safe scheduling and RegionScheduler per-chunk tasks for entity hotspot detection
  • bStats telemetry — anonymous MSPT and entity count brackets reported (opt-out in config)
  • Metrics sampled every 20 ticks (1 second) by default — configurable, no per-tick world scans
  • Thread-safe snapshot system using AtomicReference
  • Dependency-injected, interface-driven architecture

Requirements

Requirement Version
Minecraft 1.21.1
Server Software Paper 1.21.1 or Folia 1.21.1
Java 17 or higher (built with Java 21)

Spigot is not supported. Paper is required for API compatibility.


Installation

  1. Download Thread Sight-1.0-SNAPSHOT.jar from the releases page or build from source (see below).
  2. Place the jar in your server's plugins/ folder.
  3. Start or restart the server.
  4. Confirm the plugin loaded:
[Thread Sight] Enabled
[Thread Sight] Running metrics scheduler

On Folia the startup message reads Enabled (Folia mode). If PlaceholderAPI is present, Hooked into PlaceholderAPI will also appear.

No configuration is required. The plugin is ready to use immediately with sensible defaults.


Commands

Command Description Permission
/threadsight Global metrics — MSPT, entities, chunks none
/threadsight worlds Per-world entity and chunk breakdown none
/threadsight regions Top entity hotspots by chunk (Folia only) none
/threadsight history [n] Last n snapshots (default 10, max 60) none
/threadsight reload Reload config.yml without restart threadsight.reload (op)

All sub-commands support tab completion.

Example Output

[ThreadSight]
MSPT: 18.4
Entities: 245
Chunks: 812

MSPT is color-coded: green < 35ms · yellow 35–50ms · red > 50ms.

[ThreadSight] Per-world breakdown:
  world — Entities: 198, Chunks: 612
  world_nether — Entities: 32, Chunks: 120
  world_the_end — Entities: 15, Chunks: 80
[ThreadSight] Last 5 snapshots:
  14:02:01 | MSPT: 18.4 | Entities: 245 | Chunks: 812
  14:02:02 | MSPT: 19.1 | Entities: 247 | Chunks: 812
  ...

PlaceholderAPI

Placeholder Returns
%threadsight_mspt% Current MSPT (1 decimal place)
%threadsight_entities% Total entity count
%threadsight_chunks% Total loaded chunk count
%threadsight_mspt_status% Excellent / Good / Degraded / Poor

Requires PlaceholderAPI to be installed. ThreadSight registers automatically if PAPI is detected at startup.


Configuration

plugins/Thread Sight/config.yml is created on first run:

# How often (in ticks) to collect a metrics snapshot. Default: 20 (1 second)
sampling-interval-ticks: 20

alert-thresholds:
  # Broadcast to ops when MSPT exceeds this value. Set to 0 to disable.
  mspt: 50.0
  # Broadcast to ops when total entity count exceeds this value. Set to 0 to disable.
  entities: 2000

# Send anonymous usage stats to bStats.org
bstats-enabled: true

Alerts broadcast to all online players with the threadsight.alerts permission (default: op). Alerts for each metric have a 60-second cooldown to prevent spam.


Understanding the Metrics

MSPT (Milliseconds Per Tick)

The server targets 50ms per tick (20 TPS). ThreadSight measures the wall-clock time between ticks and computes a rolling average over the last 20 samples.

MSPT Server Health
< 35ms Excellent — headroom to spare
35–50ms Good — running smoothly
50–75ms Degraded — TPS starting to drop
> 75ms Poor — significant lag

Entity Count

High entity counts are one of the most common causes of server lag.

Count Status
< 500 Healthy for most servers
500–2000 Monitor closely
> 2000 Investigate entity farms or mob spawning

Use /threadsight regions on Folia to locate which chunk areas are driving the count.


Building from Source

Prerequisites

Steps

git clone <your-repo-url>
cd "Thread Sight"
gradlew.bat build        # Windows
./gradlew build          # Linux/Mac

The compiled jar will be at:

build/libs/Thread Sight-1.0-SNAPSHOT.jar

Running a Dev Server

gradlew.bat runServer    # Windows
./gradlew runServer      # Linux/Mac

The server runs in run/ with 2GB RAM. Accept the EULA on first run by setting eula=true in run/eula.txt.


Project Structure

src/main/java/dev/nullstilles/threadSight/
├── Main.java                            Entry point — wires all components
├── metrics/
│   ├── MetricsSnapshot.java             Immutable record (mspt, entities, chunks, timestamp, world/region breakdown)
│   ├── MetricsService.java              Collects raw data; owns history and optional Folia collector
│   ├── MetricsScheduler.java            Drives per-tick timing and per-second snapshot updates
│   ├── MetricsHistory.java              Ring buffer of last 60 snapshots
│   ├── WorldMetrics.java                Per-world entity and chunk record
│   ├── RegionMetrics.java               Per-chunk-region entity record (Folia only)
│   └── FoliaRegionCollector.java        Schedules RegionScheduler tasks; maintains hotspot map
├── scheduler/
│   ├── SchedulerAdapter.java            Interface abstracting the scheduler
│   ├── BukkitSchedulerAdapter.java      Bukkit implementation
│   └── FoliaSchedulerAdapter.java       Folia GlobalRegionScheduler implementation
├── config/
│   └── ThreadSightConfig.java           Loads and hot-reloads config.yml
├── alert/
│   └── AlertService.java                Threshold checks; broadcasts with 60s cooldown
├── placeholder/
│   └── ThreadSightPlaceholders.java     PlaceholderAPI expansion
└── command/
    └── ThreadSightCommand.java          Handles all /threadsight sub-commands

Architecture Notes

SchedulerAdapter

Folia has a different scheduler API (RegionScheduler, GlobalRegionScheduler). Routing all task scheduling through SchedulerAdapter means adding Folia support required only one new class with no changes to MetricsService or MetricsScheduler. Main.java detects Folia at runtime via Class.forName and selects the appropriate adapter.

Folia Region Collection

On Folia, FoliaRegionCollector samples one anchor chunk per 4×4 chunk tile. For each anchor, RegionScheduler.execute() runs a task on the thread that owns that chunk's region — the only thread allowed to safely read entity data there. Results accumulate in a ConcurrentHashMap; stale entries are pruned each collection cycle. The top 20 hotspots are included in every snapshot.

MSPT Measurement

A per-tick task records System.nanoTime() on each firing. The delta between consecutive calls gives tick duration in nanoseconds, converted to milliseconds. The last 20 values are stored in an ArrayDeque ring buffer.

Thread Safety

  • MetricsService.recordTick() and updateSnapshot() both run on the main/global-region thread.
  • MetricsSnapshot is an immutable Java record.
  • AtomicReference<MetricsSnapshot> allows safe reads from any thread (command handler, PAPI).
  • MetricsHistory synchronizes on this for buffer access.
  • FoliaRegionCollector.latest is a ConcurrentHashMap written by many region threads, read by the global thread.

Permissions

Permission Default Description
threadsight.reload op Reload config without restart
threadsight.alerts op Receive performance alert broadcasts

License

MIT — free to use, modify, and distribute.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages