Class ServerMonitorManager

java.lang.Object
com.ultikits.ultitools.manager.ServerMonitorManager

@Internal public class ServerMonitorManager extends Object
Server monitor manager. Responsible for collecting server status information and sending it via WebSocket.
  • 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

      private volatile ServerMonitorManager.ServerStateSnapshot 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, sendBatchUpdate ran on a plain ScheduledThreadPool but called Bukkit.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

      private String extractVersionNumber(String fullVersion)
      Extract the version number.
    • sampleCpuUsage

      private void sampleCpuUsage()
      Sample CPU usage and update the cached value.
    • sampleServerState

      private ServerMonitorManager.ServerStateSnapshot 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; null if 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:
      true when a fresh snapshot was published, false off 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() and sampledCpuPercent() 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:
      true when the sample ran, false off 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, like sampleTpsAndCpu().
      Returns:
      true when the clock was started, false off the main thread
    • 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 using currentSnapshot().

      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

      private ServerMonitorManager.ServerStateSnapshot 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 sendServerStatusWithRequestId would 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).