From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Helvetica Volubi Date: Sun, 28 Jun 2026 19:47:24 +0800 Subject: [PATCH] Improved Server Health Report diff --git a/io/papermc/paper/threadedregions/commands/CommandServerHealth.java b/io/papermc/paper/threadedregions/commands/CommandServerHealth.java index aa860cfe9de616f185163fe74e0f4f49bd13a805..05d4f73340412f737224cb1443ecf1e33586ed38 100644 --- a/io/papermc/paper/threadedregions/commands/CommandServerHealth.java +++ b/io/papermc/paper/threadedregions/commands/CommandServerHealth.java @@ -29,6 +29,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; +// Luminol start - Improved Server Health Report +import java.util.concurrent.TimeUnit; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryUsage; +import java.lang.management.RuntimeMXBean; +import org.jetbrains.annotations.NotNull; +// Luminol end public final class CommandServerHealth extends Command { @@ -45,7 +53,7 @@ public final class CommandServerHealth extends Command { private static final TextColor HEADER = TextColor.color(79, 164, 240); private static final TextColor PRIMARY = TextColor.color(48, 145, 237); private static final TextColor SECONDARY = TextColor.color(104, 177, 240); - private static final TextColor INFORMATION = TextColor.color(145, 198, 243); + private static final TextColor INFORMATION = TextColor.color(180, 220, 255); // Luminol start - Improved Server Health Report private static final TextColor LIST = TextColor.color(33, 97, 188); public CommandServerHealth() { @@ -60,9 +68,11 @@ public final class CommandServerHealth extends Command { return Component.text() .append(Component.text(prefix, PRIMARY, TextDecoration.BOLD)) .append(Component.text(ONE_DECIMAL_PLACES.get().format(util * 100.0), CommandUtil.getUtilisationColourRegion(util))) - .append(Component.text("% util at ", PRIMARY)) + .append(Component.text("% util", PRIMARY)) + .append(Component.text(" | ", SECONDARY)) .append(Component.text(TWO_DECIMAL_PLACES.get().format(mspt), CommandUtil.getColourForMSPT(mspt))) - .append(Component.text(" MSPT at ", PRIMARY)) + .append(Component.text(" mspt", PRIMARY)) + .append(Component.text(" | ", SECONDARY)) .append(Component.text(TWO_DECIMAL_PLACES.get().format(tps), CommandUtil.getColourForTPS(tps))) .append(Component.text(" TPS" + (newline ? "\n" : ""), PRIMARY)) .build(); @@ -81,7 +91,7 @@ public final class CommandServerHealth extends Command { private static boolean executeRegion(final CommandSender sender, final String commandLabel, final String[] args) { final ThreadedRegionizer.ThreadedRegion region = - TickRegionScheduler.getCurrentRegion(); + TickRegionScheduler.getCurrentRegion(); if (region == null) { sender.sendMessage(Component.text("You are not in a region currently", NamedTextColor.RED)); return true; @@ -105,8 +115,7 @@ public final class CommandServerHealth extends Command { final double tps1m = report1m.tpsData().segmentAll().average(); final double mspt1m = report1m.timePerTickData().segmentAll().average() / 1.0E6; - final int yLoc = 80; - final String location = "[w:'" + world.getWorld().getName() + "'," + centerBlockX + "," + yLoc + "," + centerBlockZ + "]"; + final String location = world.getWorld().getName() + " (" + centerBlockX + ", " + centerBlockZ + ")"; final Component line = Component.text() .append(Component.text("Region around block ", PRIMARY)) @@ -144,7 +153,7 @@ public final class CommandServerHealth extends Command { } final List> regions = - new ArrayList<>(); + new ArrayList<>(); for (final World bukkitWorld : Bukkit.getWorlds()) { final ServerLevel world = ((CraftWorld)bukkitWorld).getHandle(); @@ -163,6 +172,18 @@ public final class CommandServerHealth extends Command { final long currTime = System.nanoTime(); final TickData.TickReportData globalTickReport = RegionizedServer.getGlobalTickData().getTickReport15s(currTime); + final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); + final MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); + final long usedMemory = heapUsage.getUsed() / (1024 * 1024); + final long maxMemory = heapUsage.getMax() / (1024 * 1024); + + final double memPercent = (double) usedMemory / maxMemory * 100.0; + final TextColor memColor = memPercent < 60 ? CommandUtil.getUtilisationColourRegion(0.0) : (memPercent < 85 ? NamedTextColor.YELLOW : NamedTextColor.RED); + + final RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); + final long uptime = runtimeBean.getUptime(); + final String uptimeStr = formatUptime(uptime); + for (final ThreadedRegionizer.ThreadedRegion region : regions) { final TickData.TickReportData report = region.getData().getRegionSchedulingHandle().getTickReport15s(currTime); tpsByRegion.add(report == null ? 20.0 : report.tpsData().segmentAll().average()); @@ -174,31 +195,27 @@ public final class CommandServerHealth extends Command { final double loadRate = ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkFullTask.loadRate(currTime); totalUtil += globalTickReport.utilisation(); + final TextColor utilisationColor = CommandUtil.getUtilisationColourRegion(totalUtil / (double)maxThreadCount); tpsByRegion.sort(null); if (!tpsByRegion.isEmpty()) { minTps = tpsByRegion.getDouble(0); maxTps = tpsByRegion.getDouble(tpsByRegion.size() - 1); - final int middle = tpsByRegion.size() >> 1; if ((tpsByRegion.size() & 1) == 0) { - // even, average the two middle points medianTps = (tpsByRegion.getDouble(middle - 1) + tpsByRegion.getDouble(middle)) / 2.0; } else { - // odd, can just grab middle medianTps = tpsByRegion.getDouble(middle); } } else { - // no regions = green minTps = medianTps = maxTps = 20.0; } final List, TickData.TickReportData>> - regionsBelowThreshold = new ArrayList<>(); + regionsBelowThreshold = new ArrayList<>(); for (int i = 0, len = regions.size(); i < len; ++i) { final TickData.TickReportData report = reportsByRegion.get(i); - regionsBelowThreshold.add(new ObjectObjectImmutablePair<>(regions.get(i), report)); } @@ -207,11 +224,18 @@ public final class CommandServerHealth extends Command { final TickData.TickReportData report2 = p2.right(); final double util1 = report1 == null ? 0.0 : report1.utilisation(); final double util2 = report2 == null ? 0.0 : report2.utilisation(); - - // we want the largest first return Double.compare(util2, util1); }); + long totalChunks = 0; + long totalEntities = 0; + + for (final ThreadedRegionizer.ThreadedRegion region : regions) { + final TickRegions.RegionStats stats = region.getData().getRegionStats(); + totalChunks += stats.getChunkCount(); + totalEntities += stats.getEntityCount(); + } + final TextComponent.Builder lowestRegionsBuilder = Component.text(); if (sender instanceof Player) { @@ -219,23 +243,18 @@ public final class CommandServerHealth extends Command { } for (int i = 0, len = Math.min(lowestRegionsCount, regionsBelowThreshold.size()); i < len; ++i) { final ObjectObjectImmutablePair, TickData.TickReportData> - pair = regionsBelowThreshold.get(i); + pair = regionsBelowThreshold.get(i); final TickData.TickReportData report = pair.right(); final ThreadedRegionizer.ThreadedRegion region = - pair.left(); + pair.left(); - if (report == null) { - // skip regions with no data - continue; - } + if (report == null) continue; final ServerLevel world = region.regioniser.world; final ChunkPos chunkCenter = region.getCenterChunk(); - if (chunkCenter == null) { - // region does not exist anymore - continue; - } + if (chunkCenter == null) continue; + final int centerBlockX = ((chunkCenter.x() << 4) | 7); final int centerBlockZ = ((chunkCenter.z() << 4) | 7); final double util = report.utilisation(); @@ -243,18 +262,21 @@ public final class CommandServerHealth extends Command { final double mspt = report.timePerTickData().segmentAll().average() / 1.0E6; final int yLoc = 80; - final String location = "[w:'" + world.getWorld().getName() + "'," + centerBlockX + "," + yLoc + "," + centerBlockZ + "]"; + final String location = world.getWorld().getName() + " (" + centerBlockX + ", " + centerBlockZ + ")"; + final Component line = Component.text() .append(Component.text(" - ", LIST, TextDecoration.BOLD)) - .append(Component.text("Region around block ", PRIMARY)) + .append(Component.text("Region at ", PRIMARY)) .append(Component.text(location, INFORMATION)) .append(Component.text(":\n", PRIMARY)) .append(Component.text(" ", PRIMARY)) .append(Component.text(ONE_DECIMAL_PLACES.get().format(util * 100.0), CommandUtil.getUtilisationColourRegion(util))) - .append(Component.text("% util at ", PRIMARY)) + .append(Component.text("% util", PRIMARY)) + .append(Component.text(" | ", SECONDARY)) .append(Component.text(TWO_DECIMAL_PLACES.get().format(mspt), CommandUtil.getColourForMSPT(mspt))) - .append(Component.text(" MSPT at ", PRIMARY)) + .append(Component.text(" mspt", PRIMARY)) + .append(Component.text(" | ", SECONDARY)) .append(Component.text(TWO_DECIMAL_PLACES.get().format(tps), CommandUtil.getColourForTPS(tps))) .append(Component.text(" TPS\n", PRIMARY)) @@ -270,29 +292,53 @@ public final class CommandServerHealth extends Command { sender.sendMessage( Component.text() - .append(Component.text("Server Health Report\n", HEADER, TextDecoration.BOLD)) + .append(Component.text("Server Health Report", HEADER, TextDecoration.BOLD)) + .append(Component.text(" (Uptime: ", SECONDARY)) + .append(Component.text(uptimeStr, utilisationColor)) + .append(Component.text(")\n", SECONDARY)) .append(Component.text(" - ", LIST, TextDecoration.BOLD)) .append(Component.text("Online Players: ", PRIMARY)) - .append(Component.text(Bukkit.getOnlinePlayers().size() + "\n", INFORMATION)) + .append(Component.text(Bukkit.getOnlinePlayers().size(), INFORMATION)) + .append(Component.newline()) .append(Component.text(" - ", LIST, TextDecoration.BOLD)) .append(Component.text("Total regions: ", PRIMARY)) - .append(Component.text(regions.size() + "\n", INFORMATION)) + .append(Component.text(regions.size(), INFORMATION)) + .append(Component.text(", ", PRIMARY)) + .append(Component.text("Total Chunks: ", PRIMARY)) + .append(Component.text(NO_DECIMAL_PLACES.get().format(totalChunks), INFORMATION)) + .append(Component.text(", ", PRIMARY)) + .append(Component.text("Total Entities: ", PRIMARY)) + .append(Component.text(NO_DECIMAL_PLACES.get().format(totalEntities) + "\n", INFORMATION)) .append(Component.text(" - ", LIST, TextDecoration.BOLD)) .append(Component.text("Utilisation: ", PRIMARY)) - .append(Component.text(ONE_DECIMAL_PLACES.get().format(totalUtil * 100.0), CommandUtil.getUtilisationColourRegion(totalUtil / (double)maxThreadCount))) - .append(Component.text("% / ", PRIMARY)) + .append(Component.text(ONE_DECIMAL_PLACES.get().format(totalUtil * 100.0), utilisationColor)) + .append(Component.text("%", PRIMARY)) + .append(Component.text(" / ", SECONDARY)) .append(Component.text(ONE_DECIMAL_PLACES.get().format(maxThreadCount * 100.0), INFORMATION)) .append(Component.text("%\n", PRIMARY)) .append(Component.text(" - ", LIST, TextDecoration.BOLD)) .append(Component.text("Load rate: ", PRIMARY)) - .append(Component.text(TWO_DECIMAL_PLACES.get().format(loadRate) + ", ", INFORMATION)) + .append(Component.text(TWO_DECIMAL_PLACES.get().format(loadRate), INFORMATION)) + .append(Component.text(", ", PRIMARY)) .append(Component.text("Gen rate: ", PRIMARY)) .append(Component.text(TWO_DECIMAL_PLACES.get().format(genRate) + "\n", INFORMATION)) + .append(Component.text(" - ", LIST, TextDecoration.BOLD)) + .append(Component.text("Memory: ", PRIMARY)) + .append(Component.text(NO_DECIMAL_PLACES.get().format(usedMemory), memColor)) + .append(Component.text(" MB", memColor)) + .append(Component.text(" / ", SECONDARY)) + .append(Component.text(NO_DECIMAL_PLACES.get().format(maxMemory), INFORMATION)) + .append(Component.text(" MB", INFORMATION)) + .append(Component.text(" (", SECONDARY)) + .append(Component.text(ONE_DECIMAL_PLACES.get().format(memPercent) + "%", memColor)) + .append(Component.text(")", SECONDARY)) + .append(Component.newline()) + .append(Component.text(" - ", LIST, TextDecoration.BOLD)) .append(Component.text("Lowest Region TPS: ", PRIMARY)) .append(Component.text(TWO_DECIMAL_PLACES.get().format(minTps) + "\n", CommandUtil.getColourForTPS(minTps))) @@ -361,4 +407,21 @@ public final class CommandServerHealth extends Command { } return new ArrayList<>(); } + + // Luminol start - Improved Server Health Report + private static @NotNull String formatUptime(long uptimeMillis) { + long days = TimeUnit.MILLISECONDS.toDays(uptimeMillis); + long hours = TimeUnit.MILLISECONDS.toHours(uptimeMillis) % 24; + long minutes = TimeUnit.MILLISECONDS.toMinutes(uptimeMillis) % 60; + long seconds = TimeUnit.MILLISECONDS.toSeconds(uptimeMillis) % 60; + + StringBuilder sb = new StringBuilder(); + if (days > 0) sb.append(days).append("d "); + if (hours > 0) sb.append(hours).append("h "); + if (minutes > 0) sb.append(minutes).append("m "); + sb.append(seconds).append("s"); + + return sb.toString(); + } + // Luminol end } \ No newline at end of file