aboutsummaryrefslogtreecommitdiffhomepage
path: root/Spigot-Server-Patches/0410-Reduce-sync-loads.patch
diff options
context:
space:
mode:
Diffstat (limited to 'Spigot-Server-Patches/0410-Reduce-sync-loads.patch')
-rw-r--r--Spigot-Server-Patches/0410-Reduce-sync-loads.patch349
1 files changed, 349 insertions, 0 deletions
diff --git a/Spigot-Server-Patches/0410-Reduce-sync-loads.patch b/Spigot-Server-Patches/0410-Reduce-sync-loads.patch
new file mode 100644
index 0000000000..cbf364747d
--- /dev/null
+++ b/Spigot-Server-Patches/0410-Reduce-sync-loads.patch
@@ -0,0 +1,349 @@
+From 4ba2f464ac38d9b7346b8015c8ce6cb74084fc03 Mon Sep 17 00:00:00 2001
+From: Spottedleaf <[email protected]>
+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 f793ba08e..c8451afec 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 b81b37445..9d29fc8ca 100644
+--- a/src/main/java/net/minecraft/server/World.java
++++ b/src/main/java/net/minecraft/server/World.java
+@@ -1195,14 +1195,14 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose
+ }
+
+ public boolean n(BlockPosition blockposition) {
+- return isOutsideWorld(blockposition) ? false : this.chunkProvider.b(blockposition.getX() >> 4, blockposition.getZ() >> 4);
++ return isOutsideWorld(blockposition) ? false : this.isLoaded(blockposition); // Paper - reduce sync loads
+ }
+
+ public boolean a(BlockPosition blockposition, Entity entity) {
+ if (isOutsideWorld(blockposition)) {
+ return false;
+ } else {
+- IChunkAccess ichunkaccess = this.getChunkAt(blockposition.getX() >> 4, blockposition.getZ() >> 4, ChunkStatus.FULL, false);
++ IChunkAccess ichunkaccess = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4); // Paper - reduce sync loads
+
+ return ichunkaccess == null ? false : ichunkaccess.getType(blockposition).a((IBlockAccess) this, blockposition, entity);
+ }
+@@ -1249,7 +1249,7 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose
+
+ 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);
+@@ -1269,7 +1269,7 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose
+
+ 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);
+@@ -1291,7 +1291,7 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose
+
+ 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 0c1f4e6e1..845575f52 100644
+--- a/src/main/java/net/minecraft/server/WorldServer.java
++++ b/src/main/java/net/minecraft/server/WorldServer.java
+@@ -150,6 +150,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().getChunkAtIfCachedImmediately(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.23.0
+