9
0
mirror of https://github.com/Samsuik/Sakura.git synced 2025-12-27 02:39:06 +00:00
Files
SakuraMC/patches/server/0008-Track-Tick-Information.patch
Samsuik 9a7b139975 Updated Upstream (1.21.4 Paper)
Upstream has released updates that appear to apply and compile correctly

Paper Changes:
PaperMC/Paper@c0a3d51 Start update, apply API patches
PaperMC/Paper@172c7dc Work
PaperMC/Paper@ab9a3db More work
PaperMC/Paper@c60e47f More more work
PaperMC/Paper@bd55e32 More more more work
PaperMC/Paper@5265287 More more more more work
PaperMC/Paper@4601dc9 Some fixes, start updating CustomModelData API
PaperMC/Paper@2331dad Even more work
PaperMC/Paper@dc74c6f moonrise
PaperMC/Paper@d7d2f88 Apply remaining patches, fix API
PaperMC/Paper@f863bb7 Update generated classes
PaperMC/Paper@71a4ef8 Set java launcher for api generate task
PaperMC/Paper@b8aeecb Compilation fixes
PaperMC/Paper@6c35392 Tests succeed (by removing one)
PaperMC/Paper@b0603da Fix jd gson version, move back mc util diff
PaperMC/Paper@e2dd1d5 Add back post_teleport chunk ticket
PaperMC/Paper@7045b2a Update DataConverter
PaperMC/Paper@65633e3 Update Moonrise
2024-12-04 00:31:13 +00:00

