mirror of
https://github.com/PurpurMC/Purpur.git
synced 2026-02-18 08:57:44 +01:00
227 lines
12 KiB
Diff
227 lines
12 KiB
Diff
From 268092bc3630f2ac33f396e0f0d4ddc8f3cf4ede 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 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 31c5eec50c..66258ea47b 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;
|
|
@@ -215,4 +216,12 @@ public class PurpurConfig {
|
|
private static void updatePermsAndCommandsAsync() {
|
|
updatePermsAndCommandsAsync = getBoolean("settings.update-permissions-and-commands-async", updatePermsAndCommandsAsync);
|
|
}
|
|
+
|
|
+ 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
|
|
|