mirror of
https://github.com/PaperMC/Paper.git
synced 2024-09-12 06:58:11 +02:00
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: b2f1908c SPIGOT-5783: Add helpful info to UnknownDependencyException e4f46260 SPIGOT-2623: Add EntityEquipment methods to get/set ItemStacks by slot. 529a9a69 SPIGOT-5751: Clarify behaviour of block drop-related API methods CraftBukkit Changes:8ea9b138
Remove outdated build delay.ffc2b251
Revert "#675: Fix redirected CommandNodes sometimes not being properly redirected"cb701f6b
#675: Fix redirected CommandNodes sometimes not being properly redirectedc9d7c16b
SPIGOT-2623: Add EntityEquipment methods to get/set ItemStacks by slot.fad2494a
#673: Fix Craftworld#isChunkLoaded8637ec00
SPIGOT-5751: Made breakNaturally and getDrops returns the correct item if no argument is given Spigot Changes: a99063f7 Rebuild patches Fixes #3602
348 lines
15 KiB
Diff
348 lines
15 KiB
Diff
From 0000000000000000000000000000000000000000 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 331493a172f58e71b464d635efdba461082bd27d..182b440ba4802d199b8e44f7779b3401ace495d5 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
|
@@ -1,10 +1,14 @@
|
|
package com.destroystokyo.paper;
|
|
|
|
import com.destroystokyo.paper.io.chunk.ChunkTaskManager;
|
|
+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;
|
|
@@ -19,6 +23,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.*;
|
|
@@ -29,14 +36,14 @@ public class PaperCommand extends Command {
|
|
public PaperCommand(String name) {
|
|
super(name);
|
|
this.description = "Paper related commands";
|
|
- this.usageMessage = "/paper [heap | entity | reload | version | debug | dumpwaiting | chunkinfo]";
|
|
+ this.usageMessage = "/paper [heap | entity | reload | version | debug | dumpwaiting | chunkinfo | syncloadinfo]";
|
|
this.setPermission("bukkit.command.paper");
|
|
}
|
|
|
|
@Override
|
|
public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
|
|
if (args.length <= 1)
|
|
- return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "dumpwaiting", "chunkinfo");
|
|
+ return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "dumpwaiting", "chunkinfo", "syncloadinfo");
|
|
|
|
switch (args[0].toLowerCase(Locale.ENGLISH))
|
|
{
|
|
@@ -134,6 +141,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");
|
|
@@ -150,6 +160,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 0000000000000000000000000000000000000000..59aec103295f747793fdc0a52eb45f4121aba921
|
|
--- /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 981cf581c7504c38120d48c06b6351952fab43c0..54cf319e9db1aad793ab7f55249f9857b450eda2 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -470,6 +470,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.syncChunkLoad.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 8e5c54af26d2c7abf2daae081af97caee7fd9f7a..045e449c7e411dec7ef415c76c808cda426db652 100644
|
|
--- a/src/main/java/net/minecraft/server/World.java
|
|
+++ b/src/main/java/net/minecraft/server/World.java
|
|
@@ -1174,7 +1174,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);
|
|
@@ -1195,7 +1195,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);
|
|
@@ -1218,7 +1218,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 83e6c872028107a17de1c08a5f6e8a02d62277ea..da38293cf02d84f3e24a883136ffe5118a510b5a 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) {
|