mirror of
https://github.com/PurpurMC/Purpur.git
synced 2026-02-17 16:37:43 +01:00
Upstream has released updates that appears to apply and compile correctly Paper Changes: cf82dad3 Fix Non Full Status Chunk NBT Memory Leak 5a12515a Update Log4J Configuration file to stop truncating stack traces 7c001d64 More Improvements to Chunks e1c45196 Fix high memory use of non ticking chunks ee9f0d51 Fix another case of breaking blocks causing sync chunk loads 6009ba8f Drop AABB limit patch until it can be tested more 0e9c24e5 Fix log spam about Hanging entities bounding boxes 83fadad7 Fix conversion for deserializing raw nbt itemstacks - Fixes #3424 4d38ee11 Many fixes and improvements to chunk prioritization 281181c7 Use saner Entity bounding box limits edd6b6a2 Protect the visible chunk map from plugins touching it, trim Timing Errors 18c68657 Optimize performance of object pool 7e1525ea Many improvements to chunk prioritization and bug fixes c82b292a Fix pooled buffer leak resulting in dynmap black spots - Fixes #3386 63274472 Fix ./paper edit continue for Windows eb5a3058 Fix path in CONTRIBUTING.md (#3406) f6ed326d Fix a small error in CONTRIBUTING.md (#3403) 614a664b Implement Chunk Priority / Urgency System for Chunks
226 lines
11 KiB
Diff
226 lines
11 KiB
Diff
From d2c2b464f6e94a3c3904e410164da9c97e9dde84 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 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 771d87971..6d46ca591 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 0d6e0f2dd..ce0aef25c 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 24ec5d77c..1243134bc 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 ab88636ca..becb13e0e 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 066b07f5a..067272cf3 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
|
|
|