diff options
Diffstat (limited to 'Spigot-Server-Patches/0410-Reduce-sync-loads.patch')
-rw-r--r-- | Spigot-Server-Patches/0410-Reduce-sync-loads.patch | 349 |
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 + |