Files
Purpur/patches/server/0122-Tuinity-Delay-chunk-unloads.patch
William Blake Galbreath a032d548bf Updated Upstream (Paper)
Upstream has released updates that appears to apply and compile correctly

Paper Changes:
a8ef0a93 Drop remove leaked chunk patch - causing many issues
ff4ca31f Fix premature unloading of neighbor chunks for world gen
fde81356 Fix Chunks unloading immediately for priority access - Fixes #3461
9980f427 Fix some logic on villager poi max attempts
919ab67a Fix concurrency issue in light engine (Vanilla bug)
0d5ec653 Cleanup async chunks (#3456)
c3c005c9 Updated Upstream (Bukkit/CraftBukkit) (Fixes #3443)
2020-05-29 17:42:32 -05:00

226 lines
12 KiB
Diff

From 0676e1e882ad34b6ea8cd6fc7455919c2d34ccc9 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 | 68 ++++++++++++++++++-
.../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, 82 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 9805361e2d..3ad0177d2a 100644
--- a/src/main/java/net/minecraft/server/ChunkMapDistance.java
+++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java
@@ -31,7 +31,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
public static final int MOB_SPAWN_RANGE = 8; //private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); // Paper - no longer used
private final ChunkMapDistance.c g = new ChunkMapDistance.c(33);
// Paper start use a queue, but still keep unique requirement
@@ -52,6 +52,46 @@ public abstract class ChunkMapDistance {
private long currentTick;
PlayerChunkMap chunkMap; // Paper
+ // 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();
@@ -68,12 +108,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);
}
@@ -84,6 +142,7 @@ public abstract class ChunkMapDistance {
}
+ private static int getLowestTicketLevel(ArraySetSorted<Ticket<?>> arraysetsorted) { return a(arraysetsorted); } // Purpur - OBFHELPER
private static int a(ArraySetSorted<Ticket<?>> arraysetsorted) {
AsyncCatcher.catchOp("ChunkMapDistance::getHighestTicketLevel"); // Paper
return !arraysetsorted.isEmpty() ? ((Ticket) arraysetsorted.b()).b() : PlayerChunkMap.GOLDEN_TICKET + 1;
@@ -182,6 +241,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 0d6e0f2dda..ce0aef25c6 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
public int priority = 0; // Paper
protected Ticket(TicketType<T> tickettype, int i, T t0) {
@@ -61,6 +62,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 24ec5d77ca..1243134bc2 100644
--- a/src/main/java/net/minecraft/server/TicketType.java
+++ b/src/main/java/net/minecraft/server/TicketType.java
@@ -25,6 +25,7 @@ public class TicketType<T> {
public static final TicketType<Long> ASYNC_LOAD = a("async_load", Long::compareTo); // Paper
public static final TicketType<ChunkCoordIntPair> PRIORITY = a("priority", Comparator.comparingLong(ChunkCoordIntPair::pair), 300); // Paper
public static final TicketType<ChunkCoordIntPair> URGENT = a("urgent", Comparator.comparingLong(ChunkCoordIntPair::pair), 300); // 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 ab88636cad..becb13e0e0 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;
@@ -206,4 +207,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 cc1fb7108a..159c7673d5 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -488,6 +488,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