Yatopia/patches/Tuinity/patches/server/0085-Replace-ticket-level-p...

285 lines
15 KiB
Diff
Raw Normal View History

Upstream (#469) * Updated Upstream and Sidestream(s) (Paper/Tuinity/Airplane/Purpur/Empirecraft) Upstream/An Sidestream has released updates that appears to apply and compile correctly This update has NOT been tested by YatopiaMC and as with ANY update, please do your own testing. Paper Changes: fbae9dbe0 [Auto] Updated Upstream (Bukkit/CraftBukkit) ac4a33aab [Auto] Updated Upstream (Bukkit) c1e07158b [Auto] Updated Upstream (Bukkit/CraftBukkit) 5e4b88e95 Fix dangling sout 23afda179 basic hostname validation 0fb8bdf0e Updated Upstream (Bukkit/CraftBukkit) (#5508) 88ab784da [Auto] Updated Upstream (CraftBukkit) ca7111d5f Fix PlayerItemConsumeEvent cancelling (fixes #4682) (#5383) 06fb560dc Add support for tab completing and highlighting console input from the Brigadier command tree (#5437) 0a9b89c7a Fix occasional light gen issues for neighbor blocks (#5500) a08be1ec7 [Auto] Updated Upstream (CraftBukkit) Tuinity Changes: 7d36676 Fix light source locking f1ec0c2 Add concurrency check to ProtoChunk light sources 159d146 Improvements to chunk loader system Airplane Changes: 3b3cde7 Correctly use DEAR values, fix config reloading dd60919 Updated Upstream (Tuinity) Purpur Changes: 5674cdc Updated Upstream (Paper) Empirecraft Changes: efda8c5b Updated Paper * Updated Upstream and Sidestream(s) (Paper/Tuinity) Upstream/An Sidestream has released updates that appears to apply and compile correctly This update has NOT been tested by YatopiaMC and as with ANY update, please do your own testing. Paper Changes: 39bf5b525 Update teams known as code owners Tuinity Changes: b12d0cc Replace ticket level propagator 42df8e1 Correctly handle recursion for chunkholder updates 73eb2a8 Do not copy visible chunks 8a4f3be Do not schedule poi task for each block write on chunk gen * Multithreaded Entity Tracker fixup
2021-04-21 23:26:49 +02:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sun, 21 Mar 2021 16:25:42 -0700
Subject: [PATCH] Replace ticket level propagator
Mojang's propagator is slow, and this isn't surprising
given it's built on the same utilities the vanilla light engine
is built on. The simple propagator I wrote is approximately 4x
faster when simulating player movement. For a long time timing
reports have shown this function take up significant tick, (
approx 10% or more), and async sampling data shows the level
propagation alone takes up a significant amount. So this
should help with that. A big side effect is that mid-tick
will be more effective, since more time will be allocated
to actually processing chunk tasks vs the ticket level updates.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMapDistance.java b/src/main/java/net/minecraft/server/level/ChunkMapDistance.java
index ad90735b5daa658cdd5467eadcb29183d018b1fd..a5fc023312c99e591fc269999c28a766a46f8849 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMapDistance.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMapDistance.java
@@ -8,6 +8,7 @@ import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2IntMap;
import it.unimi.dsi.fastutil.longs.Long2IntMaps;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
+import it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap; // Tuinity
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
@@ -40,7 +41,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(); final ChunkMapDistance.a getTicketTracker() { return this.ticketLevelTracker; } // Tuinity - OBFHELPER
+ //private final ChunkMapDistance.a ticketLevelTracker = new ChunkMapDistance.a(); final ChunkMapDistance.a getTicketTracker() { return this.ticketLevelTracker; } // Tuinity - OBFHELPER // Tuinity - replace ticket level propagator
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); // Tuinity - no longer used
// Paper start use a queue, but still keep unique requirement
@@ -77,7 +78,7 @@ public abstract class ChunkMapDistance {
return ticket.getTicketType() == type;
});
if (changed) {
- this.getTicketTracker().update(chunk, getLowestTicketLevel(tickets), false);
+ this.updateTicketLevel(chunk, getLowestTicketLevel(tickets)); // Tuinity - replace ticket level propagator
}
}
@@ -102,6 +103,45 @@ public abstract class ChunkMapDistance {
tickets.add(ticket); // re-add with new expire time and ticket level
}
// Tuinity end - delay chunk unloads
+ // Tuinity start - replace ticket level propagator
+ protected final Long2IntLinkedOpenHashMap ticketLevelUpdates = new Long2IntLinkedOpenHashMap() {
+ @Override
+ protected void rehash(int newN) {
+ // no downsizing allowed
+ if (newN < this.n) {
+ return;
+ }
+ super.rehash(newN);
+ }
+ };
+ protected final com.tuinity.tuinity.util.misc.Delayed8WayDistancePropagator2D ticketLevelPropagator = new com.tuinity.tuinity.util.misc.Delayed8WayDistancePropagator2D(
+ (long coordinate, byte oldLevel, byte newLevel) -> {
+ ChunkMapDistance.this.ticketLevelUpdates.putAndMoveToLast(coordinate, convertBetweenTicketLevels(newLevel));
+ }
+ );
+ // function for converting between ticket levels and propagator levels and vice versa
+ // the problem is the ticket level propagator will propagate from a set source down to zero, whereas mojang expects
+ // levels to propagate from a set value up to a maximum value. so we need to convert the levels we put into the propagator
+ // and the levels we get out of the propagator
+
+ // this maps so that GOLDEN_TICKET + 1 will be 0 in the propagator, GOLDEN_TICKET will be 1, and so on
+ // we need GOLDEN_TICKET+1 as 0 because anything >= GOLDEN_TICKET+1 should be unloaded
+ public static int convertBetweenTicketLevels(final int level) {
+ return PlayerChunkMap.GOLDEN_TICKET - level + 1;
+ }
+
+ protected final int getPropagatedTicketLevel(final long coordinate) {
+ return convertBetweenTicketLevels(this.ticketLevelPropagator.getLevel(coordinate));
+ }
+
+ protected final void updateTicketLevel(final long coordinate, final int ticketLevel) {
+ if (ticketLevel > PlayerChunkMap.GOLDEN_TICKET) {
+ this.ticketLevelPropagator.removeSource(coordinate);
+ } else {
+ this.ticketLevelPropagator.setSource(coordinate, convertBetweenTicketLevels(ticketLevel));
+ }
+ }
+ // Tuinity end - replace ticket level propagator
protected ChunkMapDistance(Executor executor, Executor executor1) {
executor1.getClass();
@@ -146,7 +186,7 @@ public abstract class ChunkMapDistance {
this.computeDelayedTicketFor(entry.getLongKey(), tempLevel[0], entry.getValue());
}
// Tuinity end - delay chunk unloads
- this.ticketLevelTracker.update(entry.getLongKey(), getLowestTicketLevel((ArraySetSorted) entry.getValue()), false);
+ this.updateTicketLevel(entry.getLongKey(), getLowestTicketLevel((ArraySetSorted) entry.getValue())); // Tuinity - replace ticket level propagator
}
if (((ArraySetSorted) entry.getValue()).isEmpty()) {
@@ -166,66 +206,99 @@ public abstract class ChunkMapDistance {
protected abstract boolean a(long i);
@Nullable
- protected abstract PlayerChunk b(long i);
+ protected abstract PlayerChunk b(long i); protected final PlayerChunk getUpdatingChunk(long i) { return this.b(i); } // Tuinity - OBFHELPER
@Nullable
- protected abstract PlayerChunk a(long i, int j, @Nullable PlayerChunk playerchunk, int k);
+ protected abstract PlayerChunk a(long i, int j, @Nullable PlayerChunk playerchunk, int k); protected final PlayerChunk updateTicketLevel(long coord, int newLevel, @Nullable PlayerChunk playerchunk, int oldLevel) { return this.a(coord, newLevel, playerchunk, oldLevel); } // Tuinity - OBFHELPER
+ protected long ticketLevelUpdateCount; // Tuinity - replace ticket level propagator
public boolean a(PlayerChunkMap playerchunkmap) {
com.tuinity.tuinity.util.TickThread.softEnsureTickThread("Cannot tick ChunkMapDistance off of the main-thread");// Tuinity
//this.f.a(); // Paper - no longer used
AsyncCatcher.catchOp("DistanceManagerTick"); // Paper
//this.g.a(); // Tuinity - no longer used
- int i = Integer.MAX_VALUE - this.ticketLevelTracker.a(Integer.MAX_VALUE);
- boolean flag = i != 0;
+ boolean flag = this.ticketLevelPropagator.propagateUpdates(); // Tuinity - replace ticket level propagator
if (flag) {
;
}
- // Paper start
- if (!this.pendingChunkUpdates.isEmpty()) {
- this.pollingPendingChunkUpdates = true; try {
- while(!this.pendingChunkUpdates.isEmpty()) {
- PlayerChunk remove = this.pendingChunkUpdates.remove();
- remove.isUpdateQueued = false;
- remove.a(playerchunkmap);
- }
- } finally { this.pollingPendingChunkUpdates = false; }
- // Paper end
- return true;
- } else {
- if (!this.l.isEmpty()) {
- LongIterator longiterator = this.l.iterator();
+ // Tuinity start - replace level propagator
+ if (!this.ticketLevelUpdates.isEmpty()) {
+ boolean oldPolling = this.pollingPendingChunkUpdates;
+ this.pollingPendingChunkUpdates = true;
+ try {
+ for (java.util.Iterator<Long2IntMap.Entry> iterator = this.ticketLevelUpdates.long2IntEntrySet().fastIterator(); iterator.hasNext(); ) {
+ Long2IntMap.Entry entry = iterator.next();
+ long key = entry.getLongKey();
+ int newLevel = entry.getIntValue();
+ PlayerChunk chunk = this.getUpdatingChunk(key);
- while (longiterator.hasNext()) {
- long j = longiterator.nextLong();
+ if (chunk == null && newLevel > PlayerChunkMap.GOLDEN_TICKET) {
+ // not loaded and it shouldn't be loaded!
+ continue;
+ }
- if (this.e(j).stream().anyMatch((ticket) -> {
- return ticket.getTicketType() == TicketType.PLAYER;
- })) {
- PlayerChunk playerchunk = playerchunkmap.getUpdatingChunk(j);
+ int currentLevel = chunk == null ? PlayerChunkMap.GOLDEN_TICKET + 1 : chunk.getTicketLevel();
- if (playerchunk == null) {
- throw new IllegalStateException();
+ if (currentLevel == newLevel) {
+ // nothing to do
+ continue;
+ }
+
+ this.updateTicketLevel(key, newLevel, chunk, currentLevel);
+ }
+
+ long recursiveCheck = ++this.ticketLevelUpdateCount;
+ while (!this.ticketLevelUpdates.isEmpty()) {
+ long key = this.ticketLevelUpdates.firstLongKey();
+ int newLevel = this.ticketLevelUpdates.removeFirstInt();
+ PlayerChunk chunk = this.getUpdatingChunk(key);
+
+ if (chunk == null) {
+ if (newLevel <= PlayerChunkMap.GOLDEN_TICKET) {
+ throw new IllegalStateException("Expected chunk holder to be created");
}
+ // not loaded and it shouldn't be loaded!
+ continue;
+ }
- CompletableFuture<Either<Chunk, PlayerChunk.Failure>> completablefuture = playerchunk.b();
+ int currentLevel = chunk.oldTicketLevel;
- completablefuture.thenAccept((either) -> {
- this.m.execute(() -> {
- this.k.a(ChunkTaskQueueSorter.a(() -> {
- }, j, false));
- });
- });
+ if (currentLevel == newLevel) {
+ // nothing to do
+ continue;
+ }
+
+ chunk.handleLevelUpdate(playerchunkmap);
+ if (recursiveCheck != this.ticketLevelUpdateCount) {
+ if (!this.ticketLevelUpdates.isEmpty()) {
+ throw new IllegalStateException("Recursive call should have processed updates");
+ }
+ break;
}
}
- this.l.clear();
+ for (;;) {
+ if (recursiveCheck != this.ticketLevelUpdateCount) {
+ break;
+ }
+ PlayerChunk pendingUpdate = this.pendingChunkUpdates.poll();
+ if (pendingUpdate == null) {
+ break;
+ }
+
+ pendingUpdate.handleLevelUpdate(playerchunkmap);
+ }
+ } finally {
+ this.pollingPendingChunkUpdates = oldPolling;
}
- return flag;
+ return true;
}
+
+ return flag;
+ // Tuinity end - replace level propagator
}
boolean pollingPendingChunkUpdates = false; // Paper
@@ -237,7 +310,7 @@ public abstract class ChunkMapDistance {
ticket1.a(this.currentTick);
if (ticket.b() < j) {
- this.ticketLevelTracker.update(i, ticket.b(), true);
+ this.updateTicketLevel(i, ticket.b()); // Tuinity - replace ticket level propagator
}
return ticket == ticket1; // CraftBukkit
@@ -263,7 +336,7 @@ public abstract class ChunkMapDistance {
}
int newLevel = getLowestTicketLevel(arraysetsorted); // Paper
- if (newLevel > oldLevel) this.ticketLevelTracker.update(i, newLevel, false); // Paper
+ if (newLevel > oldLevel) this.updateTicketLevel(i, newLevel); // Paper // Tuinity - replace ticket level propagator
return removed; // CraftBukkit
}
@@ -517,7 +590,7 @@ public abstract class ChunkMapDistance {
ArraySetSorted<Ticket<?>> tickets = entry.getValue();
if (tickets.remove(target)) {
// copied from removeTicket
- this.ticketLevelTracker.update(entry.getLongKey(), getLowestTicketLevel(tickets), false);
+ this.updateTicketLevel(entry.getLongKey(), getLowestTicketLevel(tickets)); // Tuinity - replace ticket level propagator
// can't use entry after it's removed
if (tickets.isEmpty()) {
diff --git a/src/main/java/net/minecraft/server/level/PlayerChunk.java b/src/main/java/net/minecraft/server/level/PlayerChunk.java
index 63c2f6d3312d9005ef2f821456e3946f3a9a0c3b..86f156587a0939b28c5cf6f64907255c1c4f8b35 100644
--- a/src/main/java/net/minecraft/server/level/PlayerChunk.java
+++ b/src/main/java/net/minecraft/server/level/PlayerChunk.java
@@ -55,7 +55,7 @@ public class PlayerChunk {
private volatile CompletableFuture<Either<Chunk, PlayerChunk.Failure>> entityTickingFuture; private volatile boolean isEntityTickingReady; // Paper - cache chunk ticking stage
private CompletableFuture<IChunkAccess> chunkSave;
public int oldTicketLevel;
- private int ticketLevel;
+ private int ticketLevel; public final void setTicketLevel(final int level) { this.ticketLevel = level; } // Tuinity - OBFHELPER
volatile int n; public final int getCurrentPriority() { return n; } // Paper - OBFHELPER - make volatile since this is concurrently accessed
public final ChunkCoordIntPair location; // Paper - private -> public
private boolean p;
@@ -600,6 +600,7 @@ public class PlayerChunk {
}
protected long updateCount; // Tuinity - correctly handle recursion
+ protected final void handleLevelUpdate(PlayerChunkMap playerchunkmap) { this.a(playerchunkmap); } // Tuinity - OBFHELPER
protected void a(PlayerChunkMap playerchunkmap) {
com.tuinity.tuinity.util.TickThread.ensureTickThread("Async ticket level update"); // Tuinity
long updateCount = ++this.updateCount; // Tuinity - correctly handle recursion