mirror of
https://github.com/PurpurMC/Purpur.git
synced 2026-02-17 16:37:43 +01:00
Add chunk unload delay
This commit is contained in:
226
patches/server/0119-Delay-chunk-unloads.patch
Normal file
226
patches/server/0119-Delay-chunk-unloads.patch
Normal file
@@ -0,0 +1,226 @@
|
||||
From 3962da09dfb390b3b7b5168632d58811adc6bb59 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] 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 0244768f76..b69e743950 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 4b87ca2ecb..346e2ebc79 100644
|
||||
--- a/src/main/java/net/minecraft/server/TicketType.java
|
||||
+++ b/src/main/java/net/minecraft/server/TicketType.java
|
||||
@@ -23,6 +23,7 @@ public class TicketType<T> {
|
||||
public static final TicketType<org.bukkit.plugin.Plugin> PLUGIN_TICKET = a("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit
|
||||
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 bb5ba4f137..1b95504491 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 org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
@@ -214,4 +215,12 @@ public class PurpurConfig {
|
||||
enchantment.setMaxLevel(maxLevel);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ public static int delayChunkUnloadsBy;
|
||||
+ private static void delayChunkUnloadsBy() {
|
||||
+ delayChunkUnloadsBy = getInt("settings.delay-chunk-unloads-by", 10) * 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 05f0025bb9..8bd9459253 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
@@ -466,6 +466,7 @@ public class CraftWorld implements World {
|
||||
net.minecraft.server.IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, false);
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user