diff --git a/patches/server/0156-PaperPR-Apply-advancements-async.patch b/patches/server/0156-PaperPR-Apply-advancements-async.patch new file mode 100644 index 000000000..18c5e1972 --- /dev/null +++ b/patches/server/0156-PaperPR-Apply-advancements-async.patch @@ -0,0 +1,122 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mariell Hoversholm +Date: Thu, 20 Aug 2020 17:57:02 +0200 +Subject: [PATCH] PaperPR - Apply advancements async + + +diff --git a/src/main/java/net/minecraft/server/AdvancementDataPlayer.java b/src/main/java/net/minecraft/server/AdvancementDataPlayer.java +index eaa1063ff2..a3b89a4f2a 100644 +--- a/src/main/java/net/minecraft/server/AdvancementDataPlayer.java ++++ b/src/main/java/net/minecraft/server/AdvancementDataPlayer.java +@@ -63,6 +63,7 @@ public class AdvancementDataPlayer { + this.d(advancementdataworld); + } + ++ public final void setPlayer(EntityPlayer entityPlayer) { this.a(entityPlayer); } // Paper - OBFHELPER + public void a(EntityPlayer entityplayer) { + this.player = entityplayer; + } +diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java +index c5d03fd208..a58e7ba6e4 100644 +--- a/src/main/java/net/minecraft/server/EntityPlayer.java ++++ b/src/main/java/net/minecraft/server/EntityPlayer.java +@@ -49,7 +49,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + public final MinecraftServer server; + public final PlayerInteractManager playerInteractManager; + public final Deque removeQueue = new ArrayDeque<>(); // Paper +- private final AdvancementDataPlayer advancementDataPlayer; ++ private AdvancementDataPlayer advancementDataPlayer; // Paper - remove final ++ private final java.util.concurrent.CompletableFuture advancementDataPlayerCompletableFuture; // Paper - async advancements + private final ServerStatisticManager serverStatisticManager; + private float lastHealthScored = Float.MIN_VALUE; + private int lastFoodScored = Integer.MIN_VALUE; +@@ -136,7 +137,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + this.playerInteractManager = playerinteractmanager; + this.server = minecraftserver; + this.serverStatisticManager = minecraftserver.getPlayerList().getStatisticManager(this); +- this.advancementDataPlayer = minecraftserver.getPlayerList().f(this); ++ this.advancementDataPlayerCompletableFuture = minecraftserver.getPlayerList().loadAdvancementDataPlayerAsync(this); // Paper - async advancements + this.G = 1.0F; + //this.c(worldserver); // Paper - don't move to spawn on login, only first join + this.co = minecraftserver.a(this); +@@ -505,7 +506,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + CriterionTriggers.u.a(this, this.cf, this.ticksLived - this.cg); + } + +- this.advancementDataPlayer.b(this); ++ if (areAdvancementsLoaded()) // Paper - async advancements: don't tick advancements until they're loaded ++ this.advancementDataPlayer.sendUpdateIfNeeded(this); // Paper - conflict on change + + // Purpur start + if (this.world.purpurConfig.useNightVisionWhenRiding && this.getVehicle() != null && this.getVehicle().getRider() == this && world.getTime() % 100 == 0) { // 5 seconds +@@ -2121,7 +2123,26 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + this.worldChangeInvuln = false; + } + ++ // Paper start - async advancements ++ public boolean areAdvancementsLoaded() { ++ return this.advancementDataPlayer != null ++ || this.advancementDataPlayerCompletableFuture.isDone(); ++ } ++ ++ public AdvancementDataPlayer getAdvancementDataIfLoadedImmediately() { ++ // We need a null check here because this method is called before the future is assigned. ++ // Once assigned, it will essentially be a no-op as the field is final. ++ if (this.advancementDataPlayer == null && this.advancementDataPlayerCompletableFuture != null && this.advancementDataPlayerCompletableFuture.isDone()) ++ this.advancementDataPlayer = this.advancementDataPlayerCompletableFuture.join(); ++ return this.advancementDataPlayer; ++ } ++ + public AdvancementDataPlayer getAdvancementData() { ++ if (this.advancementDataPlayer == null) ++ synchronized (this.advancementDataPlayerCompletableFuture) { ++ if (this.advancementDataPlayer == null) this.advancementDataPlayer = this.advancementDataPlayerCompletableFuture.join(); ++ } ++ // Paper end + return this.advancementDataPlayer; + } + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 65dd23d8ec..47265cad93 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -2312,6 +2312,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant loadAdvancementDataPlayerAsync(EntityPlayer entityPlayer) { ++ if (entityPlayer.getAdvancementDataIfLoadedImmediately() != null) { ++ entityPlayer.getAdvancementData().setPlayer(entityPlayer); ++ return java.util.concurrent.CompletableFuture.completedFuture(entityPlayer.getAdvancementData()); ++ } ++ ++ UUID uuid = entityPlayer.getUniqueID(); ++ File file = this.server.getSavedFilePath(SavedFile.ADVANCEMENTS).toFile(); ++ final File file1 = new File(file, uuid + ".json"); ++ return java.util.concurrent.CompletableFuture.supplyAsync( ++ () -> new AdvancementDataPlayer(this.server.getDataFixer(), this, this.server.getAdvancementData(), file1, entityPlayer), ++ server.executorService ++ ); ++ } ++ // Paper end ++ + public AdvancementDataPlayer f(EntityPlayer entityplayer) { + UUID uuid = entityplayer.getUniqueID(); +- AdvancementDataPlayer advancementdataplayer = (AdvancementDataPlayer) entityplayer.getAdvancementData(); // CraftBukkit ++ AdvancementDataPlayer advancementdataplayer = (AdvancementDataPlayer) entityplayer.getAdvancementDataIfLoadedImmediately(); // CraftBukkit // Paper + + if (advancementdataplayer == null) { + File file = this.server.a(SavedFile.ADVANCEMENTS).toFile();