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

Paper Changes:
8ce3dd5f [CI-SKIP] Fix Mojang API Brigadier dep - THIS IS NOT A NEW BUILD
00d760a5 Fix build due to spigot changing the build timestamp process
842e040c Updated Upstream (Bukkit/CraftBukkit/Spigot)
c03260a2 Add getter and setter for villager's numberOfRestocksToday (#3231)
fe366fbe null check tracker for entity metadata update - Fixes #3070
fdf41b74 Implement Brigadier Mojang API
e0ea2e0e Entity Activation Range 2.0! Major improvements to restoring behavior
10396d28 Fix Tracking Range mismatch on Vehicle/Passenger checks
2020-04-28 20:14:57 -05:00

227 lines
11 KiB
Diff

From 7ed9c2447cdcbe031c8c57e56eeb441907d591d6 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 55f9f4e6e..ac21fdb4c 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 279c7a85f..f26bc3417 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 7a8397815..8aae0b63e 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 6e0d0a54a..07f77dbb8 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 be6baf299..ba2ac70ca 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;
@@ -204,4 +205,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 e44cc3755..68cc7bf66 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -487,6 +487,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