|
|
|
|
@@ -1,225 +0,0 @@
|
|
|
|
|
From c84c8fd87b181d5fa8c3d6b1623e38d00aeb96cc 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 a4a4235f42..5d0789813f 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;
|
|
|
|
|
@@ -175,6 +234,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
|
|
|
|
|
|