657 lines
30 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Sun, 19 Sep 2021 01:10:02 +0100
Subject: [PATCH] Track Tick Information
Keeps track of useful information every tick such as the tick rate,
entities, chunks and displays them with the tps command.
diff --git a/src/main/java/me/samsuik/sakura/command/SakuraCommands.java b/src/main/java/me/samsuik/sakura/command/SakuraCommands.java
index cbb2e57f9ab3b48a6e5f792711c4c6fd2d34d445..8e93fc5d7e1bc200f79b0e54edb62dc4d0bf5e74 100644
--- a/src/main/java/me/samsuik/sakura/command/SakuraCommands.java
+++ b/src/main/java/me/samsuik/sakura/command/SakuraCommands.java
@@ -4,6 +4,7 @@ import me.samsuik.sakura.command.subcommands.ConfigCommand;
import me.samsuik.sakura.command.subcommands.FPSCommand;
import me.samsuik.sakura.command.subcommands.VisualCommand;
import me.samsuik.sakura.player.visibility.VisibilityTypes;
+import me.samsuik.sakura.command.subcommands.TPSCommand;
import net.minecraft.server.MinecraftServer;
import org.bukkit.command.Command;
@@ -18,6 +19,7 @@ public final class SakuraCommands {
COMMANDS.put("fps", new FPSCommand("fps"));
COMMANDS.put("tntvisibility", new VisualCommand(VisibilityTypes.TNT, "tnttoggle"));
COMMANDS.put("sandvisibility", new VisualCommand(VisibilityTypes.SAND, "sandtoggle"));
+ COMMANDS.put("tps", new TPSCommand("tps"));
}
public static void registerCommands(MinecraftServer server) {
diff --git a/src/main/java/me/samsuik/sakura/command/subcommands/TPSCommand.java b/src/main/java/me/samsuik/sakura/command/subcommands/TPSCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..db6d03c1ed1c1d0390826dd3f96e774e2bea8a1a
--- /dev/null
+++ b/src/main/java/me/samsuik/sakura/command/subcommands/TPSCommand.java
@@ -0,0 +1,92 @@
+package me.samsuik.sakura.command.subcommands;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import me.samsuik.sakura.command.BaseSubCommand;
+import me.samsuik.sakura.tps.ServerTickInformation;
+import me.samsuik.sakura.tps.graph.BuiltComponentCanvas;
+import me.samsuik.sakura.tps.graph.DetailedTPSGraph;
+import me.samsuik.sakura.tps.graph.GraphComponents;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.event.ClickEvent;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.Style;
+import net.kyori.adventure.text.format.TextDecoration;
+import net.minecraft.server.MinecraftServer;
+import org.bukkit.command.CommandSender;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+public final class TPSCommand extends BaseSubCommand {
+ private static final int GRAPH_WIDTH = 71;
+ private static final int GRAPH_HEIGHT = 10;
+ private static final Style GRAY_WITH_STRIKETHROUGH = Style.style(NamedTextColor.GRAY, TextDecoration.STRIKETHROUGH);
+
+ public TPSCommand(String name) {
+ super(name);
+ this.description = "Displays the current ticks per second";
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args) {
+ ServerTickInformation tickInformation = MinecraftServer.getServer().latestTickInformation();
+ long identifier = this.parseLong(args, 1).orElse(tickInformation.identifier());
+ double scale = this.parseDouble(args, 0).orElse(-1.0);
+ if (scale < 0.0) {
+ scale = this.dynamicScale(identifier);
+ }
+
+ ImmutableList<ServerTickInformation> tickHistory = MinecraftServer.getServer().tickHistory(identifier - GRAPH_WIDTH, identifier);
+ DetailedTPSGraph graph = new DetailedTPSGraph(GRAPH_WIDTH, GRAPH_HEIGHT, scale, tickHistory);
+ BuiltComponentCanvas canvas = graph.plot();
+ canvas.appendLeft(Component.text(":", NamedTextColor.BLACK));
+ canvas.appendRight(Component.text(":", NamedTextColor.BLACK));
+ canvas.header(this.createHeaderComponent(tickInformation, identifier));
+ canvas.footer(Component.text("*", NamedTextColor.DARK_GRAY)
+ .append(Component.text(Strings.repeat(" ", GRAPH_WIDTH - 1), GRAY_WITH_STRIKETHROUGH))
+ .append(Component.text("*")));
+
+ for (Component component : canvas.components()) {
+ sender.sendMessage(component);
+ }
+ }
+
+ private double dynamicScale(long identifier) {
+ ImmutableList<ServerTickInformation> tickHistory = MinecraftServer.getServer().tickHistory(identifier - 5, identifier);
+ double averageTps = tickHistory.stream()
+ .mapToDouble(ServerTickInformation::tps)
+ .average()
+ .orElse(0.0);
+ return 20 / averageTps;
+ }
+
+ private Component createHeaderComponent(ServerTickInformation tickInformation, long identifier) {
+ int scrollAmount = GRAPH_WIDTH / 3 * 2;
+ double memoryUsage = memoryUsage();
+ TextComponent.Builder builder = Component.text();
+ builder.color(NamedTextColor.DARK_GRAY);
+ builder.append(Component.text("< ")
+ .clickEvent(ClickEvent.runCommand("/tps -1 " + (identifier + scrollAmount))));
+ builder.append(Component.text(Strings.repeat(" ", 19), GRAY_WITH_STRIKETHROUGH));
+ builder.append(Component.text(" ( "));
+ builder.append(Component.text("Now: ", NamedTextColor.WHITE)
+ .append(Component.text("%.1f".formatted(tickInformation.tps()), tickInformation.colour())));
+ builder.appendSpace();
+ builder.append(Component.text("Mem: ", NamedTextColor.WHITE)
+ .append(Component.text("%.1f".formatted(memoryUsage * 100), GraphComponents.colour(1 - (float) memoryUsage))));
+ builder.append(Component.text("% ) "));
+ builder.append(Component.text(Strings.repeat(" ", 18), GRAY_WITH_STRIKETHROUGH));
+ builder.append(Component.text(" >")
+ .clickEvent(ClickEvent.runCommand("/tps -1 " + (identifier - scrollAmount))));
+ return builder.build();
+ }
+
+ private static double memoryUsage() {
+ Runtime runtime = Runtime.getRuntime();
+ double free = runtime.freeMemory();
+ double max = runtime.maxMemory();
+ double alloc = runtime.totalMemory();
+ return (alloc - free) / max;
+ }
+}
diff --git a/src/main/java/me/samsuik/sakura/tps/ServerTickInformation.java b/src/main/java/me/samsuik/sakura/tps/ServerTickInformation.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a65a3dac75834a2fe10d2c6d6ae594ceb208fef
--- /dev/null
+++ b/src/main/java/me/samsuik/sakura/tps/ServerTickInformation.java
@@ -0,0 +1,37 @@
+package me.samsuik.sakura.tps;
+
+import me.samsuik.sakura.configuration.GlobalConfiguration;
+import me.samsuik.sakura.tps.graph.GraphComponents;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.format.TextColor;
+import net.minecraft.server.MinecraftServer;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+public record ServerTickInformation(long identifier, double tps, double averageTick, long longestTick, float targetTickRate, int chunks, int entities) {
+ public static final ServerTickInformation FILLER = new ServerTickInformation(0, 0.0, 0.0, 0, 0.0f, 0, 0);
+
+ public TextColor colour() {
+ float lag = (float) this.tps / this.targetTickRate;
+ return GraphComponents.colour(lag);
+ }
+
+ public Component hoverComponent(TextColor colour) {
+ TextComponent.Builder builder = Component.text();
+ builder.append(Component.text("TPS: ")
+ .append(Component.text("%.1f".formatted(this.tps), colour)));
+ builder.appendNewline();
+ builder.append(Component.text("MSPT: ")
+ .append(Component.text("%.1f".formatted(this.averageTick), colour))
+ .append(Component.text("/"))
+ .append(Component.text(this.longestTick, colour)));
+ if (GlobalConfiguration.get().messages.tpsShowEntityAndChunkCount) {
+ builder.appendNewline();
+ builder.append(Component.text("Entities: " + this.entities));
+ builder.appendNewline();
+ builder.append(Component.text("Chunks: " + this.chunks));
+ }
+ return builder.build();
+ }
+}
diff --git a/src/main/java/me/samsuik/sakura/tps/TickInformationCollector.java b/src/main/java/me/samsuik/sakura/tps/TickInformationCollector.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f88e17db0cf8eca161d98cc8cdac8383903e694
--- /dev/null
+++ b/src/main/java/me/samsuik/sakura/tps/TickInformationCollector.java
@@ -0,0 +1,71 @@
+package me.samsuik.sakura.tps;
+
+import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import org.jspecify.annotations.NullMarked;
+
+import java.util.Collection;
+import java.util.List;
+
+@NullMarked
+public final class TickInformationCollector {
+ private static final int TEN_MINUTES = 10 * 60;
+ private final ObjectArrayList<ServerTickInformation> collectedInformation = new ObjectArrayList<>();
+ private final LongArrayList tickSamples = new LongArrayList();
+ private long identifier = 0;
+
+ public ServerTickInformation latestTickInformation() {
+ return this.collectedInformation.getLast();
+ }
+
+ public void levelData(Collection<ServerLevel> levels, double tps) {
+ int chunks = 0;
+ int entities = 0;
+ for (ServerLevel level : levels) {
+ chunks += level.chunkSource.getFullChunksCount();
+ entities += level.entityTickList.entities.size();
+ }
+
+ double averageTick = this.tickSamples.longStream()
+ .average()
+ .orElse(0.0);
+ long longestTick = this.tickSamples.longStream()
+ .max()
+ .orElse(0);
+ float targetTickRate = MinecraftServer.getServer().tickRateManager().tickrate();
+
+ ServerTickInformation tickInformation = new ServerTickInformation(
+ this.identifier++, tps, averageTick, longestTick, targetTickRate, chunks, entities
+ );
+
+ this.collectedInformation.add(tickInformation);
+ this.tickSamples.clear();
+
+ if (this.collectedInformation.size() > TEN_MINUTES) {
+ this.collectedInformation.subList(0, 60).clear();
+ }
+ }
+
+ public void tickDuration(long timeTaken) {
+ this.tickSamples.add(timeTaken);
+ }
+
+ public ImmutableList<ServerTickInformation> collect(long from, long to) {
+ List<ServerTickInformation> collected = new ObjectArrayList<>();
+ for (ServerTickInformation tickInformation : this.collectedInformation.reversed()) {
+ if (tickInformation.identifier() >= from && tickInformation.identifier() < to) {
+ collected.add(tickInformation);
+ }
+ }
+ long ahead = to - this.identifier;
+ long missing = to - from - collected.size();
+ for (int i = 0; i < missing; ++i) {
+ int ind = (i < ahead) ? 0 : collected.size();
+ collected.add(ind, ServerTickInformation.FILLER);
+ }
+ return ImmutableList.copyOf(collected);
+ }
+}
diff --git a/src/main/java/me/samsuik/sakura/tps/graph/BuiltComponentCanvas.java b/src/main/java/me/samsuik/sakura/tps/graph/BuiltComponentCanvas.java
new file mode 100644
index 0000000000000000000000000000000000000000..bf3e953c99825274b800d19c74019c904620ec74
--- /dev/null
+++ b/src/main/java/me/samsuik/sakura/tps/graph/BuiltComponentCanvas.java
@@ -0,0 +1,36 @@
+package me.samsuik.sakura.tps.graph;
+
+import com.google.common.collect.ImmutableList;
+import net.kyori.adventure.text.Component;
+import org.jspecify.annotations.NullMarked;
+
+import java.util.List;
+
+@NullMarked
+public final class BuiltComponentCanvas {
+ private final List<Component> components;
+
+ BuiltComponentCanvas(List<Component> components) {
+ this.components = components;
+ }
+
+ public void appendLeft(Component component) {
+ this.components.replaceAll(component::append);
+ }
+
+ public void appendRight(Component component) {
+ this.components.replaceAll(row -> row.append(component));
+ }
+
+ public void header(Component component) {
+ this.components.addFirst(component);
+ }
+
+ public void footer(Component component) {
+ this.components.add(component);
+ }
+
+ public ImmutableList<Component> components() {
+ return ImmutableList.copyOf(this.components);
+ }
+}
diff --git a/src/main/java/me/samsuik/sakura/tps/graph/ComponentCanvas.java b/src/main/java/me/samsuik/sakura/tps/graph/ComponentCanvas.java
new file mode 100644
index 0000000000000000000000000000000000000000..42024655f1b1cad12ba1db66086bb978c5ee8f02
--- /dev/null
+++ b/src/main/java/me/samsuik/sakura/tps/graph/ComponentCanvas.java
@@ -0,0 +1,63 @@
+package me.samsuik.sakura.tps.graph;
+
+import com.google.common.base.Preconditions;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.JoinConfiguration;
+import org.jspecify.annotations.NullMarked;
+
+import java.util.List;
+
+@NullMarked
+public final class ComponentCanvas {
+ private final int width;
+ private final int height;
+ private final Component[][] components;
+
+ public ComponentCanvas(int width, int height) {
+ this.width = width;
+ this.height = height;
+ // [x, y] is flipped as it makes converting the components into a list easier
+ this.components = new Component[height][width];
+ }
+
+ public void flip() {
+ for (int y = 0; y < this.height; ++y) {
+ if (y >= this.height / 2) {
+ Component[] row = this.components[y];
+ int relocatingRow = this.height - 1 - y;
+ this.components[y] = this.components[relocatingRow];
+ this.components[relocatingRow] = row;
+ }
+ }
+ }
+
+ public void fill(Component component) {
+ for (int x = 0; x < this.width; ++x) {
+ for (int y = 0; y < this.height; ++y) {
+ this.set(x, y, component);
+ }
+ }
+ }
+
+ public Component get(int x, int y) {
+ Component component = this.components[y][x];
+ return Preconditions.checkNotNull(component, "missing component at x:{} y:{}", x, y);
+ }
+
+ public void set(int x, int y, Component component) {
+ this.components[y][x] = component;
+ }
+
+ public BuiltComponentCanvas build() {
+ return new BuiltComponentCanvas(this.joinComponents());
+ }
+
+ private List<Component> joinComponents() {
+ List<Component> componentList = new ObjectArrayList<>(this.height);
+ for (Component[] row : this.components) {
+ componentList.add(Component.join(JoinConfiguration.noSeparators(), row));
+ }
+ return componentList;
+ }
+}
diff --git a/src/main/java/me/samsuik/sakura/tps/graph/DetailedTPSGraph.java b/src/main/java/me/samsuik/sakura/tps/graph/DetailedTPSGraph.java
new file mode 100644
index 0000000000000000000000000000000000000000..bebc61d33a5cbb6a46c7f5ac0a9b453e0ab48655
--- /dev/null
+++ b/src/main/java/me/samsuik/sakura/tps/graph/DetailedTPSGraph.java
@@ -0,0 +1,102 @@
+package me.samsuik.sakura.tps.graph;
+
+import me.samsuik.sakura.tps.ServerTickInformation;
+import org.jspecify.annotations.NullMarked;
+
+import java.util.List;
+
+@NullMarked
+public final class DetailedTPSGraph extends TPSGraph {
+ public DetailedTPSGraph(int width, int height, double scale, List<ServerTickInformation> tickInformation) {
+ super(width, height, scale, tickInformation);
+ }
+
+ @Override
+ public BuiltComponentCanvas plot() {
+ ComponentCanvas canvas = new ComponentCanvas(this.width, this.height);
+ canvas.fill(GraphComponents.BACKGROUND);
+
+ this.basicOutline(canvas);
+ this.prettifyOutline(canvas);
+ this.addColourAndHoverInformation(canvas);
+
+ canvas.flip();
+ return canvas.build();
+ }
+
+ private void basicOutline(ComponentCanvas canvas) {
+ for (int x = 0; x < this.width; ++x) {
+ int row = this.rowFromColumn(x);
+ int nextRow = this.rowFromColumn(x + 1);
+ int minRow = Math.min(row, nextRow);
+ int maxRow = Math.max(row, nextRow);
+
+ if (maxRow - minRow >= 2) {
+ canvas.set(x, minRow, GraphComponents.TOP_DOTTED_LINE);
+ canvas.set(x, maxRow, GraphComponents.BOTTOM_DOTTED_LINE);
+
+ for (int y = minRow + 1; y < maxRow; ++y) {
+ canvas.set(x, y, GraphComponents.VERTICAL_LINE);
+ }
+ } else {
+ canvas.set(x, row, GraphComponents.HORIZONTAL_LINE);
+ }
+ }
+ }
+
+ private void prettifyOutline(ComponentCanvas canvas) {
+ for (int x = 0; x < this.width; ++x) {
+ int row = this.rowFromColumn(x);
+ int nextRow = this.rowFromColumn(x + 1);
+ int prevRow = this.rowFromColumn(x - 1);
+ int minRow = Math.min(row, nextRow);
+ int maxRow = Math.max(row, nextRow);
+
+ if (maxRow - minRow >= 2) {
+ this.prettifyVerticalOutline(canvas, x, row, nextRow, prevRow, minRow, maxRow);
+ } else {
+ this.prettifySlopes(canvas, x, row, nextRow, prevRow);
+ }
+ }
+ }
+
+ private void prettifyVerticalOutline(ComponentCanvas canvas, int x, int row, int nextRow, int prevRow, int minRow, int maxRow) {
+ if (minRow == nextRow) {
+ canvas.set(x, minRow, GraphComponents.CONE_BOTTOM_LEFT);
+ } else if (prevRow <= minRow) {
+ canvas.set(x, minRow, GraphComponents.CONE_BOTTOM_RIGHT);
+ }
+ if (prevRow == row + 1 && nextRow < row) {
+ canvas.set(x, maxRow, GraphComponents.CONE_TOP_RIGHT);
+ }
+ if (maxRow == row && Math.abs(nextRow - maxRow) > 1 && Math.abs(prevRow - maxRow) > 1 && prevRow < maxRow) {
+ canvas.set(x - 1, maxRow, GraphComponents.CONE_TOP_LEFT);
+ canvas.set(x, maxRow, GraphComponents.CONE_TOP_RIGHT);
+ }
+ if (minRow == row && Math.abs(nextRow - minRow) > 1 && Math.abs(prevRow - minRow) > 1 && prevRow > minRow) {
+ canvas.set(x - 1, minRow, GraphComponents.CONE_BOTTOM_LEFT);
+ canvas.set(x, minRow, GraphComponents.CONE_BOTTOM_RIGHT);
+ }
+ }
+
+ private void prettifySlopes(ComponentCanvas canvas, int x, int row, int nextRow, int prevRow) {
+ int slopeDirection = nextRow - prevRow;
+ int slopeChange = Math.abs(slopeDirection);
+
+ if (slopeChange >= 2 && Math.max(nextRow, prevRow) == row + 1) {
+ canvas.set(x, row, slopeDirection < 0 ? GraphComponents.TL_TO_BR : GraphComponents.BL_TO_TR);
+ } else if (Math.abs(row - nextRow) == 1 || slopeDirection == 0) {
+ if (row < nextRow) {
+ canvas.set(x, row, GraphComponents.TOP_DOTTED_LINE);
+ } else if (row > nextRow) {
+ canvas.set(x, row, GraphComponents.BOTTOM_DOTTED_LINE);
+ }
+ } else if (Math.abs(row - prevRow) == 1) {
+ if (prevRow > row) {
+ canvas.set(x, row, GraphComponents.TOP_DOTTED_LINE);
+ } else if (prevRow < row) {
+ canvas.set(x, row, GraphComponents.BOTTOM_DOTTED_LINE);
+ }
+ }
+ }
+}
diff --git a/src/main/java/me/samsuik/sakura/tps/graph/GraphComponents.java b/src/main/java/me/samsuik/sakura/tps/graph/GraphComponents.java
new file mode 100644
index 0000000000000000000000000000000000000000..95d3c8031e4708d2d0a0a12213060acd9ab65522
--- /dev/null
+++ b/src/main/java/me/samsuik/sakura/tps/graph/GraphComponents.java
@@ -0,0 +1,42 @@
+package me.samsuik.sakura.tps.graph;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.Style;
+import net.kyori.adventure.text.format.TextColor;
+import net.kyori.adventure.text.format.TextDecoration;
+
+import java.util.List;
+
+public final class GraphComponents {
+ private static final Style STRIKE_THROUGH_STYLE = Style.style(TextDecoration.STRIKETHROUGH);
+ private static final Style REMOVE_STRIKE_THROUGH_STYLE = Style.style(TextDecoration.STRIKETHROUGH.withState(false));
+
+ public static final Component BACKGROUND = Component.text("::");
+ public static final Component HORIZONTAL_LINE = Component.text(" ", STRIKE_THROUGH_STYLE);
+ public static final Component VERTICAL_LINE = Component.text("||");
+ public static final Component TOP_DOTTED_LINE = Component.text("''");
+ public static final Component BOTTOM_DOTTED_LINE = Component.text("..");
+ public static final Component BL_TO_TR = Component.text(".", STRIKE_THROUGH_STYLE).append(Component.text("'", REMOVE_STRIKE_THROUGH_STYLE));
+ public static final Component TL_TO_BR = Component.text("'").append(Component.text(".", STRIKE_THROUGH_STYLE));
+ public static final Component CONE_TOP_LEFT = Component.text(".!");
+ public static final Component CONE_TOP_RIGHT = Component.text("!.");
+ public static final Component CONE_BOTTOM_LEFT = Component.text("'!");
+ public static final Component CONE_BOTTOM_RIGHT = Component.text("!'");
+
+ private static final List<TextColor> COLOURS = List.of(
+ NamedTextColor.GREEN, NamedTextColor.YELLOW, NamedTextColor.GOLD,
+ NamedTextColor.RED, NamedTextColor.DARK_GRAY, TextColor.color(40, 40, 40)
+ );
+
+ public static TextColor colour(float num) {
+ float segment = 1.0f / COLOURS.size();
+ float a = (1.0f - num) / segment;
+ float t = a % 1.0f;
+ int startIndex = Math.clamp((int) a, 0, COLOURS.size() - 2);
+ int endIndex = startIndex + 1;
+ TextColor startColour = COLOURS.get(startIndex);
+ TextColor endColour = COLOURS.get(endIndex);
+ return TextColor.lerp(t, startColour, endColour);
+ }
+}
diff --git a/src/main/java/me/samsuik/sakura/tps/graph/TPSGraph.java b/src/main/java/me/samsuik/sakura/tps/graph/TPSGraph.java
new file mode 100644
index 0000000000000000000000000000000000000000..27b6b071ad38589d37e35ea7fdf1d45924079f49
--- /dev/null
+++ b/src/main/java/me/samsuik/sakura/tps/graph/TPSGraph.java
@@ -0,0 +1,60 @@
+package me.samsuik.sakura.tps.graph;
+
+import com.google.common.base.Preconditions;
+import me.samsuik.sakura.tps.ServerTickInformation;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.event.HoverEvent;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.TextColor;
+import net.minecraft.util.Mth;
+import org.jspecify.annotations.NullMarked;
+
+import java.util.List;
+
+@NullMarked
+public abstract class TPSGraph {
+ protected final List<ServerTickInformation> tickInformation;
+ protected final int width;
+ protected final int height;
+ protected final double scale;
+
+ public TPSGraph(int width, int height, double scale, List<ServerTickInformation> tickInformation) {
+ Preconditions.checkArgument(tickInformation.size() == width);
+ this.width = width;
+ this.height = height;
+ this.scale = scale;
+ this.tickInformation = tickInformation;
+ }
+
+ public abstract BuiltComponentCanvas plot();
+
+ protected final int rowFromColumn(int x) {
+ int clamped = Math.clamp(x, 0, this.width - 1);
+ ServerTickInformation tickInformation = this.tickInformation.get(clamped);
+ return this.rowFromTPS(tickInformation.tps());
+ }
+
+ protected final int rowFromTPS(double tps) {
+ int row = Mth.floor((tps / 3) * this.scale);
+ return Mth.clamp(row, 0, this.height - 1);
+ }
+
+ protected final void addColourAndHoverInformation(ComponentCanvas canvas) {
+ for (int x = 0; x < this.width; ++x) {
+ ServerTickInformation tickInformation = this.tickInformation.get(x);
+ TextColor colourFromTPS = tickInformation.colour();
+ Component hoverComponent = tickInformation.hoverComponent(colourFromTPS);
+ HoverEvent<Component> hoverEvent = HoverEvent.showText(hoverComponent);
+
+ for (int y = 0; y < this.height; ++y) {
+ Component component = canvas.get(x, y);
+ if (component == GraphComponents.BACKGROUND) {
+ component = component.color(NamedTextColor.BLACK);
+ } else {
+ component = component.color(colourFromTPS);
+ }
+ canvas.set(x, y, component.hoverEvent(hoverEvent));
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 1d0462b970304f543ff949f7aa32c67a1860ba4f..733541e91b0064d50de6c5c0985e9c472685c20c 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -427,6 +427,17 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
// Paper end - rewrite chunk system
+ // Sakura start - track tick information
+ private final me.samsuik.sakura.tps.TickInformationCollector tickInformationCollector = new me.samsuik.sakura.tps.TickInformationCollector();
+
+ public final me.samsuik.sakura.tps.ServerTickInformation latestTickInformation() {
+ return this.tickInformationCollector.latestTickInformation();
+ }
+
+ public final ImmutableList<me.samsuik.sakura.tps.ServerTickInformation> tickHistory(long from, long to) {
+ return this.tickInformationCollector.collect(from, to);
+ }
+ // Sakura end - track tick information
public MinecraftServer(OptionSet options, WorldLoader.DataLoadContext worldLoader, Thread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, Proxy proxy, DataFixer datafixer, Services services, ChunkProgressListenerFactory worldloadlistenerfactory) {
super("Server");
@@ -1301,6 +1312,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (++MinecraftServer.currentTick % MinecraftServer.SAMPLE_INTERVAL == 0) {
final long diff = currentTime - tickSection;
final java.math.BigDecimal currentTps = TPS_BASE.divide(new java.math.BigDecimal(diff), 30, java.math.RoundingMode.HALF_UP);
+ this.tickInformationCollector.levelData(this.levels.values(), currentTps.doubleValue()); // Sakura - track tick information
tps1.add(currentTps, diff);
tps5.add(currentTps, diff);
tps15.add(currentTps, diff);
@@ -1343,6 +1355,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
throw new RuntimeException("Chunk system crash propagated to tick()", crash);
}
// Paper end - rewrite chunk system
+ this.tickInformationCollector.tickDuration((System.nanoTime() - currentTime) / 1_000_000L); // Sakura - track tick information
this.tickFrame.end();
gameprofilerfiller.popPush("nextTickWait");
this.mayHaveDelayedTasks = true;
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 0c1587b8ccf05e7a0a5df5a64c483cee434e1c0f..e1f0da0b20ec310470160718338920bbd5b088de 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -201,7 +201,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
private final MinecraftServer server;
public final PrimaryLevelData serverLevelData; // CraftBukkit - type
private int lastSpawnChunkRadius;
- final EntityTickList entityTickList = new EntityTickList();
+ public final EntityTickList entityTickList = new EntityTickList(); // Sakura - package-private -> public
// Paper - rewrite chunk system
private final GameEventDispatcher gameEventDispatcher;
public boolean noSave;
diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
index d8b4196adf955f8d414688dc451caac2d9c609d9..0859aecb141261638b8547fb8854768fb6b6f5c7 100644
--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
+++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
@@ -9,7 +9,7 @@ import javax.annotation.Nullable;
import net.minecraft.world.entity.Entity;
public class EntityTickList {
- private final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<net.minecraft.world.entity.Entity> entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Paper - rewrite chunk system
+ public final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<net.minecraft.world.entity.Entity> entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Sakura - track tick information; expose entity list // Paper - rewrite chunk system
private void ensureActiveIsNotIterated() {
// Paper - rewrite chunk system
diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java
index 4dbb109d0526afee99b9190fc256585121aac9b5..019ae7104a644f23495e42510a80573a7ac06a37 100644
--- a/src/main/java/org/spigotmc/SpigotConfig.java
+++ b/src/main/java/org/spigotmc/SpigotConfig.java
@@ -279,7 +279,7 @@ public class SpigotConfig
private static void tpsCommand()
{
- SpigotConfig.commands.put( "tps", new TicksPerSecondCommand( "tps" ) );
+ // SpigotConfig.commands.put( "tps", new TicksPerSecondCommand( "tps" ) ); // Sakura - TPS Graph
}
public static int playerSample;