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/CriterionProgress.java b/src/main/java/net/minecraft/server/CriterionProgress.java index 98c3884f47..244b58a7c4 100644 --- a/src/main/java/net/minecraft/server/CriterionProgress.java +++ b/src/main/java/net/minecraft/server/CriterionProgress.java @@ -10,8 +10,9 @@ import java.util.Date; public class CriterionProgress { - private static final SimpleDateFormat a = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); - private Date b; + private static final SimpleDateFormat a = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); // Paper - diff on change + private static final ThreadLocal THREAD_LOCAL_FORMATTER = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z")); // Paper - async advancements - SimpleDateFormat is not thread safe + private Date b; private void setDate(Date date) { this.b = date; } // Paper - OBFHELPER public CriterionProgress() {} @@ -44,7 +45,7 @@ public class CriterionProgress { } public JsonElement e() { - return (JsonElement) (this.b != null ? new JsonPrimitive(CriterionProgress.a.format(this.b)) : JsonNull.INSTANCE); + return this.getDate() != null ? new JsonPrimitive(THREAD_LOCAL_FORMATTER.get().format(this.getDate())) : JsonNull.INSTANCE; // Paper } public static CriterionProgress b(PacketDataSerializer packetdataserializer) { @@ -61,7 +62,7 @@ public class CriterionProgress { CriterionProgress criterionprogress = new CriterionProgress(); try { - criterionprogress.b = CriterionProgress.a.parse(s); + criterionprogress.setDate(THREAD_LOCAL_FORMATTER.get().parse(s)); // Paper return criterionprogress; } catch (ParseException parseexception) { throw new JsonSyntaxException("Invalid datetime: " + s, parseexception); diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java index 50ea875a3b..cc6a54e06a 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 fd75444cec..95d93cd8e5 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -2315,6 +2315,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), + this.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();