mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-19 15:09:25 +00:00
Readd PWT
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,129 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Thu, 6 Mar 2025 00:26:19 +0100
|
||||
Subject: [PATCH] SparklyPaper: Track each world MSPT
|
||||
|
||||
Original project: https://github.com/SparklyPower/SparklyPaper
|
||||
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 7f791ff5bdf6c29c78f863d21af16270a74d8e7e..1431a0fac3c8a846535c1bd2f60a1279d08f14ea 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1699,7 +1699,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
// Leaf start - SparklyPaper - parallel world ticking mod (move level ticking logic out for branch convergence)
|
||||
private void tickLevel(ServerLevel serverLevel, BooleanSupplier hasTimeLeft) {
|
||||
try {
|
||||
+ long i = Util.getNanos(); // SparklyPaper - track world's MSPT
|
||||
serverLevel.tick(hasTimeLeft);
|
||||
+ // SparklyPaper start - track world's MSPT
|
||||
+ long j = Util.getNanos() - i;
|
||||
+
|
||||
+ // These are from the "tickServer" function
|
||||
+ serverLevel.tickTimes5s.add(this.tickCount, j);
|
||||
+ serverLevel.tickTimes10s.add(this.tickCount, j);
|
||||
+ serverLevel.tickTimes60s.add(this.tickCount, j);
|
||||
+ // SparklyPaper end - track world's MSPT
|
||||
} catch (Throwable levelTickingException) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(levelTickingException, "Exception ticking world");
|
||||
serverLevel.fillReportDetails(crashReport);
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index d6524d5c442555eaeb4d90f6a101262ee669f0d9..5c1f99a8fd9920b30e6a80769bc306591510012a 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -574,6 +574,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
// Paper end - chunk tick iteration
|
||||
|
||||
+ // SparklyPaper start - track world's MSPT
|
||||
+ public final MinecraftServer.TickTimes tickTimes5s = new MinecraftServer.TickTimes(100);
|
||||
+ public final MinecraftServer.TickTimes tickTimes10s = new MinecraftServer.TickTimes(200);
|
||||
+ public final MinecraftServer.TickTimes tickTimes60s = new MinecraftServer.TickTimes(1200);
|
||||
+ // SparklyPaper end - track world's MSPT
|
||||
+
|
||||
public ServerLevel(
|
||||
MinecraftServer server,
|
||||
Executor dispatcher,
|
||||
diff --git a/org/purpurmc/purpur/task/TPSBarTask.java b/org/purpurmc/purpur/task/TPSBarTask.java
|
||||
index 8769993e7ca59da309087051a3cd38fc562c15d1..b65c839e45402c618020c77fe63b89bd19cc7d96 100644
|
||||
--- a/org/purpurmc/purpur/task/TPSBarTask.java
|
||||
+++ b/org/purpurmc/purpur/task/TPSBarTask.java
|
||||
@@ -28,6 +28,44 @@ public class TPSBarTask extends BossBarTask {
|
||||
|
||||
@Override
|
||||
void updateBossBar(BossBar bossbar, Player player) {
|
||||
+ // SparklyPaper start - track world's MSPT
|
||||
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
||||
+ // Get player's current world
|
||||
+ net.minecraft.server.level.ServerLevel serverLevel = ((org.bukkit.craftbukkit.CraftWorld) player.getWorld()).getHandle();
|
||||
+
|
||||
+ // Calculate world-specific MSPT and TPS
|
||||
+ double worldMspt = calculateWorldMSPT(serverLevel);
|
||||
+ double worldTps = Math.min(20.0, 1000.0 / Math.max(worldMspt, 0.001)); // Avoid division by zero
|
||||
+
|
||||
+ // Store original values
|
||||
+ double originalTps = this.tps;
|
||||
+ double originalMspt = this.mspt;
|
||||
+
|
||||
+ try {
|
||||
+ // Temporarily set to world values
|
||||
+ this.tps = worldTps;
|
||||
+ this.mspt = worldMspt;
|
||||
+
|
||||
+ // Create MSPT component with world name
|
||||
+ Component msptWithWorld = Component.empty()
|
||||
+ .append(getMSPTColor())
|
||||
+ .append(Component.text(" [" + player.getWorld().getName() + "]").color(net.kyori.adventure.text.format.NamedTextColor.GRAY));
|
||||
+
|
||||
+ // Update the boss bar using the methods that depend on the fields
|
||||
+ bossbar.progress(getBossBarProgress());
|
||||
+ bossbar.color(getBossBarColor());
|
||||
+ bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandTPSBarTitle,
|
||||
+ Placeholder.component("tps", getTPSColor()),
|
||||
+ Placeholder.component("mspt", msptWithWorld),
|
||||
+ Placeholder.component("ping", getPingColor(player.getPing()))
|
||||
+ ));
|
||||
+ } finally {
|
||||
+ // Restore original values
|
||||
+ this.tps = originalTps;
|
||||
+ this.mspt = originalMspt;
|
||||
+ }
|
||||
+ } else {
|
||||
+ // Default behavior
|
||||
bossbar.progress(getBossBarProgress());
|
||||
bossbar.color(getBossBarColor());
|
||||
bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandTPSBarTitle,
|
||||
@@ -35,6 +73,8 @@ public class TPSBarTask extends BossBarTask {
|
||||
Placeholder.component("mspt", getMSPTColor()),
|
||||
Placeholder.component("ping", getPingColor(player.getPing()))
|
||||
));
|
||||
+ }
|
||||
+ // SparklyPaper end - track world's MSPT
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,6 +176,25 @@ public class TPSBarTask extends BossBarTask {
|
||||
return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%s", ping)));
|
||||
}
|
||||
|
||||
+ // SparklyPaper start - track world's MSPT
|
||||
+ private double calculateWorldMSPT(net.minecraft.server.level.ServerLevel serverLevel) {
|
||||
+ long[] times = serverLevel.tickTimes5s.getTimes();
|
||||
+ long total = 0L;
|
||||
+ int count = 0;
|
||||
+
|
||||
+ for (long value : times) {
|
||||
+ if (value > 0L) {
|
||||
+ total += value;
|
||||
+ count++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (count == 0) return 0.0;
|
||||
+
|
||||
+ return (double) total / (double) count * 1.0E-6D;
|
||||
+ }
|
||||
+ // SparklyPaper end - track world's MSPT
|
||||
+
|
||||
public enum FillMode {
|
||||
TPS, MSPT, PING
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,37 +0,0 @@
|
||||
package org.dreeam.leaf.async.world;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class PWTEventScheduler {
|
||||
|
||||
private static volatile PWTEventScheduler instance;
|
||||
private final ExecutorService executor;
|
||||
|
||||
private PWTEventScheduler() {
|
||||
this.executor = Executors.newCachedThreadPool(
|
||||
new ThreadFactoryBuilder()
|
||||
.setNameFormat("Leaf PWT Event Scheduler Thread - %d")
|
||||
.setDaemon(true)
|
||||
.setPriority(Thread.NORM_PRIORITY - 2)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public static PWTEventScheduler getScheduler() {
|
||||
if (instance == null) {
|
||||
synchronized (PWTEventScheduler.class) {
|
||||
if (instance == null) {
|
||||
instance = new PWTEventScheduler();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void scheduleTask(Runnable task) {
|
||||
this.executor.execute(task);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package org.dreeam.leaf.async.world;
|
||||
|
||||
public enum ReadOperationType {
|
||||
BLOCK_GET_BIOME,
|
||||
BLOCK_GET_COMPUTED_BIOME,
|
||||
BLOCK_IS_INDIRECTLY_POWERED,
|
||||
BLOCK_GET_BLOCK_POWER,
|
||||
BLOCK_RAY_TRACE,
|
||||
BLOCK_CAN_PLACE,
|
||||
BLOCK_GET_NMS_STATE
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.dreeam.leaf.async.world;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
public class SparklyPaperServerLevelTickExecutorThreadFactory implements ThreadFactory {
|
||||
|
||||
private final String worldName;
|
||||
|
||||
public SparklyPaperServerLevelTickExecutorThreadFactory(final String worldName) {
|
||||
this.worldName = worldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(@NotNull Runnable runnable) {
|
||||
TickThread.ServerLevelTickThread tickThread = new TickThread.ServerLevelTickThread(runnable, "Leaf World Ticking Thread - " + this.worldName);
|
||||
|
||||
if (tickThread.isDaemon()) {
|
||||
tickThread.setDaemon(false);
|
||||
}
|
||||
|
||||
if (tickThread.getPriority() != 5) {
|
||||
tickThread.setPriority(5);
|
||||
}
|
||||
|
||||
return tickThread;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package org.dreeam.leaf.async.world;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public record WorldReadRequest(
|
||||
ReadOperationType type,
|
||||
Object[] params, // Parameters for the read operation
|
||||
CompletableFuture<Object> future // Future to complete with the result
|
||||
) {
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
package org.dreeam.leaf.command.subcommands;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
import org.dreeam.leaf.command.LeafCommand;
|
||||
import org.dreeam.leaf.command.PermissionedLeafSubcommand;
|
||||
import org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.*;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class MSPTCommand extends PermissionedLeafSubcommand {
|
||||
|
||||
public static final String LITERAL_ARGUMENT = "mspt";
|
||||
public static final String PERM = LeafCommand.BASE_PERM + "." + LITERAL_ARGUMENT;
|
||||
private static final DecimalFormat DF = new DecimalFormat("########0.0");
|
||||
private static final Component SLASH = text("/");
|
||||
|
||||
public MSPTCommand() {
|
||||
super(PERM, PermissionDefault.TRUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
// Check if parallel world ticking is enabled
|
||||
if (!SparklyPaperParallelWorldTicking.enabled) {
|
||||
sender.sendMessage(Component.text()
|
||||
.content("Per-world MSPT tracking is only available when parallel world ticking is enabled.")
|
||||
.color(RED)
|
||||
.build());
|
||||
sender.sendMessage(Component.text()
|
||||
.content("Please enable it in your Leaf configuration to use this command.")
|
||||
.color(GRAY)
|
||||
.build());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if compact mode is requested
|
||||
boolean compactMode = args.length > 0 && args[0].equalsIgnoreCase("compact");
|
||||
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
|
||||
if (compactMode) {
|
||||
displayCompactStats(sender, server);
|
||||
} else {
|
||||
// Display header
|
||||
sender.sendMessage(Component.text()
|
||||
.content("━━━━━━━━━━━━━ ")
|
||||
.color(GOLD)
|
||||
.append(Component.text("MSPT Statistics").color(YELLOW))
|
||||
.append(Component.text(" ━━━━━━━━━━━━━").color(GOLD))
|
||||
.build());
|
||||
|
||||
// Overall server MSPT
|
||||
displayServerMSPT(sender, server);
|
||||
|
||||
// Add separator
|
||||
sender.sendMessage(Component.text(""));
|
||||
|
||||
// World-specific MSPT
|
||||
displayWorldMSPT(sender, server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void displayCompactStats(CommandSender sender, MinecraftServer server) {
|
||||
// Get server stats (only 5s data with avg/min/max)
|
||||
List<Component> serverTimes = eval(server.tickTimes5s.getTimes());
|
||||
|
||||
// Display server stats in compact form
|
||||
sender.sendMessage(Component.text()
|
||||
.content("Server: ")
|
||||
.color(GOLD)
|
||||
.append(serverTimes.get(0)).append(SLASH).append(serverTimes.get(1)).append(SLASH).append(serverTimes.get(2))
|
||||
.build());
|
||||
|
||||
// Display world stats in compact form
|
||||
for (net.minecraft.server.level.ServerLevel serverLevel : server.getAllLevels()) {
|
||||
List<Component> worldTimes = eval(serverLevel.tickTimes5s.getTimes());
|
||||
|
||||
sender.sendMessage(Component.text()
|
||||
.content(serverLevel.getWorld().getName() + ": ")
|
||||
.color(GOLD)
|
||||
.append(worldTimes.get(0)).append(SLASH).append(worldTimes.get(1)).append(SLASH).append(worldTimes.get(2))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
private void displayServerMSPT(CommandSender sender, MinecraftServer server) {
|
||||
List<Component> times = new ArrayList<>();
|
||||
times.addAll(eval(server.tickTimes5s.getTimes()));
|
||||
times.addAll(eval(server.tickTimes10s.getTimes()));
|
||||
times.addAll(eval(server.tickTimes60s.getTimes()));
|
||||
|
||||
sender.sendMessage(Component.text()
|
||||
.content("Server tick times ")
|
||||
.color(GOLD)
|
||||
.append(Component.text()
|
||||
.content("(avg/min/max)")
|
||||
.color(YELLOW)
|
||||
)
|
||||
.build());
|
||||
|
||||
sender.sendMessage(Component.text()
|
||||
.content(" 5s: ")
|
||||
.color(GOLD)
|
||||
.append(times.get(0)).append(SLASH).append(times.get(1)).append(SLASH).append(times.get(2))
|
||||
.build());
|
||||
|
||||
sender.sendMessage(Component.text()
|
||||
.content(" 10s: ")
|
||||
.color(GOLD)
|
||||
.append(times.get(3)).append(SLASH).append(times.get(4)).append(SLASH).append(times.get(5))
|
||||
.build());
|
||||
|
||||
sender.sendMessage(Component.text()
|
||||
.content(" 60s: ")
|
||||
.color(GOLD)
|
||||
.append(times.get(6)).append(SLASH).append(times.get(7)).append(SLASH).append(times.get(8))
|
||||
.build());
|
||||
}
|
||||
|
||||
private void displayWorldMSPT(CommandSender sender, MinecraftServer server) {
|
||||
sender.sendMessage(Component.text()
|
||||
.content("World-specific tick times ")
|
||||
.color(GOLD)
|
||||
.append(Component.text()
|
||||
.content("(avg/min/max)")
|
||||
.color(YELLOW)
|
||||
)
|
||||
.build());
|
||||
|
||||
for (net.minecraft.server.level.ServerLevel serverLevel : server.getAllLevels()) {
|
||||
List<Component> worldTimes = new ArrayList<>();
|
||||
worldTimes.addAll(eval(serverLevel.tickTimes5s.getTimes()));
|
||||
worldTimes.addAll(eval(serverLevel.tickTimes10s.getTimes()));
|
||||
worldTimes.addAll(eval(serverLevel.tickTimes60s.getTimes()));
|
||||
|
||||
// World name header
|
||||
sender.sendMessage(Component.text()
|
||||
.content("➤ ")
|
||||
.color(YELLOW)
|
||||
.append(Component.text(serverLevel.getWorld().getName()).color(GOLD))
|
||||
.build());
|
||||
|
||||
// Display time periods
|
||||
sender.sendMessage(Component.text()
|
||||
.content(" 5s: ")
|
||||
.color(GRAY)
|
||||
.append(worldTimes.get(0)).append(SLASH).append(worldTimes.get(1)).append(SLASH).append(worldTimes.get(2))
|
||||
.build());
|
||||
|
||||
sender.sendMessage(Component.text()
|
||||
.content(" 10s: ")
|
||||
.color(GRAY)
|
||||
.append(worldTimes.get(3)).append(SLASH).append(worldTimes.get(4)).append(SLASH).append(worldTimes.get(5))
|
||||
.build());
|
||||
|
||||
sender.sendMessage(Component.text()
|
||||
.content(" 60s: ")
|
||||
.color(GRAY)
|
||||
.append(worldTimes.get(6)).append(SLASH).append(worldTimes.get(7)).append(SLASH).append(worldTimes.get(8))
|
||||
.build());
|
||||
|
||||
boolean hasMoreWorlds = false;
|
||||
Iterable<net.minecraft.server.level.ServerLevel> levels = server.getAllLevels();
|
||||
for (net.minecraft.server.level.ServerLevel level : levels) {
|
||||
if (level != serverLevel) {
|
||||
hasMoreWorlds = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMoreWorlds) {
|
||||
sender.sendMessage(Component.text(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Component> eval(long[] times) {
|
||||
long min = Integer.MAX_VALUE;
|
||||
long max = 0L;
|
||||
long total = 0L;
|
||||
int count = 0;
|
||||
|
||||
for (long value : times) {
|
||||
if (value > 0L) {
|
||||
count++;
|
||||
if (value < min) min = value;
|
||||
if (value > max) max = value;
|
||||
total += value;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
// No data available yet
|
||||
return Arrays.asList(
|
||||
text("N/A", GRAY),
|
||||
text("N/A", GRAY),
|
||||
text("N/A", GRAY)
|
||||
);
|
||||
}
|
||||
|
||||
double avgD = ((double) total / (double) count) * 1.0E-6D;
|
||||
double minD = ((double) min) * 1.0E-6D;
|
||||
double maxD = ((double) max) * 1.0E-6D;
|
||||
|
||||
return Arrays.asList(getColoredValue(avgD), getColoredValue(minD), getColoredValue(maxD));
|
||||
}
|
||||
|
||||
private static Component getColoredValue(double value) {
|
||||
return text(DF.format(value) + "ms",
|
||||
value >= 50 ? RED :
|
||||
value >= 40 ? YELLOW :
|
||||
value >= 30 ? GOLD :
|
||||
value >= 20 ? GREEN :
|
||||
AQUA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
if (!SparklyPaperParallelWorldTicking.enabled) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (args.length == 1) {
|
||||
return Collections.singletonList("compact");
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user