mirror of https://github.com/YatopiaMC/Yatopia.git
261 lines
14 KiB
Diff
261 lines
14 KiB
Diff
From 0000000000000000000000000000000000000000 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 1s. Specifically, ticket level
|
|
reduction is delayed by 1s. 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.
|
|
|
|
This patch replaces the paper patch as the paper patch only
|
|
affects player loaded chunks, when we want to target all
|
|
loads.
|
|
|
|
diff --git a/src/main/java/com/tuinity/tuinity/config/TuinityConfig.java b/src/main/java/com/tuinity/tuinity/config/TuinityConfig.java
|
|
index f10fa659680f8a574f77d260bbc52be349c244e8..182f419fde8eb3646a79cc0ba689ee486cb53338 100644
|
|
--- a/src/main/java/com/tuinity/tuinity/config/TuinityConfig.java
|
|
+++ b/src/main/java/com/tuinity/tuinity/config/TuinityConfig.java
|
|
@@ -1,6 +1,7 @@
|
|
package com.tuinity.tuinity.config;
|
|
|
|
import com.destroystokyo.paper.util.SneakyThrow;
|
|
+import net.minecraft.server.level.TicketType;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.configuration.ConfigurationSection;
|
|
import org.bukkit.configuration.file.YamlConfiguration;
|
|
@@ -123,6 +124,15 @@ public final class TuinityConfig {
|
|
tickThreads = TuinityConfig.getInt("server-tick-threads", 1); // will be 4 in the future
|
|
}*/
|
|
|
|
+ public static int delayChunkUnloadsBy;
|
|
+
|
|
+ private static void delayChunkUnloadsBy() {
|
|
+ delayChunkUnloadsBy = TuinityConfig.getInt("delay-chunkunloads-by", 5) * 20;
|
|
+ if (delayChunkUnloadsBy >= 0) {
|
|
+ TicketType.DELAYED_UNLOAD.loadPeriod = delayChunkUnloadsBy;
|
|
+ }
|
|
+ }
|
|
+
|
|
public static final class WorldConfig {
|
|
|
|
public final String worldName;
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMapDistance.java b/src/main/java/net/minecraft/server/level/ChunkMapDistance.java
|
|
index 4dfc1101d5fc78630ef8b816e8f84f5541683f0b..c474ee61d98772a2852c44dec1c4a1037472ed2c 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMapDistance.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMapDistance.java
|
|
@@ -40,7 +40,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 ticketLevelTracker = new ChunkMapDistance.a();
|
|
+ private final ChunkMapDistance.a ticketLevelTracker = new ChunkMapDistance.a(); final ChunkMapDistance.a getTicketTracker() { return this.ticketLevelTracker; } // Tuinity - 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
|
|
@@ -62,6 +62,47 @@ public abstract class ChunkMapDistance {
|
|
|
|
PlayerChunkMap chunkMap; // Paper
|
|
|
|
+ // Tuinity start - delay chunk unloads
|
|
+ private long nextUnloadId; // delay chunk unloads
|
|
+ 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
|
|
+ }
|
|
+ // Tuinity end - delay chunk unloads
|
|
+
|
|
protected ChunkMapDistance(Executor executor, Executor executor1) {
|
|
executor1.getClass();
|
|
Mailbox<Runnable> mailbox = Mailbox.a("player ticket throttler", executor1::execute);
|
|
@@ -78,18 +119,41 @@ public abstract class ChunkMapDistance {
|
|
++this.currentTick;
|
|
ObjectIterator objectiterator = this.tickets.long2ObjectEntrySet().fastIterator();
|
|
|
|
+ // Tuinity start - delay chunk unloads
|
|
+ int[] tempLevel = new int[] { PlayerChunkMap.GOLDEN_TICKET + 1 };
|
|
+ Entry<ArraySetSorted<Ticket<?>>>[] entryPass = new Entry[1];
|
|
+ java.util.function.Predicate<Ticket<?>> isExpired = (ticket) -> { // CraftBukkit - decompile error
|
|
+ // Tuinity start - delay chunk unloads
|
|
+ boolean ret = ticket.isExpired(this.currentTick);
|
|
+ if (com.tuinity.tuinity.config.TuinityConfig.delayChunkUnloadsBy <= 0) {
|
|
+ return ret;
|
|
+ }
|
|
+ if (ret && ticket.getTicketType().delayUnloadViable && ticket.getTicketLevel() < tempLevel[0]) {
|
|
+ tempLevel[0] = ticket.getTicketLevel();
|
|
+ }
|
|
+ if (ret && ticket.getTicketType() == TicketType.DELAYED_UNLOAD && ticket.isCached) {
|
|
+ this.delayedChunks.remove(entryPass[0].getLongKey(), ticket); // clean up ticket...
|
|
+ }
|
|
+ return ret;
|
|
+ };
|
|
+ // Tuinity end - delay chunk unloads
|
|
while (objectiterator.hasNext()) {
|
|
- Entry<ArraySetSorted<Ticket<?>>> entry = (Entry) objectiterator.next();
|
|
+ Entry<ArraySetSorted<Ticket<?>>> entry = (Entry) objectiterator.next(); entryPass[0] = entry; // Tuinity - only allocate lambda once
|
|
|
|
- if ((entry.getValue()).removeIf((ticket) -> { // CraftBukkit - decompile error
|
|
- return ticket.b(this.currentTick);
|
|
- })) {
|
|
+ if ((entry.getValue()).removeIf(isExpired)) { // Tuinity - move above - only allocate once
|
|
+ // Tuinity start - delay chunk unloads
|
|
+ if (tempLevel[0] < (PlayerChunkMap.GOLDEN_TICKET + 1)) {
|
|
+ this.computeDelayedTicketFor(entry.getLongKey(), tempLevel[0], entry.getValue());
|
|
+ }
|
|
+ // Tuinity end - delay chunk unloads
|
|
this.ticketLevelTracker.update(entry.getLongKey(), getLowestTicketLevel((ArraySetSorted) entry.getValue()), false);
|
|
}
|
|
|
|
if (((ArraySetSorted) entry.getValue()).isEmpty()) {
|
|
objectiterator.remove();
|
|
}
|
|
+
|
|
+ tempLevel[0] = PlayerChunkMap.GOLDEN_TICKET + 1; // Tuinity - reset
|
|
}
|
|
|
|
}
|
|
@@ -187,27 +251,11 @@ public abstract class ChunkMapDistance {
|
|
boolean removed = false; // CraftBukkit
|
|
if (arraysetsorted.remove(ticket)) {
|
|
removed = true; // CraftBukkit
|
|
- // Paper start - delay chunk unloads for player tickets
|
|
- long delayChunkUnloadsBy = chunkMap.world.paperConfig.delayChunkUnloadsBy;
|
|
- if (ticket.getTicketType() == TicketType.PLAYER && delayChunkUnloadsBy > 0) {
|
|
- boolean hasPlayer = false;
|
|
- for (Ticket<?> ticket1 : arraysetsorted) {
|
|
- if (ticket1.getTicketType() == TicketType.PLAYER) {
|
|
- hasPlayer = true;
|
|
- break;
|
|
- }
|
|
- }
|
|
- PlayerChunk playerChunk = chunkMap.getUpdatingChunk(i);
|
|
- if (!hasPlayer && playerChunk != null && playerChunk.isFullChunkReady()) {
|
|
- Ticket<Long> delayUnload = new Ticket<Long>(TicketType.DELAY_UNLOAD, 33, i);
|
|
- delayUnload.delayUnloadBy = delayChunkUnloadsBy;
|
|
- delayUnload.setCurrentTick(this.currentTick);
|
|
- arraysetsorted.remove(delayUnload);
|
|
- // refresh ticket
|
|
- arraysetsorted.add(delayUnload);
|
|
- }
|
|
+ // Tuinity start - delay chunk unloads
|
|
+ if (com.tuinity.tuinity.config.TuinityConfig.delayChunkUnloadsBy > 0 && ticket.getTicketType().delayUnloadViable) {
|
|
+ this.computeDelayedTicketFor(i, ticket.getTicketLevel(), arraysetsorted);
|
|
}
|
|
- // Paper end
|
|
+ // Tuinity end - delay chunk unloads
|
|
}
|
|
|
|
if (arraysetsorted.isEmpty()) {
|
|
diff --git a/src/main/java/net/minecraft/server/level/Ticket.java b/src/main/java/net/minecraft/server/level/Ticket.java
|
|
index 6e5ae954c6eb40590bf8c83f592c22088d489be8..c9b395dd0e4881a2e2e409bf782491b42ff087e6 100644
|
|
--- a/src/main/java/net/minecraft/server/level/Ticket.java
|
|
+++ b/src/main/java/net/minecraft/server/level/Ticket.java
|
|
@@ -5,17 +5,17 @@ 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; } // Tuinity - remove final, add set 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 // Tuinity - OBFHELPER
|
|
public int priority = 0; // Paper
|
|
- public long delayUnloadBy; // Paper
|
|
+ boolean isCached; // Tuinity - delay chunk unloads, this defends against really stupid plugins
|
|
|
|
protected Ticket(TicketType<T> tickettype, int i, T t0) {
|
|
this.a = tickettype;
|
|
this.b = i;
|
|
this.identifier = t0;
|
|
- this.delayUnloadBy = tickettype.loadPeriod; // Paper
|
|
+ // Tuinity - delay chunk unloads
|
|
}
|
|
|
|
public int compareTo(Ticket<?> ticket) {
|
|
@@ -64,8 +64,9 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
|
|
this.d = i;
|
|
}
|
|
|
|
+ protected final boolean isExpired(long time) { return this.b(time); } // Tuinity - OBFHELPER
|
|
protected boolean b(long i) {
|
|
- long j = delayUnloadBy; // Paper
|
|
+ long j = this.a.b(); // Tuinity - delay chunk unloads
|
|
|
|
return j != 0L && i - this.d > j;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java
|
|
index 3c804c7b20a14ea6e510810e2be10c1cc89ff5c1..47da7efffde2e6d63c1a064b950abf8135705622 100644
|
|
--- a/src/main/java/net/minecraft/server/level/TicketType.java
|
|
+++ b/src/main/java/net/minecraft/server/level/TicketType.java
|
|
@@ -30,8 +30,18 @@ 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> DELAY_UNLOAD = a("delay_unload", Long::compareTo, 300); // Paper
|
|
+ public static final TicketType<Long> DELAYED_UNLOAD = a("delayed_unload", Long::compareTo); // Tuinity - delay chunk unloads
|
|
|
|
+ // Tuinity start - delay chunk unloads
|
|
+ boolean delayUnloadViable = true;
|
|
+ static {
|
|
+ TicketType.LIGHT.delayUnloadViable = false;
|
|
+ TicketType.PLUGIN.delayUnloadViable = false;
|
|
+ TicketType.PRIORITY.delayUnloadViable = false;
|
|
+ TicketType.URGENT.delayUnloadViable = false;
|
|
+ TicketType.DELAYED_UNLOAD.delayUnloadViable = false;
|
|
+ }
|
|
+ // Tuinity end - delay chunk unloads
|
|
public static <T> TicketType<T> a(String s, Comparator<T> comparator) {
|
|
return new TicketType<>(s, comparator, 0L);
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index 05098332d83b1abfaa0a6d3bd4a9e801ea90d2ad..8484542c108b69a310b19806db5e32a04e3ee991 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -505,6 +505,7 @@ public class CraftWorld implements World {
|
|
org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
|
|
if (isChunkLoaded(x, z)) {
|
|
world.getChunkProvider().removeTicket(TicketType.PLUGIN, new ChunkCoordIntPair(x, z), 0, Unit.INSTANCE); // Paper
|
|
+ ((ChunkMapDistance)world.getChunkProvider().playerChunkMap.chunkDistanceManager).removeTickets(ChunkCoordIntPair.pair(x, z), TicketType.DELAYED_UNLOAD); // Tuinity - delay chunk unloads - let plugins override
|
|
}
|
|
|
|
return true;
|