Class ServerMonitorManager
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) static final classThe result of one main-thread sample. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate intprivate doubleprivate longA server-state snapshot sampled on the main thread; the async send thread only ever reads it.private final long[]private final long[]private final long[] -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprivate doublecalculateAverageTPS(long[] history, int count) Calculates the average of a history-of-TPS array.private doubleCalculates the realtime TPS (based on the most recent history entry).private double[]Calculates TPS -- returns the [1-minute, 5-minute, 15-minute] averages.Gets the current snapshot for the send thread to use.(package private) ServerMonitorManager.ServerStateSnapshotReads the published main-thread sample for the v2 telemetry encoders (AgentTelemetry).private StringextractVersionNumber(String fullVersion) Extract the version number.doubleGet current 1-minute average TPS.(package private) booleanLets tests assert whether sampling has actually happened yet -- more direct than observing the emitted JSON externally.private booleanThe single check point for the thread contract: logs SEVERE and returns false when not on the main thread.(package private) booleanStarts the TPS clock now without recording a sample (plan 22-22 fix): the clock otherwise starts when this object is created, and the v2 sampler first runs 20 ticks after the first connection, which may come long after the plugin enabled; that gap would be recorded as the first TPS sample.(package private) booleanSamples and publishes one state snapshot, so the v2 telemetry path and its tests can publish a fresh sample.private voidSample CPU usage and update the cached value.(package private) doubleSamples a server-state snapshot on the main thread.(package private) booleanRuns one TPS and CPU sample on the main thread.private voidUpdates the TPS calculation + CPU sampling -- runs once per second (20 ticks).
-
Field Details
-
lastTick
private long lastTick -
tpsHistory1m
private final long[] tpsHistory1m -
tpsHistory5m
private final long[] tpsHistory5m -
tpsHistory15m
private final long[] tpsHistory15m -
historyIndex
private int historyIndex -
lastCpuUsage
private volatile double lastCpuUsage -
stateSnapshot
A server-state snapshot sampled on the main thread; the async send thread only ever reads it.This is the entire point of issue #179. Before this field existed,
sendBatchUpdateran on a plainScheduledThreadPoolbut calledBukkit.getWorlds(),world.getLoadedChunks(),Bukkit.getOnlinePlayers(),player.getLocation()directly from there -- all mutable world state Paper explicitly does not support touching from an async thread, surfacing as intermittent concurrent-modification exceptions or torn reads.TPS/CPU sampling in this same class already correctly hopped over to
runTaskTimer, showing the contract was recognized at the time -- just only half applied. This field fills in the other half, following the exact same pattern.The cost is that data can be stale by up to one sampling period (5 seconds). This is a deliberate choice: the alternative would have the async thread synchronously wait on the main thread via
callSyncMethod().get(), which would make monitoring's liveness depend on the main thread's health -- precisely when the server is lagging is exactly when monitoring most needs to still be able to speak.
-
-
Constructor Details
-
ServerMonitorManager
public ServerMonitorManager()
-
-
Method Details
-
extractVersionNumber
Extract the version number. -
sampleCpuUsage
private void sampleCpuUsage()Sample CPU usage and update the cached value. -
sampleServerState
Samples a server-state snapshot on the main thread.May only be called on the Bukkit main thread. A call from a non-main thread is refused and logged as SEVERE -- this is a second, defensive gate: scheduling already guarantees the thread, but this class's own history is exactly "the contract was recognized, only half applied", and writing the contract into the code is more reliable than writing it into a comment.
- Returns:
- the new snapshot;
nullif not on the main thread
-
isOnPrimaryThreadOrComplain
private boolean isOnPrimaryThreadOrComplain()The single check point for the thread contract: logs SEVERE and returns false when not on the main thread.This class's own history is exactly "the contract was recognized, only half applied", so the contract is written as a runtime-observable signal rather than trusted to a comment -- on a real server, this line appearing in the log is proof that scheduling was broken.
-
sampleAndPublishState
boolean sampleAndPublishState()Samples and publishes one state snapshot, so the v2 telemetry path and its tests can publish a fresh sample.May only be called on the Bukkit main thread;
sampleServerState()refuses any other thread and logs it as SEVERE.- Returns:
truewhen a fresh snapshot was published,falseoff the main thread or when sampling failed
-
sampleTpsAndCpu
boolean sampleTpsAndCpu()Runs one TPS and CPU sample on the main thread.The v2 telemetry session (plan 22-22) schedules this once per second in place of the v1
tpsTask:getCurrentTPS()andsampledCpuPercent()read what it records.May only be called on the Bukkit main thread, like every other sample this class takes; any other thread is refused and logged as SEVERE.
- Returns:
truewhen the sample ran,falseoff the main thread
-
primeTpsClock
boolean primeTpsClock()Starts the TPS clock now without recording a sample (plan 22-22 fix): the clock otherwise starts when this object is created, and the v2 sampler first runs 20 ticks after the first connection, which may come long after the plugin enabled; that gap would be recorded as the first TPS sample. CPU is sampled once so the next sample has a value. Main thread only, likesampleTpsAndCpu().- Returns:
truewhen the clock was started,falseoff the main thread
-
currentStateSnapshot
ServerMonitorManager.ServerStateSnapshot currentStateSnapshot()Reads the published main-thread sample for the v2 telemetry encoders (AgentTelemetry). Package-private: internal cross-class reading, not a public API, and the v1 send paths keep usingcurrentSnapshot().Samples once here when nothing has been published yet and the caller is on the main thread, exactly like
currentSnapshot(); otherwise it returns the last publication, which may be up to one sampling period old.- Returns:
- the latest sampled state, or the empty snapshot before the first sample
-
sampledCpuPercent
double sampledCpuPercent()- Returns:
- the process CPU load percentage measured by the 1 Hz main-thread task, unclamped (the contract's encoder applies its own bounds)
-
hasSampledState
boolean hasSampledState()Lets tests assert whether sampling has actually happened yet -- more direct than observing the emitted JSON externally. -
currentSnapshot
Gets the current snapshot for the send thread to use.If sampling has never happened even once and the caller happens to be on the main thread, sample once right here -- otherwise the first frame after the connection is established would be all zeroes, and on-demand requests like
sendServerStatusWithRequestIdwould return empty data before monitoring has started. -
updateTpsAndCpu
private void updateTpsAndCpu()Updates the TPS calculation + CPU sampling -- runs once per second (20 ticks). -
getCurrentTPS
public double getCurrentTPS()Get current 1-minute average TPS.- Returns:
- the 1-minute average TPS
- Since:
- 6.2.3
-
calculateTPS
private double[] calculateTPS()Calculates TPS -- returns the [1-minute, 5-minute, 15-minute] averages. -
calculateAverageTPS
private double calculateAverageTPS(long[] history, int count) Calculates the average of a history-of-TPS array. -
calculateRealtimeTPS
private double calculateRealtimeTPS()Calculates the realtime TPS (based on the most recent history entry).
-