mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-30 20:21:51 +01:00
f8fd607e04
Upstream has released updates that appears to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: bbfd13dd Hyperlink 'Events' in raid event package documentation b2095bed SPIGOT-5413: Add TrustedPlayer API for foxes 1bf1f3f4 Block trace methods do not require hash sets abf0cfdc Javadoc improvements per checkstyle c4a2b425 Add TimeSkipEvent CraftBukkit Changes:817116de
SPIGOT-5413: Add TrustedPlayer API for foxes062680a8
SPIGOT-5467: Calm down bees that cannot exit hive75fac431
SPIGOT-5472: Spurious warning when using clone command on tile entities85106731
SPIGOT-5471: Allow empty title/author for books2d9db47f
Add TimeSkipEvent384225c2
Add thread name to TerminalConsoleWriterThread Spigot Changes: 05bb8bcf Postpone stopping the watchdog until the server is completely stopped 18e2b9be Add package-info.java for Spigot APIs
332 lines
13 KiB
Diff
332 lines
13 KiB
Diff
From 0d6bd6ef77c14a6a7d648dc84ac43d675e46973e Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
Date: Fri, 19 Jul 2019 03:29:14 -0700
|
|
Subject: [PATCH] Reduce sync loads
|
|
|
|
This reduces calls to getChunkAt which would load chunks.
|
|
|
|
This patch also adds a tool to find calls which are doing this, however
|
|
it must be enabled by setting the startup flag -Dpaper.debug-sync-loads=true
|
|
|
|
To get a debug log for sync loads, the command is /paper syncloadinfo
|
|
|
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
|
index 09efbf725..132397b3f 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
|
@@ -1,9 +1,13 @@
|
|
package com.destroystokyo.paper;
|
|
|
|
+import com.destroystokyo.paper.io.SyncLoadFinder;
|
|
import com.google.common.base.Functions;
|
|
import com.google.common.collect.Iterables;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Maps;
|
|
+import com.google.gson.JsonObject;
|
|
+import com.google.gson.internal.Streams;
|
|
+import com.google.gson.stream.JsonWriter;
|
|
import net.minecraft.server.*;
|
|
import org.apache.commons.lang3.tuple.MutablePair;
|
|
import org.apache.commons.lang3.tuple.Pair;
|
|
@@ -18,6 +22,9 @@ import org.bukkit.craftbukkit.CraftWorld;
|
|
import org.bukkit.entity.Player;
|
|
|
|
import java.io.File;
|
|
+import java.io.FileOutputStream;
|
|
+import java.io.PrintStream;
|
|
+import java.io.StringWriter;
|
|
import java.time.LocalDateTime;
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.util.*;
|
|
@@ -130,6 +137,9 @@ public class PaperCommand extends Command {
|
|
case "chunkinfo":
|
|
doChunkInfo(sender, args);
|
|
break;
|
|
+ case "syncloadinfo":
|
|
+ this.doSyncLoadInfo(sender, args);
|
|
+ break;
|
|
case "ver":
|
|
case "version":
|
|
Command ver = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version");
|
|
@@ -146,6 +156,40 @@ public class PaperCommand extends Command {
|
|
return true;
|
|
}
|
|
|
|
+ private void doSyncLoadInfo(CommandSender sender, String[] args) {
|
|
+ if (!SyncLoadFinder.ENABLED) {
|
|
+ sender.sendMessage(ChatColor.RED + "This command requires the server startup flag '-Dpaper.debug-sync-loads=true' to be set.");
|
|
+ return;
|
|
+ }
|
|
+ File file = new File(new File(new File("."), "debug"),
|
|
+ "sync-load-info" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + ".txt");
|
|
+ file.getParentFile().mkdirs();
|
|
+ sender.sendMessage(ChatColor.GREEN + "Writing sync load info to " + file.toString());
|
|
+
|
|
+
|
|
+ try {
|
|
+ final JsonObject data = SyncLoadFinder.serialize();
|
|
+
|
|
+ StringWriter stringWriter = new StringWriter();
|
|
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
|
+ jsonWriter.setIndent(" ");
|
|
+ jsonWriter.setLenient(false);
|
|
+ Streams.write(data, jsonWriter);
|
|
+
|
|
+ String fileData = stringWriter.toString();
|
|
+
|
|
+ try (
|
|
+ PrintStream out = new PrintStream(new FileOutputStream(file), false, "UTF-8")
|
|
+ ) {
|
|
+ out.print(fileData);
|
|
+ }
|
|
+ sender.sendMessage(ChatColor.GREEN + "Successfully written sync load information!");
|
|
+ } catch (Throwable thr) {
|
|
+ sender.sendMessage(ChatColor.RED + "Failed to write sync load information");
|
|
+ thr.printStackTrace();
|
|
+ }
|
|
+ }
|
|
+
|
|
private void doChunkInfo(CommandSender sender, String[] args) {
|
|
List<org.bukkit.World> worlds;
|
|
if (args.length < 2 || args[1].equals("*")) {
|
|
diff --git a/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
|
|
new file mode 100644
|
|
index 000000000..59aec1032
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
|
|
@@ -0,0 +1,172 @@
|
|
+package com.destroystokyo.paper.io;
|
|
+
|
|
+import com.google.gson.JsonArray;
|
|
+import com.google.gson.JsonObject;
|
|
+import com.mojang.datafixers.util.Pair;
|
|
+import it.unimi.dsi.fastutil.longs.Long2IntMap;
|
|
+import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
|
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
|
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
+import net.minecraft.server.World;
|
|
+
|
|
+import java.util.ArrayList;
|
|
+import java.util.List;
|
|
+import java.util.Map;
|
|
+import java.util.WeakHashMap;
|
|
+
|
|
+public class SyncLoadFinder {
|
|
+
|
|
+ public static final boolean ENABLED = Boolean.getBoolean("paper.debug-sync-loads");
|
|
+
|
|
+ private static final WeakHashMap<World, Object2ObjectOpenHashMap<ThrowableWithEquals, SyncLoadInformation>> SYNC_LOADS = new WeakHashMap<>();
|
|
+
|
|
+ private static final class SyncLoadInformation {
|
|
+
|
|
+ public int times;
|
|
+
|
|
+ public final Long2IntOpenHashMap coordinateTimes = new Long2IntOpenHashMap();
|
|
+ }
|
|
+
|
|
+ public static void logSyncLoad(final World world, final int chunkX, final int chunkZ) {
|
|
+ if (!ENABLED) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ final ThrowableWithEquals stacktrace = new ThrowableWithEquals(Thread.currentThread().getStackTrace());
|
|
+
|
|
+ SYNC_LOADS.compute(world, (final World keyInMap, Object2ObjectOpenHashMap<ThrowableWithEquals, SyncLoadInformation> map) -> {
|
|
+ if (map == null) {
|
|
+ map = new Object2ObjectOpenHashMap<>();
|
|
+ }
|
|
+
|
|
+ map.compute(stacktrace, (ThrowableWithEquals keyInMap0, SyncLoadInformation valueInMap) -> {
|
|
+ if (valueInMap == null) {
|
|
+ valueInMap = new SyncLoadInformation();
|
|
+ }
|
|
+
|
|
+ ++valueInMap.times;
|
|
+
|
|
+ valueInMap.coordinateTimes.compute(IOUtil.getCoordinateKey(chunkX, chunkZ), (Long keyInMap1, Integer valueInMap1) -> {
|
|
+ return valueInMap1 == null ? Integer.valueOf(1) : Integer.valueOf(valueInMap1.intValue() + 1);
|
|
+ });
|
|
+
|
|
+ return valueInMap;
|
|
+ });
|
|
+
|
|
+ return map;
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public static JsonObject serialize() {
|
|
+ final JsonObject ret = new JsonObject();
|
|
+
|
|
+ final JsonArray worldsData = new JsonArray();
|
|
+
|
|
+ for (final Map.Entry<World, Object2ObjectOpenHashMap<ThrowableWithEquals, SyncLoadInformation>> entry : SYNC_LOADS.entrySet()) {
|
|
+ final World world = entry.getKey();
|
|
+
|
|
+ final JsonObject worldData = new JsonObject();
|
|
+
|
|
+ worldData.addProperty("name", world.getWorld().getName());
|
|
+
|
|
+ final List<Pair<ThrowableWithEquals, SyncLoadInformation>> data = new ArrayList<>();
|
|
+
|
|
+ entry.getValue().forEach((ThrowableWithEquals stacktrace, SyncLoadInformation times) -> {
|
|
+ data.add(new Pair<>(stacktrace, times));
|
|
+ });
|
|
+
|
|
+ data.sort((Pair<ThrowableWithEquals, SyncLoadInformation> pair1, Pair<ThrowableWithEquals, SyncLoadInformation> pair2) -> {
|
|
+ return Integer.compare(pair2.getSecond().times, pair1.getSecond().times); // reverse order
|
|
+ });
|
|
+
|
|
+ final JsonArray stacktraces = new JsonArray();
|
|
+
|
|
+ for (Pair<ThrowableWithEquals, SyncLoadInformation> pair : data) {
|
|
+ final JsonObject stacktrace = new JsonObject();
|
|
+
|
|
+ stacktrace.addProperty("times", pair.getSecond().times);
|
|
+
|
|
+ final JsonArray traces = new JsonArray();
|
|
+
|
|
+ for (StackTraceElement element : pair.getFirst().stacktrace) {
|
|
+ traces.add(String.valueOf(element));
|
|
+ }
|
|
+
|
|
+ stacktrace.add("stacktrace", traces);
|
|
+
|
|
+ final JsonArray coordinates = new JsonArray();
|
|
+
|
|
+ for (Long2IntMap.Entry coordinate : pair.getSecond().coordinateTimes.long2IntEntrySet()) {
|
|
+ final long key = coordinate.getLongKey();
|
|
+ final int times = coordinate.getIntValue();
|
|
+ coordinates.add("(" + IOUtil.getCoordinateX(key) + "," + IOUtil.getCoordinateZ(key) + "): " + times);
|
|
+ }
|
|
+
|
|
+ stacktrace.add("coordinates", coordinates);
|
|
+
|
|
+ stacktraces.add(stacktrace);
|
|
+ }
|
|
+
|
|
+
|
|
+ worldData.add("stacktraces", stacktraces);
|
|
+ worldsData.add(worldData);
|
|
+ }
|
|
+
|
|
+ ret.add("worlds", worldsData);
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ static final class ThrowableWithEquals {
|
|
+
|
|
+ private final StackTraceElement[] stacktrace;
|
|
+ private final int hash;
|
|
+
|
|
+ public ThrowableWithEquals(final StackTraceElement[] stacktrace) {
|
|
+ this.stacktrace = stacktrace;
|
|
+ this.hash = ThrowableWithEquals.hash(stacktrace);
|
|
+ }
|
|
+
|
|
+ public static int hash(final StackTraceElement[] stacktrace) {
|
|
+ int hash = 0;
|
|
+
|
|
+ for (int i = 0; i < stacktrace.length; ++i) {
|
|
+ hash *= 31;
|
|
+ hash += stacktrace[i].hashCode();
|
|
+ }
|
|
+
|
|
+ return hash;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int hashCode() {
|
|
+ return this.hash;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean equals(final Object obj) {
|
|
+ if (obj == null || obj.getClass() != this.getClass()) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ final ThrowableWithEquals other = (ThrowableWithEquals)obj;
|
|
+ final StackTraceElement[] otherStackTrace = other.stacktrace;
|
|
+
|
|
+ if (this.stacktrace.length != otherStackTrace.length) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (this == obj) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ for (int i = 0; i < this.stacktrace.length; ++i) {
|
|
+ if (!this.stacktrace[i].equals(otherStackTrace[i])) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
index 1f6b1c4f1..58654f453 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -280,6 +280,7 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
this.world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY);
|
|
com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.world, x, z);
|
|
// Paper end
|
|
+ com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.world, x, z); // Paper - sync load info
|
|
this.world.timings.chunkAwait.startTiming(); // Paper
|
|
this.serverThreadQueue.awaitTasks(completablefuture::isDone);
|
|
com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug
|
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
|
index 37604d8cc..5460ace8f 100644
|
|
--- a/src/main/java/net/minecraft/server/World.java
|
|
+++ b/src/main/java/net/minecraft/server/World.java
|
|
@@ -1197,7 +1197,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
|
|
|
|
for (int i1 = i; i1 <= j; ++i1) {
|
|
for (int j1 = k; j1 <= l; ++j1) {
|
|
- Chunk chunk = this.getChunkProvider().getChunkAt(i1, j1, false);
|
|
+ Chunk chunk = (Chunk)this.getChunkIfLoadedImmediately(i1, j1); // Paper
|
|
|
|
if (chunk != null) {
|
|
chunk.a(entity, axisalignedbb, list, predicate);
|
|
@@ -1217,7 +1217,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
|
|
|
|
for (int i1 = i; i1 < j; ++i1) {
|
|
for (int j1 = k; j1 < l; ++j1) {
|
|
- Chunk chunk = this.getChunkProvider().getChunkAt(i1, j1, false);
|
|
+ Chunk chunk = (Chunk)this.getChunkIfLoadedImmediately(i1, j1); // Paper
|
|
|
|
if (chunk != null) {
|
|
chunk.a(entitytypes, axisalignedbb, list, predicate);
|
|
@@ -1239,7 +1239,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
|
|
|
|
for (int i1 = i; i1 < j; ++i1) {
|
|
for (int j1 = k; j1 < l; ++j1) {
|
|
- Chunk chunk = ichunkprovider.getChunkAt(i1, j1, false);
|
|
+ Chunk chunk = (Chunk)this.getChunkIfLoadedImmediately(i1, j1); // Paper
|
|
|
|
if (chunk != null) {
|
|
chunk.a(oclass, axisalignedbb, list, predicate);
|
|
diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
|
|
index 136c18d10..14f25caa0 100644
|
|
--- a/src/main/java/net/minecraft/server/WorldServer.java
|
|
+++ b/src/main/java/net/minecraft/server/WorldServer.java
|
|
@@ -154,6 +154,12 @@ public class WorldServer extends World {
|
|
};
|
|
public final com.destroystokyo.paper.io.chunk.ChunkTaskManager asyncChunkTaskManager;
|
|
// Paper end
|
|
+ // Paper start
|
|
+ @Override
|
|
+ public boolean isChunkLoaded(int x, int z) {
|
|
+ return this.getChunkProvider().getChunkAtIfLoadedImmediately(x, z) != null;
|
|
+ }
|
|
+ // Paper end
|
|
|
|
// Add env and gen to constructor
|
|
public WorldServer(MinecraftServer minecraftserver, Executor executor, WorldNBTStorage worldnbtstorage, WorldData worlddata, DimensionManager dimensionmanager, GameProfilerFiller gameprofilerfiller, WorldLoadListener worldloadlistener, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) {
|
|
--
|
|
2.24.1
|
|
|