diff options
10 files changed, 260 insertions, 184 deletions
diff --git a/spark-api/src/main/java/me/lucko/spark/api/statistic/StatisticWindow.java b/spark-api/src/main/java/me/lucko/spark/api/statistic/StatisticWindow.java index cdf4d01..cdaa2b8 100644 --- a/spark-api/src/main/java/me/lucko/spark/api/statistic/StatisticWindow.java +++ b/spark-api/src/main/java/me/lucko/spark/api/statistic/StatisticWindow.java @@ -92,7 +92,8 @@ public interface StatisticWindow { enum MillisPerTick implements StatisticWindow { SECONDS_10(Duration.ofSeconds(10)), - MINUTES_1(Duration.ofMinutes(1)); + MINUTES_1(Duration.ofMinutes(1)), + MINUTES_5(Duration.ofMinutes(5)); private final Duration value; diff --git a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java index f609719..84f435a 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java +++ b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java @@ -45,6 +45,7 @@ import me.lucko.spark.common.monitor.memory.GarbageCollectorStatistics; import me.lucko.spark.common.monitor.net.NetworkMonitor; import me.lucko.spark.common.monitor.ping.PingStatistics; import me.lucko.spark.common.monitor.ping.PlayerPingProvider; +import me.lucko.spark.common.monitor.tick.SparkTickStatistics; import me.lucko.spark.common.monitor.tick.TickStatistics; import me.lucko.spark.common.platform.PlatformStatisticsProvider; import me.lucko.spark.common.sampler.BackgroundSamplerManager; @@ -153,9 +154,13 @@ public class SparkPlatform { this.samplerContainer = new SamplerContainer(); this.backgroundSamplerManager = new BackgroundSamplerManager(this, this.configuration); + TickStatistics tickStatistics = plugin.createTickStatistics(); this.tickHook = plugin.createTickHook(); this.tickReporter = plugin.createTickReporter(); - this.tickStatistics = this.tickHook != null || this.tickReporter != null ? new TickStatistics() : null; + if (tickStatistics == null && (this.tickHook != null || this.tickReporter != null)) { + tickStatistics = new SparkTickStatistics(); + } + this.tickStatistics = tickStatistics; PlayerPingProvider pingProvider = plugin.createPlayerPingProvider(); this.pingStatistics = pingProvider != null ? new PingStatistics(pingProvider) : null; @@ -168,12 +173,12 @@ public class SparkPlatform { throw new RuntimeException("Platform has already been enabled!"); } - if (this.tickHook != null) { - this.tickHook.addCallback(this.tickStatistics); + if (this.tickHook != null && this.tickStatistics instanceof SparkTickStatistics) { + this.tickHook.addCallback((TickHook.Callback) this.tickStatistics); this.tickHook.start(); } - if (this.tickReporter != null) { - this.tickReporter.addCallback(this.tickStatistics); + if (this.tickReporter != null&& this.tickStatistics instanceof SparkTickStatistics) { + this.tickReporter.addCallback((TickReporter.Callback) this.tickStatistics); this.tickReporter.start(); } if (this.pingStatistics != null) { diff --git a/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java b/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java index b7aef2a..a3bdceb 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java +++ b/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java @@ -23,6 +23,7 @@ package me.lucko.spark.common; import me.lucko.spark.api.Spark; import me.lucko.spark.common.command.sender.CommandSender; import me.lucko.spark.common.monitor.ping.PlayerPingProvider; +import me.lucko.spark.common.monitor.tick.TickStatistics; import me.lucko.spark.common.platform.MetadataProvider; import me.lucko.spark.common.platform.PlatformInfo; import me.lucko.spark.common.platform.serverconfig.ServerConfigProvider; @@ -128,6 +129,18 @@ public interface SparkPlugin { } /** + * Creates tick statistics for the platform, if supported. + * + * <p>Spark is able to provide a default implementation for platforms that + * provide a {@link TickHook} and {@link TickReporter}.</p> + * + * @return a new tick statistics instance + */ + default TickStatistics createTickStatistics() { + return null; + } + + /** * Creates a class source lookup function. * * @return the class source lookup function diff --git a/spark-common/src/main/java/me/lucko/spark/common/api/SparkApi.java b/spark-common/src/main/java/me/lucko/spark/common/api/SparkApi.java index 5b1ec2b..9e4eee4 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/api/SparkApi.java +++ b/spark-common/src/main/java/me/lucko/spark/common/api/SparkApi.java @@ -151,6 +151,8 @@ public class SparkApi implements Spark { return stats.duration10Sec(); case MINUTES_1: return stats.duration1Min(); + case MINUTES_5: + return stats.duration5Min(); default: throw new AssertionError(window); } diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/SparkTickStatistics.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/SparkTickStatistics.java new file mode 100644 index 0000000..5877cbe --- /dev/null +++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/SparkTickStatistics.java @@ -0,0 +1,197 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.common.monitor.tick; + +import me.lucko.spark.api.statistic.misc.DoubleAverageInfo; +import me.lucko.spark.common.tick.TickHook; +import me.lucko.spark.common.tick.TickReporter; +import me.lucko.spark.common.util.RollingAverage; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.concurrent.TimeUnit; + +/** + * Calculates the servers TPS (ticks per second) rate. + * + * <p>The code use to calculate the TPS is the same as the code used by the Minecraft server itself. + * This means that this class will output values the same as the /tps command.</p> + * + * <p>We calculate our own values instead of pulling them from the server for two reasons. Firstly, + * it's easier - pulling from the server requires reflection code on each of the platforms, we'd + * rather avoid that. Secondly, it allows us to generate rolling averages over a shorter period of + * time.</p> + */ +public class SparkTickStatistics implements TickHook.Callback, TickReporter.Callback, TickStatistics { + + private static final long SEC_IN_NANO = TimeUnit.SECONDS.toNanos(1); + private static final int TPS = 20; + private static final int TPS_SAMPLE_INTERVAL = 20; + private static final BigDecimal TPS_BASE = new BigDecimal(SEC_IN_NANO).multiply(new BigDecimal(TPS_SAMPLE_INTERVAL)); + + private final TpsRollingAverage tps5Sec = new TpsRollingAverage(5); + private final TpsRollingAverage tps10Sec = new TpsRollingAverage(10); + private final TpsRollingAverage tps1Min = new TpsRollingAverage(60); + private final TpsRollingAverage tps5Min = new TpsRollingAverage(60 * 5); + private final TpsRollingAverage tps15Min = new TpsRollingAverage(60 * 15); + private final TpsRollingAverage[] tpsAverages = {this.tps5Sec, this.tps10Sec, this.tps1Min, this.tps5Min, this.tps15Min}; + + private boolean durationSupported = false; + private final RollingAverage tickDuration10Sec = new RollingAverage(TPS * 10); + private final RollingAverage tickDuration1Min = new RollingAverage(TPS * 60); + private final RollingAverage tickDuration5Min = new RollingAverage(TPS * 60 * 5); + private final RollingAverage[] tickDurationAverages = {this.tickDuration10Sec, this.tickDuration1Min, this.tickDuration5Min}; + + private long last = 0; + + @Override + public boolean isDurationSupported() { + return this.durationSupported; + } + + @Override + public void onTick(int currentTick) { + if (currentTick % TPS_SAMPLE_INTERVAL != 0) { + return; + } + + long now = System.nanoTime(); + + if (this.last == 0) { + this.last = now; + return; + } + + long diff = now - this.last; + BigDecimal currentTps = TPS_BASE.divide(new BigDecimal(diff), 30, RoundingMode.HALF_UP); + BigDecimal total = currentTps.multiply(new BigDecimal(diff)); + + for (TpsRollingAverage rollingAverage : this.tpsAverages) { + rollingAverage.add(currentTps, diff, total); + } + + this.last = now; + } + + @Override + public void onTick(double duration) { + this.durationSupported = true; + BigDecimal decimal = new BigDecimal(duration); + for (RollingAverage rollingAverage : this.tickDurationAverages) { + rollingAverage.add(decimal); + } + } + + @Override + public double tps5Sec() { + return this.tps5Sec.getAverage(); + } + + @Override + public double tps10Sec() { + return this.tps10Sec.getAverage(); + } + + @Override + public double tps1Min() { + return this.tps1Min.getAverage(); + } + + @Override + public double tps5Min() { + return this.tps5Min.getAverage(); + } + + @Override + public double tps15Min() { + return this.tps15Min.getAverage(); + } + + @Override + public DoubleAverageInfo duration10Sec() { + if (!this.durationSupported) { + return null; + } + return this.tickDuration10Sec; + } + + @Override + public DoubleAverageInfo duration1Min() { + if (!this.durationSupported) { + return null; + } + return this.tickDuration1Min; + } + + @Override + public DoubleAverageInfo duration5Min() { + if (!this.durationSupported) { + return null; + } + return this.tickDuration5Min; + } + + + /** + * Rolling average calculator. + * + * <p>This code is taken from PaperMC/Paper, licensed under MIT.</p> + * + * @author aikar (PaperMC) https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0021-Further-improve-server-tick-loop.patch + */ + public static final class TpsRollingAverage { + private final int size; + private long time; + private BigDecimal total; + private int index = 0; + private final BigDecimal[] samples; + private final long[] times; + + TpsRollingAverage(int size) { + this.size = size; + this.time = size * SEC_IN_NANO; + this.total = new BigDecimal(TPS).multiply(new BigDecimal(SEC_IN_NANO)).multiply(new BigDecimal(size)); + this.samples = new BigDecimal[size]; + this.times = new long[size]; + for (int i = 0; i < size; i++) { + this.samples[i] = new BigDecimal(TPS); + this.times[i] = SEC_IN_NANO; + } + } + + public void add(BigDecimal x, long t, BigDecimal total) { + this.time -= this.times[this.index]; + this.total = this.total.subtract(this.samples[this.index].multiply(new BigDecimal(this.times[this.index]))); + this.samples[this.index] = x; + this.times[this.index] = t; + this.time += t; + this.total = this.total.add(total); + if (++this.index == this.size) { + this.index = 0; + } + } + + public double getAverage() { + return this.total.divide(new BigDecimal(this.time), 30, RoundingMode.HALF_UP).doubleValue(); + } + } + +} diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java index bd2b834..a48b178 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java +++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java @@ -20,168 +20,23 @@ package me.lucko.spark.common.monitor.tick; -import me.lucko.spark.common.tick.TickHook; -import me.lucko.spark.common.tick.TickReporter; -import me.lucko.spark.common.util.RollingAverage; - -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.concurrent.TimeUnit; +import me.lucko.spark.api.statistic.misc.DoubleAverageInfo; /** - * Calculates the servers TPS (ticks per second) rate. - * - * <p>The code use to calculate the TPS is the same as the code used by the Minecraft server itself. - * This means that this class will output values the same as the /tps command.</p> - * - * <p>We calculate our own values instead of pulling them from the server for two reasons. Firstly, - * it's easier - pulling from the server requires reflection code on each of the platforms, we'd - * rather avoid that. Secondly, it allows us to generate rolling averages over a shorter period of - * time.</p> + * Provides the server TPS (ticks per second) and MSPT (milliseconds per tick) rates. */ -public class TickStatistics implements TickHook.Callback, TickReporter.Callback { - - private static final long SEC_IN_NANO = TimeUnit.SECONDS.toNanos(1); - private static final int TPS = 20; - private static final int TPS_SAMPLE_INTERVAL = 20; - private static final BigDecimal TPS_BASE = new BigDecimal(SEC_IN_NANO).multiply(new BigDecimal(TPS_SAMPLE_INTERVAL)); - - private final TpsRollingAverage tps5Sec = new TpsRollingAverage(5); - private final TpsRollingAverage tps10Sec = new TpsRollingAverage(10); - private final TpsRollingAverage tps1Min = new TpsRollingAverage(60); - private final TpsRollingAverage tps5Min = new TpsRollingAverage(60 * 5); - private final TpsRollingAverage tps15Min = new TpsRollingAverage(60 * 15); - private final TpsRollingAverage[] tpsAverages = {this.tps5Sec, this.tps10Sec, this.tps1Min, this.tps5Min, this.tps15Min}; - - private boolean durationSupported = false; - private final RollingAverage tickDuration10Sec = new RollingAverage(TPS * 10); - private final RollingAverage tickDuration1Min = new RollingAverage(TPS * 60); - private final RollingAverage tickDuration5Min = new RollingAverage(TPS * 60 * 5); - private final RollingAverage[] tickDurationAverages = {this.tickDuration10Sec, this.tickDuration1Min, this.tickDuration5Min}; - - private long last = 0; - - public boolean isDurationSupported() { - return this.durationSupported; - } - - @Override - public void onTick(int currentTick) { - if (currentTick % TPS_SAMPLE_INTERVAL != 0) { - return; - } - - long now = System.nanoTime(); - - if (this.last == 0) { - this.last = now; - return; - } - - long diff = now - this.last; - BigDecimal currentTps = TPS_BASE.divide(new BigDecimal(diff), 30, RoundingMode.HALF_UP); - BigDecimal total = currentTps.multiply(new BigDecimal(diff)); - - for (TpsRollingAverage rollingAverage : this.tpsAverages) { - rollingAverage.add(currentTps, diff, total); - } - - this.last = now; - } - - @Override - public void onTick(double duration) { - this.durationSupported = true; - BigDecimal decimal = new BigDecimal(duration); - for (RollingAverage rollingAverage : this.tickDurationAverages) { - rollingAverage.add(decimal); - } - } - - public double tps5Sec() { - return this.tps5Sec.getAverage(); - } - - public double tps10Sec() { - return this.tps10Sec.getAverage(); - } - - public double tps1Min() { - return this.tps1Min.getAverage(); - } - - public double tps5Min() { - return this.tps5Min.getAverage(); - } - - public double tps15Min() { - return this.tps15Min.getAverage(); - } - - public RollingAverage duration10Sec() { - if (!this.durationSupported) { - return null; - } - return this.tickDuration10Sec; - } - - public RollingAverage duration1Min() { - if (!this.durationSupported) { - return null; - } - return this.tickDuration1Min; - } - - public RollingAverage duration5Min() { - if (!this.durationSupported) { - return null; - } - return this.tickDuration5Min; - } - - - /** - * Rolling average calculator. - * - * <p>This code is taken from PaperMC/Paper, licensed under MIT.</p> - * - * @author aikar (PaperMC) https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0021-Further-improve-server-tick-loop.patch - */ - public static final class TpsRollingAverage { - private final int size; - private long time; - private BigDecimal total; - private int index = 0; - private final BigDecimal[] samples; - private final long[] times; +public interface TickStatistics { - TpsRollingAverage(int size) { - this.size = size; - this.time = size * SEC_IN_NANO; - this.total = new BigDecimal(TPS).multiply(new BigDecimal(SEC_IN_NANO)).multiply(new BigDecimal(size)); - this.samples = new BigDecimal[size]; - this.times = new long[size]; - for (int i = 0; i < size; i++) { - this.samples[i] = new BigDecimal(TPS); - this.times[i] = SEC_IN_NANO; - } - } + double tps5Sec(); + double tps10Sec(); + double tps1Min(); + double tps5Min(); + double tps15Min(); - public void add(BigDecimal x, long t, BigDecimal total) { - this.time -= this.times[this.index]; - this.total = this.total.subtract(this.samples[this.index].multiply(new BigDecimal(this.times[this.index]))); - this.samples[this.index] = x; - this.times[this.index] = t; - this.time += t; - this.total = this.total.add(total); - if (++this.index == this.size) { - this.index = 0; - } - } + boolean isDurationSupported(); - public double getAverage() { - return this.total.divide(new BigDecimal(this.time), 30, RoundingMode.HALF_UP).doubleValue(); - } - } + DoubleAverageInfo duration10Sec(); + DoubleAverageInfo duration1Min(); + DoubleAverageInfo duration5Min(); } diff --git a/spark-common/src/main/java/me/lucko/spark/common/platform/PlatformStatisticsProvider.java b/spark-common/src/main/java/me/lucko/spark/common/platform/PlatformStatisticsProvider.java index 059590d..b0987c9 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/platform/PlatformStatisticsProvider.java +++ b/spark-common/src/main/java/me/lucko/spark/common/platform/PlatformStatisticsProvider.java @@ -20,6 +20,7 @@ package me.lucko.spark.common.platform; +import me.lucko.spark.api.statistic.misc.DoubleAverageInfo; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.monitor.cpu.CpuInfo; import me.lucko.spark.common.monitor.cpu.CpuMonitor; @@ -33,6 +34,7 @@ import me.lucko.spark.common.monitor.ping.PingStatistics; import me.lucko.spark.common.monitor.tick.TickStatistics; import me.lucko.spark.common.platform.world.AsyncWorldInfoProvider; import me.lucko.spark.common.platform.world.WorldStatisticsProvider; +import me.lucko.spark.proto.SparkProtos; import me.lucko.spark.proto.SparkProtos.PlatformStatistics; import me.lucko.spark.proto.SparkProtos.SystemStatistics; import me.lucko.spark.proto.SparkProtos.WorldStatistics; @@ -118,10 +120,10 @@ public class PlatformStatisticsProvider { networkInterfaceStats.forEach((name, statistics) -> builder.putNet( name, SystemStatistics.NetInterface.newBuilder() - .setRxBytesPerSecond(statistics.rxBytesPerSecond().toProto()) - .setRxPacketsPerSecond(statistics.rxPacketsPerSecond().toProto()) - .setTxBytesPerSecond(statistics.txBytesPerSecond().toProto()) - .setTxPacketsPerSecond(statistics.txPacketsPerSecond().toProto()) + .setRxBytesPerSecond(rollingAvgProto(statistics.rxBytesPerSecond())) + .setRxPacketsPerSecond(rollingAvgProto(statistics.rxPacketsPerSecond())) + .setTxBytesPerSecond(rollingAvgProto(statistics.txBytesPerSecond())) + .setTxPacketsPerSecond(rollingAvgProto(statistics.txPacketsPerSecond())) .build() )); @@ -166,8 +168,8 @@ public class PlatformStatisticsProvider { ); if (tickStatistics.isDurationSupported()) { builder.setMspt(PlatformStatistics.Mspt.newBuilder() - .setLast1M(tickStatistics.duration1Min().toProto()) - .setLast5M(tickStatistics.duration5Min().toProto()) + .setLast1M(rollingAvgProto(tickStatistics.duration1Min())) + .setLast5M(rollingAvgProto(tickStatistics.duration5Min())) .build() ); } @@ -176,7 +178,7 @@ public class PlatformStatisticsProvider { PingStatistics pingStatistics = this.platform.getPingStatistics(); if (pingStatistics != null && pingStatistics.getPingAverage().getSamples() != 0) { builder.setPing(PlatformStatistics.Ping.newBuilder() - .setLast15M(pingStatistics.getPingAverage().toProto()) + .setLast15M(rollingAvgProto(pingStatistics.getPingAverage())) .build() ); } @@ -204,4 +206,14 @@ public class PlatformStatisticsProvider { return builder.build(); } + public static SparkProtos.RollingAverageValues rollingAvgProto(DoubleAverageInfo info) { + return SparkProtos.RollingAverageValues.newBuilder() + .setMean(info.mean()) + .setMax(info.max()) + .setMin(info.min()) + .setMedian(info.median()) + .setPercentile95(info.percentile95th()) + .build(); + } + } diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/window/WindowStatisticsCollector.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/window/WindowStatisticsCollector.java index 7acbd6b..86c0b20 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/sampler/window/WindowStatisticsCollector.java +++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/window/WindowStatisticsCollector.java @@ -20,13 +20,13 @@ package me.lucko.spark.common.sampler.window; +import me.lucko.spark.api.statistic.misc.DoubleAverageInfo; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.monitor.cpu.CpuMonitor; import me.lucko.spark.common.monitor.tick.TickStatistics; import me.lucko.spark.common.platform.world.AsyncWorldInfoProvider; import me.lucko.spark.common.platform.world.WorldInfoProvider; import me.lucko.spark.common.tick.TickHook; -import me.lucko.spark.common.util.RollingAverage; import me.lucko.spark.proto.SparkProtos; import java.util.HashMap; @@ -165,7 +165,7 @@ public class WindowStatisticsCollector { if (tickStatistics != null) { builder.setTps(tickStatistics.tps1Min()); - RollingAverage mspt = tickStatistics.duration1Min(); + DoubleAverageInfo mspt = tickStatistics.duration1Min(); if (mspt != null) { builder.setMsptMedian(mspt.median()); builder.setMsptMax(mspt.max()); diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java b/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java index 65753bc..57dfdff 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java +++ b/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java @@ -21,7 +21,6 @@ package me.lucko.spark.common.util; import me.lucko.spark.api.statistic.misc.DoubleAverageInfo; -import me.lucko.spark.proto.SparkProtos; import java.math.BigDecimal; import java.math.RoundingMode; @@ -112,14 +111,4 @@ public class RollingAverage implements DoubleAverageInfo { return sortedSamples[rank].doubleValue(); } - public SparkProtos.RollingAverageValues toProto() { - return SparkProtos.RollingAverageValues.newBuilder() - .setMean(mean()) - .setMax(max()) - .setMin(min()) - .setMedian(median()) - .setPercentile95(percentile95th()) - .build(); - } - } diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/StatisticFormatter.java b/spark-common/src/main/java/me/lucko/spark/common/util/StatisticFormatter.java index 22ee9bb..b488f50 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/util/StatisticFormatter.java +++ b/spark-common/src/main/java/me/lucko/spark/common/util/StatisticFormatter.java @@ -22,6 +22,8 @@ package me.lucko.spark.common.util; import com.google.common.base.Strings; +import me.lucko.spark.api.statistic.misc.DoubleAverageInfo; + import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.TextColor; @@ -55,7 +57,7 @@ public enum StatisticFormatter { return text((tps > 20.0 ? "*" : "") + Math.min(Math.round(tps * 100.0) / 100.0, 20.0), color); } - public static TextComponent formatTickDurations(RollingAverage average) { + public static TextComponent formatTickDurations(DoubleAverageInfo average) { return text() .append(formatTickDuration(average.min())) .append(text('/', GRAY)) |