mirror of
https://github.com/PurpurMC/Purpur.git
synced 2026-02-18 08:57:44 +01:00
Upstream has released updates that appears to apply and compile correctly Paper Changes: f29c7ebd Improve async login (#3189) 9fd36824 Fix Citizens Player NPC tracking issue - Fixes #3186 0e72de1c Fix Player Tracking issue during join process. f26362af Load Spawn Chunks Asynchronously and Spiral Out 54f762e2 Load Chunks for Login Asynchronously f2d1b6e5 Clean up duplicate PlayerInitialSpawnEvent c2d022d7 Fix Longstanding Broken behavior of PlayerJoinEvent d0a348b9 Broadcast join messages to console 1ef687a4 Don't crash if player is attempted to be removed from untracked chunk. c11668ac Make sure the chunk conversion task is executed immediately 26fb7cc3 Fix Chunk Post Processing deadlock risk
227 lines
11 KiB
Diff
227 lines
11 KiB
Diff
From 9b9c02d7330a016c77147e42462ee459b54edafd Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
Date: Fri, 25 Oct 2019 02:11:30 -0700
|
|
Subject: [PATCH] Tuinity - Delay chunk unloads
|
|
|
|
Chunk unloads are now delayed by 10s. Specifically, ticket level
|
|
reduction is delayed by 10s. This is done to allow players to
|
|
teleport and have their pets follow them, as the chunks will no longer
|
|
unload or have entity ticking status removed.
|
|
|
|
It's also targetted to reduce performance regressions when
|
|
plugins or edge cases in code do not spam sync loads since chunks
|
|
without tickets get unloaded immediately.
|
|
|
|
Configurable under `delay-chunkunloads-by` in config.
|
|
---
|
|
.../java/net/minecraft/server/ChunkMap.java | 1 +
|
|
.../minecraft/server/ChunkMapDistance.java | 69 ++++++++++++++++++-
|
|
.../java/net/minecraft/server/Ticket.java | 6 +-
|
|
.../java/net/minecraft/server/TicketType.java | 1 +
|
|
.../java/net/pl3x/purpur/PurpurConfig.java | 9 +++
|
|
.../org/bukkit/craftbukkit/CraftWorld.java | 1 +
|
|
6 files changed, 83 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkMap.java b/src/main/java/net/minecraft/server/ChunkMap.java
|
|
index 55f9f4e6e7..ac21fdb4c9 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkMap.java
|
|
@@ -68,6 +68,7 @@ public abstract class ChunkMap extends LightEngineGraph {
|
|
|
|
protected abstract int b(long i);
|
|
|
|
+ public final void update(long coordinate, int level, boolean increaseInLevel) { this.b(coordinate, level, increaseInLevel); } // Purpur - OBFHELPER
|
|
public void b(long i, int j, boolean flag) {
|
|
this.a(ChunkCoordIntPair.a, i, j, flag);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
index 279c7a85fb..f26bc3417e 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
@@ -30,7 +30,7 @@ public abstract class ChunkMapDistance {
|
|
private static final int b = 33 + ChunkStatus.a(ChunkStatus.FULL) - 2;
|
|
private final Long2ObjectMap<ObjectSet<EntityPlayer>> c = new Long2ObjectOpenHashMap();
|
|
public final Long2ObjectOpenHashMap<ArraySetSorted<Ticket<?>>> tickets = new Long2ObjectOpenHashMap();
|
|
- private final ChunkMapDistance.a e = new ChunkMapDistance.a();
|
|
+ private final ChunkMapDistance.a e = new ChunkMapDistance.a(); final ChunkMapDistance.a getTicketTracker() { return this.e; } // Purpur - OBFHELPER
|
|
private final ChunkMapDistance.b f = new ChunkMapDistance.b(8);
|
|
private final ChunkMapDistance.c g = new ChunkMapDistance.c(33);
|
|
private final java.util.Queue<PlayerChunk> pendingChunkUpdates = new java.util.LinkedList<>(); // PAIL pendingChunkUpdates // Paper - use a queue
|
|
@@ -41,6 +41,47 @@ public abstract class ChunkMapDistance {
|
|
private final Executor m;
|
|
private long currentTick;
|
|
|
|
+ // Purpur start
|
|
+ private long nextUnloadId;
|
|
+ private final Long2ObjectOpenHashMap<Ticket<Long>> delayedChunks = new Long2ObjectOpenHashMap<>();
|
|
+ public final void removeTickets(long chunk, TicketType<?> type) {
|
|
+ ArraySetSorted<Ticket<?>> tickets = this.tickets.get(chunk);
|
|
+ if (tickets == null) {
|
|
+ return;
|
|
+ }
|
|
+ if (type == TicketType.DELAYED_UNLOAD) {
|
|
+ this.delayedChunks.remove(chunk);
|
|
+ }
|
|
+ boolean changed = tickets.removeIf((Ticket<?> ticket) -> {
|
|
+ return ticket.getTicketType() == type;
|
|
+ });
|
|
+ if (changed) {
|
|
+ this.getTicketTracker().update(chunk, getLowestTicketLevel(tickets), false);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private final java.util.function.LongFunction<Ticket<Long>> computeFuntion = (long key) -> {
|
|
+ Ticket<Long> ret = new Ticket<>(TicketType.DELAYED_UNLOAD, -1, ++ChunkMapDistance.this.nextUnloadId);
|
|
+ ret.isCached = true;
|
|
+ return ret;
|
|
+ };
|
|
+
|
|
+ private void computeDelayedTicketFor(long chunk, int removedLevel, ArraySetSorted<Ticket<?>> tickets) {
|
|
+ int lowestLevel = getLowestTicketLevel(tickets);
|
|
+ if (removedLevel > lowestLevel) {
|
|
+ return;
|
|
+ }
|
|
+ final Ticket<Long> ticket = this.delayedChunks.computeIfAbsent(chunk, this.computeFuntion);
|
|
+ if (ticket.getTicketLevel() != -1) {
|
|
+ // since we modify data used in sorting, we need to remove before
|
|
+ tickets.remove(ticket);
|
|
+ }
|
|
+ ticket.setCreationTick(this.currentTick);
|
|
+ ticket.setTicketLevel(removedLevel);
|
|
+ tickets.add(ticket); // re-add with new expire time and ticket level
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
protected ChunkMapDistance(Executor executor, Executor executor1) {
|
|
executor1.getClass();
|
|
Mailbox<Runnable> mailbox = Mailbox.a("player ticket throttler", executor1::execute);
|
|
@@ -56,12 +97,30 @@ public abstract class ChunkMapDistance {
|
|
++this.currentTick;
|
|
ObjectIterator objectiterator = this.tickets.long2ObjectEntrySet().fastIterator();
|
|
|
|
+ int[] tempLevel = new int[] { PlayerChunkMap.GOLDEN_TICKET + 1 }; // Purpur
|
|
while (objectiterator.hasNext()) {
|
|
Entry<ArraySetSorted<Ticket<?>>> entry = (Entry) objectiterator.next();
|
|
|
|
if ((entry.getValue()).removeIf((ticket) -> { // CraftBukkit - decompile error
|
|
- return ticket.b(this.currentTick);
|
|
+ // Purpur start
|
|
+ boolean ret = ticket.isExpired(this.currentTick);
|
|
+ if (net.pl3x.purpur.PurpurConfig.delayChunkUnloadsBy <= 0) {
|
|
+ return ret;
|
|
+ }
|
|
+ if (ret && ticket.getTicketType() != TicketType.DELAYED_UNLOAD && ticket.getTicketLevel() < tempLevel[0]) {
|
|
+ tempLevel[0] = ticket.getTicketLevel();
|
|
+ }
|
|
+ if (ticket.getTicketType() == TicketType.DELAYED_UNLOAD && ticket.isCached) {
|
|
+ this.delayedChunks.remove(entry.getLongKey(), ticket); // clean up ticket...
|
|
+ }
|
|
+ return ret;
|
|
+ // Purpur end
|
|
})) {
|
|
+ // Purpur start
|
|
+ if (tempLevel[0] < (PlayerChunkMap.GOLDEN_TICKET + 1)) {
|
|
+ this.computeDelayedTicketFor(entry.getLongKey(), tempLevel[0], entry.getValue());
|
|
+ }
|
|
+ // Purpur end
|
|
this.e.b(entry.getLongKey(), a((ArraySetSorted) entry.getValue()), false);
|
|
}
|
|
|
|
@@ -72,6 +131,7 @@ public abstract class ChunkMapDistance {
|
|
|
|
}
|
|
|
|
+ private static int getLowestTicketLevel(ArraySetSorted<Ticket<?>> arraysetsorted) { return a(arraysetsorted); } // Purpur - OBFHELPER
|
|
private static int a(ArraySetSorted<Ticket<?>> arraysetsorted) {
|
|
return !arraysetsorted.isEmpty() ? ((Ticket) arraysetsorted.b()).b() : PlayerChunkMap.GOLDEN_TICKET + 1;
|
|
}
|
|
@@ -154,6 +214,11 @@ public abstract class ChunkMapDistance {
|
|
boolean removed = false; // CraftBukkit
|
|
if (arraysetsorted.remove(ticket)) {
|
|
removed = true; // CraftBukkit
|
|
+ // Purpur start
|
|
+ if (net.pl3x.purpur.PurpurConfig.delayChunkUnloadsBy > 0 && ticket.getTicketType() != TicketType.DELAYED_UNLOAD) {
|
|
+ this.computeDelayedTicketFor(i, ticket.getTicketLevel(), arraysetsorted);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
if (arraysetsorted.isEmpty()) {
|
|
diff --git a/src/main/java/net/minecraft/server/Ticket.java b/src/main/java/net/minecraft/server/Ticket.java
|
|
index 7a8397815a..8aae0b63e5 100644
|
|
--- a/src/main/java/net/minecraft/server/Ticket.java
|
|
+++ b/src/main/java/net/minecraft/server/Ticket.java
|
|
@@ -5,9 +5,10 @@ import java.util.Objects;
|
|
public final class Ticket<T> implements Comparable<Ticket<?>> {
|
|
|
|
private final TicketType<T> a;
|
|
- private final int b;
|
|
+ private int b; public final void setTicketLevel(final int value) { this.b = value; } // Purpur - remove final - OBFHELPER
|
|
public final T identifier; public final T getObjectReason() { return this.identifier; } // Paper - OBFHELPER
|
|
- private long d; public final long getCreationTick() { return this.d; } // Paper - OBFHELPER
|
|
+ private long d; public final long getCreationTick() { return this.d; } public final void setCreationTick(final long value) { this.d = value; } // Paper - OBFHELPER // Purpur - OBFHELPER
|
|
+ boolean isCached; // Purpur - delay chunk unloads, this defends against really stupid plugins
|
|
|
|
protected Ticket(TicketType<T> tickettype, int i, T t0) {
|
|
this.a = tickettype;
|
|
@@ -60,6 +61,7 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
|
|
this.d = i;
|
|
}
|
|
|
|
+ protected final boolean isExpired(long time) { return this.b(time); } // Purpur - OBFHELPER
|
|
protected boolean b(long i) {
|
|
long j = this.a.b();
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java
|
|
index 6e0d0a54a2..07f77dbb87 100644
|
|
--- a/src/main/java/net/minecraft/server/TicketType.java
|
|
+++ b/src/main/java/net/minecraft/server/TicketType.java
|
|
@@ -24,6 +24,7 @@ public class TicketType<T> {
|
|
public static final TicketType<Long> FUTURE_AWAIT = a("future_await", Long::compareTo); // Paper
|
|
public static final TicketType<Integer> ANTIXRAY = a("antixray", Integer::compareTo); // Paper - Anti-Xray
|
|
public static final TicketType<Long> ASYNC_LOAD = a("async_load", Long::compareTo); // Paper
|
|
+ public static final TicketType<Long> DELAYED_UNLOAD = a("delayed_unload", Long::compareTo); // Purpur
|
|
|
|
public static <T> TicketType<T> a(String s, Comparator<T> comparator) {
|
|
return new TicketType<>(s, comparator, 0L);
|
|
diff --git a/src/main/java/net/pl3x/purpur/PurpurConfig.java b/src/main/java/net/pl3x/purpur/PurpurConfig.java
|
|
index 4d7795c938..a13cb072ba 100644
|
|
--- a/src/main/java/net/pl3x/purpur/PurpurConfig.java
|
|
+++ b/src/main/java/net/pl3x/purpur/PurpurConfig.java
|
|
@@ -4,6 +4,7 @@ import com.google.common.base.Throwables;
|
|
import net.minecraft.server.Enchantment;
|
|
import net.minecraft.server.IRegistry;
|
|
import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.server.TicketType;
|
|
import net.pl3x.purpur.command.PurpurCommand;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.command.Command;
|
|
@@ -210,4 +211,12 @@ public class PurpurConfig {
|
|
enchantment.setMaxLevel(maxLevel);
|
|
}
|
|
}
|
|
+
|
|
+ public static int delayChunkUnloadsBy;
|
|
+ private static void delayChunkUnloadsBy() {
|
|
+ delayChunkUnloadsBy = getInt("settings.delay-chunk-unloads-by", 1) * 20;
|
|
+ if (delayChunkUnloadsBy >= 0) {
|
|
+ TicketType.DELAYED_UNLOAD.loadPeriod = delayChunkUnloadsBy;
|
|
+ }
|
|
+ }
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index 46de7d232f..efc9e52305 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -485,6 +485,7 @@ public class CraftWorld implements World {
|
|
net.minecraft.server.IChunkAccess chunk = world.getChunkProvider().getChunkAtIfLoadedImmediately(x, z); // Paper
|
|
if (chunk != null) {
|
|
world.getChunkProvider().removeTicket(TicketType.PLUGIN, chunk.getPos(), 1, Unit.INSTANCE);
|
|
+ ((ChunkMapDistance)world.getChunkProvider().playerChunkMap.getChunkMapDistanceManager()).removeTickets(ChunkCoordIntPair.pair(x, z), TicketType.DELAYED_UNLOAD); // Purpur - delay chunk unloads - let plugins override
|
|
}
|
|
|
|
return true;
|
|
--
|
|
2.24.0
|
|
|