Re-arrange most chunk system patches to front (#8338)

* Re-arrange most chunk system patches to front

Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
This commit is contained in:
Spottedleaf 2022-09-01 09:51:59 -07:00
parent 61a8488806
commit 90da9124c5
38 changed files with 7792 additions and 795 deletions

View File

@ -9,18 +9,30 @@ sometimes it is.
This patch also prevents the saving/unloading of POI data when This patch also prevents the saving/unloading of POI data when
world saving is disabled. world saving is disabled.
diff --git a/src/main/java/net/minecraft/server/ChunkSystem.java b/src/main/java/net/minecraft/server/ChunkSystem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/ChunkSystem.java
+++ b/src/main/java/net/minecraft/server/ChunkSystem.java
@@ -0,0 +0,0 @@ public final class ChunkSystem {
for (int index = 0, len = chunkMap.regionManagers.size(); index < len; ++index) {
chunkMap.regionManagers.get(index).addChunk(holder.pos.x, holder.pos.z);
}
+ chunkMap.getPoiManager().dequeueUnload(holder.pos.longKey); // Paper - unload POI data
}
public static void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
@@ -0,0 +0,0 @@ public final class ChunkSystem {
for (int index = 0, len = chunkMap.regionManagers.size(); index < len; ++index) {
chunkMap.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
}
+ chunkMap.getPoiManager().queueUnload(holder.pos.longKey, MinecraftServer.currentTickLong + 1); // Paper - unload POI data
}
public static void onChunkBorder(LevelChunk chunk, ChunkHolder holder) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
// Paper end
}
+ this.getPoiManager().dequeueUnload(holder.pos.longKey); // Paper - unload POI data
this.updatingChunks.queueUpdate(pos, holder); // Paper - Don't copy
this.modified = true;
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
private void processUnloads(BooleanSupplier shouldKeepTicking) { private void processUnloads(BooleanSupplier shouldKeepTicking) {
@ -30,22 +42,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
long j = longiterator.nextLong(); long j = longiterator.nextLong();
ChunkHolder playerchunk = this.updatingChunks.queueRemove(j); // Paper - Don't copy ChunkHolder playerchunk = this.updatingChunks.queueRemove(j); // Paper - Don't copy
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
}
// Paper end
+ this.getPoiManager().queueUnload(holder.pos.longKey, MinecraftServer.currentTickLong + 1); // Paper - unload POI data
if (ichunkaccess instanceof LevelChunk) {
((LevelChunk) ichunkaccess).setLoaded(false);
}
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
for (int index = 0, len = this.regionManagers.size(); index < len; ++index) {
this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
}
+ this.getPoiManager().queueUnload(holder.pos.longKey, MinecraftServer.currentTickLong + 1); // Paper - unload POI data
} // Paper end
} finally { this.unloadingPlayerChunk = unloadingBefore; } // Paper - do not allow ticket level changes while unloading chunks
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} }
this.poiManager.loadInData(pos, chunkHolder.poiData); this.poiManager.loadInData(pos, chunkHolder.poiData);

View File

@ -32,9 +32,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/server/players/PlayerList.java --- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -0,0 +0,0 @@ public abstract class PlayerList { @@ -0,0 +0,0 @@ public abstract class PlayerList {
}
public void placeNewPlayer(Connection connection, ServerPlayer player) { public void placeNewPlayer(Connection connection, ServerPlayer player) {
player.isRealPlayer = true; // Paper - Chunk priority
+ player.loginTime = System.currentTimeMillis(); // Paper + player.loginTime = System.currentTimeMillis(); // Paper
GameProfile gameprofile = player.getGameProfile(); GameProfile gameprofile = player.getGameProfile();
GameProfileCache usercache = this.server.getProfileCache(); GameProfileCache usercache = this.server.getProfileCache();

View File

@ -122,6 +122,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - Different message for short timeout + // Paper end - Different message for short timeout
log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "------------------------------" );
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "------------------------------" );
// //

View File

@ -112,10 +112,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
return true; return true;
// Paper end // Paper end
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
net.minecraft.server.ChunkSystem.scheduleChunkLoad(this.getHandle(), x, z, gen, ChunkStatus.FULL, true, priority, (c) -> {
return this.world.getChunkSource().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> {
net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) either.left().orElse(null); net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)c;
+ if (chunk != null) addTicket(x, z); // Paper + if (chunk != null) addTicket(x, z); // Paper
return java.util.concurrent.CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk()); ret.complete(chunk == null ? null : chunk.getBukkitChunk());
}, net.minecraft.server.MinecraftServer.getServer()); });
} });

View File

@ -85,4 +85,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end + // Paper end
log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "------------------------------" );
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper

View File

@ -2703,9 +2703,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private static final Random rand = new Random(); private static final Random rand = new Random();
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return this.spigot;
return ret;
} }
// Spigot end
+ +
+ // Paper start - implement pointers + // Paper start - implement pointers
+ @Override + @Override
@ -2719,7 +2719,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ +
+ return this.adventure$pointers; + return this.adventure$pointers;
+ } + }
+ // Paper end // Paper end
} }
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644

View File

@ -16,7 +16,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ this.getProfileCache().save(false); // Paper + this.getProfileCache().save(false); // Paper
} }
// Spigot end // Spigot end
com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.close(true, true); // Paper
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java

View File

@ -24,9 +24,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private boolean addEntity(T entity, boolean existing) { private boolean addEntity(T entity, boolean existing) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity add"); // Paper + org.spigotmc.AsyncCatcher.catchOp("Entity add"); // Paper
if (!this.addEntityUuid(entity)) { // Paper start - chunk system hooks
return false; if (existing) {
} else { // I don't want to know why this is a generic type.
@@ -0,0 +0,0 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A @@ -0,0 +0,0 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
} }

View File

@ -37,6 +37,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]))); // Paper + server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]))); // Paper
return; return;
} }
// Paper start
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
}
// Paper end
// CraftBukkit end // CraftBukkit end
+ // Paper start - async tab completion + // Paper start - async tab completion
+ TAB_COMPLETE_EXECUTOR.execute(() -> { + TAB_COMPLETE_EXECUTOR.execute(() -> {

View File

@ -2282,7 +2282,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/server/MinecraftServer.java --- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa @@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.getProfileCache().save(false); // Paper this.getProfileCache().save();
} }
// Spigot end // Spigot end
- -
@ -2302,6 +2302,31 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
this.poiManager.close(); this.poiManager.close();
} finally { } finally {
super.close(); super.close();
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
protected void saveAllChunks(boolean flush) {
+ // Paper start - do not overload I/O threads with too much work when saving
+ int[] saved = new int[1];
+ int maxAsyncSaves = 50;
+ Runnable onChunkSave = () -> {
+ if (++saved[0] >= maxAsyncSaves) {
+ saved[0] = 0;
+ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.flush();
+ }
+ };
+ // Paper end - do not overload I/O threads with too much work when saving
if (flush) {
List<ChunkHolder> list = (List) net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); // Paper
MutableBoolean mutableboolean = new MutableBoolean();
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}).filter((ichunkaccess) -> {
return ichunkaccess instanceof ImposterProtoChunk || ichunkaccess instanceof LevelChunk;
}).filter(this::save).forEach((ichunkaccess) -> {
+ onChunkSave.run(); // Paper - do not overload I/O threads with too much work when saving
mutableboolean.setTrue();
});
} while (mutableboolean.isTrue());
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.processUnloads(() -> { this.processUnloads(() -> {
return true; return true;
@ -2310,7 +2335,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ //this.flushWorker(); // Paper - nuke IOWorker + //this.flushWorker(); // Paper - nuke IOWorker
+ this.level.asyncChunkTaskManager.flush(); // Paper - flush to preserve behavior compat with pre-async behaviour + this.level.asyncChunkTaskManager.flush(); // Paper - flush to preserve behavior compat with pre-async behaviour
} else { } else {
this.visibleChunkMap.values().forEach(this::saveChunkIfNeeded); net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).forEach(this::saveChunkIfNeeded);
} }
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
protected void tick(BooleanSupplier shouldKeepTicking) { protected void tick(BooleanSupplier shouldKeepTicking) {
@ -2824,7 +2849,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic @@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]))); // Paper this.disconnect(Component.translatable("disconnect.spam"));
return; return;
} }
+ // Paper start + // Paper start
@ -2835,8 +2860,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ // Paper end + // Paper end
// CraftBukkit end // CraftBukkit end
// Paper start - async tab completion StringReader stringreader = new StringReader(packet.getCommand());
TAB_COMPLETE_EXECUTOR.execute(() -> {
diff --git a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java diff --git a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java --- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
@ -3365,7 +3390,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end + // Paper end
return regionfile; return regionfile;
} else { } else {
if (this.regionCache.size() >= io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize) { // Paper - configurable if (this.regionCache.size() >= 256) {
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { @@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
RegionFile regionfile1 = new RegionFile(path1, this.folder, this.sync); RegionFile regionfile1 = new RegionFile(path1, this.folder, this.sync);
@ -3410,13 +3435,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- RegionFile regionfile = this.getRegionFile(pos, false); // CraftBukkit - RegionFile regionfile = this.getRegionFile(pos, false); // CraftBukkit
+ RegionFile regionfile = this.getRegionFile(pos, false, true); // CraftBukkit // Paper + RegionFile regionfile = this.getRegionFile(pos, false, true); // CraftBukkit // Paper
+ try { // Paper + try { // Paper
int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper
if (nbt == null) { if (nbt == null) {
regionfile.clear(pos);
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { @@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
net.minecraft.server.MinecraftServer.LOGGER.error("Failed to save chunk", laste); }
} }
// Paper end
+ } finally { // Paper start + } finally { // Paper start
+ regionfile.fileLock.unlock(); + regionfile.fileLock.unlock();
+ } // Paper end + } // Paper end
@ -3554,95 +3579,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
} }
+ // Paper end + // Paper end
} }
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
public DragonBattle getEnderDragonBattle() {
return (this.getHandle().dragonFight() == null) ? null : new CraftDragonBattle(this.getHandle().dragonFight());
}
+ // Paper start
+ @Override
+ public java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) {
+ if (Bukkit.isPrimaryThread()) {
+ net.minecraft.world.level.chunk.LevelChunk immediate = this.world.getChunkSource().getChunkAtIfLoadedImmediately(x, z);
+ if (immediate != null) {
+ return java.util.concurrent.CompletableFuture.completedFuture(immediate.getBukkitChunk());
+ }
+ } else {
+ java.util.concurrent.CompletableFuture<Chunk> future = new java.util.concurrent.CompletableFuture<Chunk>();
+ world.getServer().execute(() -> {
+ getChunkAtAsync(x, z, gen, urgent).whenComplete((chunk, err) -> {
+ if (err != null) {
+ future.completeExceptionally(err);
+ } else {
+ future.complete(chunk);
+ }
+ });
+ });
+ return future;
+ }
+
+ return this.world.getChunkSource().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> {
+ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) either.left().orElse(null);
+ return java.util.concurrent.CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk());
+ }, net.minecraft.server.MinecraftServer.getServer());
+ }
+ // Paper end
@Override
public PersistentDataContainer getPersistentDataContainer() {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -0,0 +0,0 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
this.entity.setYHeadRot(yaw);
}
+ // Paper start
+ @Override
+ public java.util.concurrent.CompletableFuture<Boolean> teleportAsync(Location location, TeleportCause cause) {
+ Preconditions.checkArgument(location != null, "location");
+ location.checkFinite();
+ Location locationClone = location.clone(); // clone so we don't need to worry about mutations after this call.
+
+ net.minecraft.server.level.ServerLevel world = ((CraftWorld)locationClone.getWorld()).getHandle();
+ java.util.concurrent.CompletableFuture<Boolean> ret = new java.util.concurrent.CompletableFuture<>();
+
+ world.loadChunksForMoveAsync(getHandle().getBoundingBoxAt(locationClone.getX(), locationClone.getY(), locationClone.getZ()), location.getX(), location.getZ(), (list) -> {
+ net.minecraft.server.level.ServerChunkCache chunkProviderServer = world.getChunkSource();
+ for (net.minecraft.world.level.chunk.ChunkAccess chunk : list) {
+ chunkProviderServer.addTicketAtLevel(net.minecraft.server.level.TicketType.POST_TELEPORT, chunk.getPos(), 33, CraftEntity.this.getEntityId());
+ }
+ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> {
+ try {
+ ret.complete(CraftEntity.this.teleport(locationClone, cause) ? Boolean.TRUE : Boolean.FALSE);
+ } catch (Throwable throwable) {
+ if (throwable instanceof ThreadDeath) {
+ throw (ThreadDeath)throwable;
+ }
+ ret.completeExceptionally(throwable);
+ }
+ });
+ });
+
+ return ret;
+ }
+ // Paper end
+
@Override
public boolean teleport(Location location) {
return this.teleport(location, TeleportCause.PLUGIN);
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java --- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java +++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -0,0 +0,0 @@ public class WatchdogThread extends Thread @@ -0,0 +0,0 @@ public class WatchdogThread extends Thread
// Paper end - Different message for short timeout //
log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "------------------------------" );
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Spigot!):" );
+ com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper + com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "------------------------------" );

View File

@ -564,11 +564,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+import com.destroystokyo.paper.profile.CraftPlayerProfile; +import com.destroystokyo.paper.profile.CraftPlayerProfile;
+import com.destroystokyo.paper.profile.PlayerProfile; +import com.destroystokyo.paper.profile.PlayerProfile;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; import com.google.gson.JsonArray;
import java.lang.ref.Cleaner; import com.google.gson.JsonObject;
@@ -0,0 +0,0 @@ import net.minecraft.world.level.ChunkPos; @@ -0,0 +0,0 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.Level; import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang.exception.ExceptionUtils;
+import com.mojang.authlib.GameProfile; +import com.mojang.authlib.GameProfile;
import org.bukkit.Location; import org.bukkit.Location;

View File

@ -22,9 +22,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
+import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ChunkHolder;
import net.minecraft.world.entity.Entity; import net.minecraft.server.level.ChunkMap;
import net.minecraft.world.level.ChunkPos; import net.minecraft.server.level.DistanceManager;
@@ -0,0 +0,0 @@ public final class MCUtil { @@ -0,0 +0,0 @@ public final class MCUtil {
} }
} }
@ -44,8 +44,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return null; + return null;
+ } + }
+ +
public static int getTicketLevelFor(net.minecraft.world.level.chunk.ChunkStatus status) { public static ChunkStatus getChunkStatus(ChunkHolder chunk) {
return net.minecraft.server.level.ChunkMap.MAX_VIEW_DISTANCE + net.minecraft.world.level.chunk.ChunkStatus.getDistance(status); return chunk.getChunkHolderStatus();
} }
diff --git a/src/main/java/net/minecraft/world/level/BaseCommandBlock.java b/src/main/java/net/minecraft/world/level/BaseCommandBlock.java diff --git a/src/main/java/net/minecraft/world/level/BaseCommandBlock.java b/src/main/java/net/minecraft/world/level/BaseCommandBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644

View File

@ -23,29 +23,29 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java --- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { @@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
protected void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException { protected void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException {
RegionFile regionfile = this.getRegionFile(pos, false); // CraftBukkit RegionFile regionfile = this.getRegionFile(pos, false, true); // CraftBukkit // Paper
try { // Paper
+ int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper + int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper
if (nbt == null) { if (nbt == null) {
regionfile.clear(pos); regionfile.clear(pos);
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { @@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
dataoutputstream.close();
} }
} }
+ // Paper start
+ // Paper start + return;
+ return;
+ } catch (Exception ex) { + } catch (Exception ex) {
+ laste = ex; + laste = ex;
+ } + }
+ } + }
+
+ if (laste != null) { + if (laste != null) {
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(laste); + com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(laste);
+ net.minecraft.server.MinecraftServer.LOGGER.error("Failed to save chunk", laste); + net.minecraft.server.MinecraftServer.LOGGER.error("Failed to save chunk " + pos, laste);
+ } + }
+ // Paper end + // Paper end
} } finally { // Paper start
regionfile.fileLock.unlock();
public void close() throws IOException { } // Paper end

View File

@ -148,14 +148,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ int ticking = 0; + int ticking = 0;
+ int entityTicking = 0; + int entityTicking = 0;
+ +
+ for (final ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunkMap.values()) { + for (final ChunkHolder chunk : net.minecraft.server.ChunkSystem.getVisibleChunkHolders(world)) {
+ if (chunk.getFullChunkNowUnchecked() == null) { + if (chunk.getFullChunkNowUnchecked() == null) {
+ continue; + continue;
+ } + }
+ +
+ ++total; + ++total;
+ +
+ ChunkHolder.FullChunkStatus state = ChunkHolder.getFullChunkStatus(chunk.getTicketLevel()); + ChunkHolder.FullChunkStatus state = chunk.getFullStatus();
+ +
+ switch (state) { + switch (state) {
+ case INACCESSIBLE -> ++inactive; + case INACCESSIBLE -> ++inactive;
@ -226,14 +226,22 @@ diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java --- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java +++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -0,0 +0,0 @@ import net.minecraft.core.BlockPos; @@ -0,0 +0,0 @@
package net.minecraft.server;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.internal.Streams;
+import com.google.gson.stream.JsonWriter;
+import com.mojang.datafixers.util.Either;
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet;
import java.lang.ref.Cleaner;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
+import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.ChunkHolder;
+import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ChunkMap;
+import net.minecraft.server.level.DistanceManager; +import net.minecraft.server.level.DistanceManager;
+import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.server.level.Ticket; +import net.minecraft.server.level.Ticket;
@ -244,22 +252,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang.exception.ExceptionUtils;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.internal.Streams;
+import com.google.gson.stream.JsonWriter;
import com.mojang.authlib.GameProfile;
+import com.mojang.datafixers.util.Either;
+import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.CraftWorld;
@@ -0,0 +0,0 @@ import org.spigotmc.AsyncCatcher; @@ -0,0 +0,0 @@ import org.spigotmc.AsyncCatcher;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
+import java.io.*; +import java.io.*;
+import java.util.ArrayList; +import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
+import java.util.Set; +import java.util.Set;
@ -267,19 +267,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
@@ -0,0 +0,0 @@ public final class MCUtil { @@ -0,0 +0,0 @@ public final class MCUtil {
return null; }
} }
+ public static ChunkStatus getChunkStatus(ChunkHolder chunk) { + public static ChunkStatus getChunkStatus(ChunkHolder chunk) {
+ List<ChunkStatus> statuses = net.minecraft.server.level.ServerChunkCache.CHUNK_STATUSES; + return chunk.getChunkHolderStatus();
+ for (int i = statuses.size() - 1; i >= 0; --i) {
+ ChunkStatus curr = statuses.get(i);
+ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = chunk.getFutureIfPresentUnchecked(curr);
+ if (future != ChunkHolder.UNLOADED_CHUNK_FUTURE) {
+ return curr;
+ }
+ }
+ return null; // unloaded
+ } + }
+ +
+ public static void dumpChunks(File file) throws IOException { + public static void dumpChunks(File file) throws IOException {
@ -337,9 +329,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ +
+ ServerLevel world = ((org.bukkit.craftbukkit.CraftWorld)bukkitWorld).getHandle(); + ServerLevel world = ((org.bukkit.craftbukkit.CraftWorld)bukkitWorld).getHandle();
+ ChunkMap chunkMap = world.getChunkSource().chunkMap; + ChunkMap chunkMap = world.getChunkSource().chunkMap;
+ Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunks = chunkMap.visibleChunkMap;
+ DistanceManager chunkMapDistance = chunkMap.distanceManager; + DistanceManager chunkMapDistance = chunkMap.distanceManager;
+ List<ChunkHolder> allChunks = new ArrayList<>(visibleChunks.values()); + List<ChunkHolder> allChunks = net.minecraft.server.ChunkSystem.getVisibleChunkHolders(world);
+ List<ServerPlayer> players = world.players; + List<ServerPlayer> players = world.players;
+ +
+ int fullLoadedChunks = 0; + int fullLoadedChunks = 0;
@ -362,7 +353,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ worldData.addProperty("view-distance", world.spigotConfig.viewDistance); + worldData.addProperty("view-distance", world.spigotConfig.viewDistance);
+ worldData.addProperty("keep-spawn-loaded", world.keepSpawnInMemory); + worldData.addProperty("keep-spawn-loaded", world.keepSpawnInMemory);
+ worldData.addProperty("keep-spawn-loaded-range", world.paperConfig().spawn.keepSpawnLoadedRange * 16); + worldData.addProperty("keep-spawn-loaded-range", world.paperConfig().spawn.keepSpawnLoadedRange * 16);
+ worldData.addProperty("visible-chunk-count", visibleChunks.size()); + worldData.addProperty("visible-chunk-count", allChunks.size());
+ worldData.addProperty("loaded-chunk-count", chunkMap.entitiesInLevel.size()); + worldData.addProperty("loaded-chunk-count", chunkMap.entitiesInLevel.size());
+ worldData.addProperty("verified-fully-loaded-chunks", fullLoadedChunks); + worldData.addProperty("verified-fully-loaded-chunks", fullLoadedChunks);
+ +
@ -431,7 +422,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ +
+ String fileData = stringWriter.toString(); + String fileData = stringWriter.toString();
+ +
+ try (PrintStream out = new PrintStream(new FileOutputStream(file), false, "UTF-8")) { + try (PrintStream out = new PrintStream(new FileOutputStream(file), false, StandardCharsets.UTF_8)) {
+ out.print(fileData); + out.print(fileData);
+ } + }
+ } + }

View File

@ -15,7 +15,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ boolean isUpdateQueued = false; // Paper + boolean isUpdateQueued = false; // Paper
private final ChunkMap chunkMap; // Paper private final ChunkMap chunkMap; // Paper
public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) { // Paper start
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java --- a/src/main/java/net/minecraft/server/level/DistanceManager.java

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ boolean unloadingPlayerChunk = false; // Paper - do not allow ticket level changes while unloading chunks + boolean unloadingPlayerChunk = false; // Paper - do not allow ticket level changes while unloading chunks
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) { public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
this.visibleChunkMap = this.updatingChunkMap.clone(); // Paper - don't copy
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@Nullable @Nullable
@ -41,9 +41,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
boolean removed; boolean removed;
if ((removed = this.pendingUnloads.remove(pos, holder)) && ichunkaccess != null) { if ((removed = this.pendingUnloads.remove(pos, holder)) && ichunkaccess != null) {
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z); } else if (removed) { // Paper start
} net.minecraft.server.ChunkSystem.onChunkHolderDelete(this.level, holder);
} // Paper end } // Paper end
+ } finally { this.unloadingPlayerChunk = unloadingBefore; } // Paper - do not allow ticket level changes while unloading chunks + } finally { this.unloadingPlayerChunk = unloadingBefore; } // Paper - do not allow ticket level changes while unloading chunks
} }

View File

@ -8,32 +8,39 @@ tickDistanceManager call can take up quite a bit in
the function. I saw approximately 1/3rd of the function the function. I saw approximately 1/3rd of the function
on the copy. on the copy.
diff --git a/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java diff --git a/src/main/java/net/minecraft/server/ChunkSystem.java b/src/main/java/net/minecraft/server/ChunkSystem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java --- a/src/main/java/net/minecraft/server/ChunkSystem.java
+++ b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java +++ b/src/main/java/net/minecraft/server/ChunkSystem.java
@@ -0,0 +0,0 @@ public final class ChunkDebugCommand implements PaperSubcommand { @@ -0,0 +0,0 @@ public final class ChunkSystem {
int ticking = 0; }
int entityTicking = 0;
- for (final ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunkMap.values()) { public static List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
+ for (final ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunks.getUpdatingMap().values()) { // Paper - change updating chunks map - return new ArrayList<>(level.chunkSource.chunkMap.visibleChunkMap.values());
if (chunk.getFullChunkNowUnchecked() == null) { + if (Bukkit.isPrimaryThread()) {
continue; + return level.chunkSource.chunkMap.updatingChunks.getVisibleValuesCopy();
} + }
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java + synchronized (level.chunkSource.chunkMap.updatingChunks) {
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 + return level.chunkSource.chunkMap.updatingChunks.getVisibleValuesCopy();
--- a/src/main/java/net/minecraft/server/MCUtil.java + }
+++ b/src/main/java/net/minecraft/server/MCUtil.java }
@@ -0,0 +0,0 @@ public final class MCUtil {
ServerLevel world = ((org.bukkit.craftbukkit.CraftWorld)bukkitWorld).getHandle(); public static List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
ChunkMap chunkMap = world.getChunkSource().chunkMap; - return new ArrayList<>(level.chunkSource.chunkMap.updatingChunkMap.values());
- Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunks = chunkMap.visibleChunkMap; + return level.chunkSource.chunkMap.updatingChunks.getUpdatingValuesCopy();
+ Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunks = chunkMap.updatingChunks.getVisibleMap(); // Paper }
DistanceManager chunkMapDistance = chunkMap.distanceManager;
List<ChunkHolder> allChunks = new ArrayList<>(visibleChunks.values()); public static int getVisibleChunkHolderCount(final ServerLevel level) {
List<ServerPlayer> players = world.players; - return level.chunkSource.chunkMap.visibleChunkMap.size();
+ return level.chunkSource.chunkMap.updatingChunks.getVisibleMap().size();
}
public static int getUpdatingChunkHolderCount(final ServerLevel level) {
- return level.chunkSource.chunkMap.updatingChunkMap.size();
+ return level.chunkSource.chunkMap.updatingChunks.getUpdatingMap().size();
}
public static boolean hasAnyChunkHolders(final ServerLevel level) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
@ -53,7 +60,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
public final LongSet entitiesInLevel; public final LongSet entitiesInLevel;
public final ServerLevel level; public final ServerLevel level;
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
boolean unloadingPlayerChunk = false; // Paper - do not allow ticket level changes while unloading chunks
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) { public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
- this.visibleChunkMap = this.updatingChunkMap.clone(); - this.visibleChunkMap = this.updatingChunkMap.clone();
@ -82,21 +89,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
protected IntSupplier getChunkQueueLevel(long pos) { protected IntSupplier getChunkQueueLevel(long pos) {
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}; // Paper start
holder.onChunkAdd();
stringbuilder.append("Updating:").append(System.lineSeparator()); // Paper end
- this.updatingChunkMap.values().forEach(consumer);
+ this.updatingChunks.getUpdatingValuesCopy().forEach(consumer); // Paper
stringbuilder.append("Visible:").append(System.lineSeparator());
- this.visibleChunkMap.values().forEach(consumer);
+ this.updatingChunks.getVisibleValuesCopy().forEach(consumer); // Paper
CrashReport crashreport = CrashReport.forThrowable(exception, "Chunk loading");
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Chunk loading");
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end
}
- this.updatingChunkMap.put(pos, holder); - this.updatingChunkMap.put(pos, holder);
+ this.updatingChunks.queueUpdate(pos, holder); // Paper - Don't copy + this.updatingChunks.queueUpdate(pos, holder); // Paper - Don't copy
this.modified = true; this.modified = true;
@ -104,32 +99,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
protected void saveAllChunks(boolean flush) {
if (flush) {
- List<ChunkHolder> list = (List) this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList());
+ List<ChunkHolder> list = (List) this.updatingChunks.getVisibleValuesCopy().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); // Paper
MutableBoolean mutableboolean = new MutableBoolean();
do {
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
//this.flushWorker(); // Paper - nuke IOWorker
this.level.asyncChunkTaskManager.flush(); // Paper - flush to preserve behavior compat with pre-async behaviour
} else {
- this.visibleChunkMap.values().forEach(this::saveChunkIfNeeded);
+ this.updatingChunks.getVisibleValuesCopy().forEach(this::saveChunkIfNeeded); // Paper
}
}
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public boolean hasWork() {
- return this.lightEngine.hasLightWork() || !this.pendingUnloads.isEmpty() || !this.updatingChunkMap.isEmpty() || this.poiManager.hasWork() || !this.toDrop.isEmpty() || !this.unloadQueue.isEmpty() || this.queueSorter.hasWork() || this.distanceManager.hasTickets();
+ return this.lightEngine.hasLightWork() || !this.pendingUnloads.isEmpty() || !this.updatingChunks.getUpdatingValuesCopy().isEmpty() || this.poiManager.hasWork() || !this.toDrop.isEmpty() || !this.unloadQueue.isEmpty() || this.queueSorter.hasWork() || this.distanceManager.hasTickets(); // Paper
}
private void processUnloads(BooleanSupplier shouldKeepTicking) {
LongIterator longiterator = this.toDrop.iterator();
for (int i = 0; longiterator.hasNext() && (shouldKeepTicking.getAsBoolean() || i < 200 || this.toDrop.size() > 2000); longiterator.remove()) { for (int i = 0; longiterator.hasNext() && (shouldKeepTicking.getAsBoolean() || i < 200 || this.toDrop.size() > 2000); longiterator.remove()) {
long j = longiterator.nextLong(); long j = longiterator.nextLong();
- ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j); - ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j);
@ -151,88 +120,3 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
this.modified = false; this.modified = false;
return true; return true;
} }
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.viewDistance = j;
this.distanceManager.updatePlayerTickets(this.viewDistance + 1);
- ObjectIterator objectiterator = this.updatingChunkMap.values().iterator();
+ Iterator objectiterator = this.updatingChunks.getVisibleValuesCopy().iterator(); // Paper
while (objectiterator.hasNext()) {
ChunkHolder playerchunk = (ChunkHolder) objectiterator.next();
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public int size() {
- return this.visibleChunkMap.size();
+ return this.updatingChunks.getVisibleMap().size(); // Paper - Don't copy
}
public DistanceManager getDistanceManager() {
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
protected Iterable<ChunkHolder> getChunks() {
- return Iterables.unmodifiableIterable(this.visibleChunkMap.values());
+ return Iterables.unmodifiableIterable(this.updatingChunks.getVisibleValuesCopy()); // Paper
}
void dumpChunks(Writer writer) throws IOException {
CsvOutput csvwriter = CsvOutput.builder().addColumn("x").addColumn("z").addColumn("level").addColumn("in_memory").addColumn("status").addColumn("full_status").addColumn("accessible_ready").addColumn("ticking_ready").addColumn("entity_ticking_ready").addColumn("ticket").addColumn("spawning").addColumn("block_entity_count").addColumn("ticking_ticket").addColumn("ticking_level").addColumn("block_ticks").addColumn("fluid_ticks").build(writer);
TickingTracker tickingtracker = this.distanceManager.tickingTracker();
- ObjectBidirectionalIterator objectbidirectionaliterator = this.visibleChunkMap.long2ObjectEntrySet().iterator();
+ ObjectBidirectionalIterator objectbidirectionaliterator = this.updatingChunks.getVisibleMap().clone().long2ObjectEntrySet().fastIterator(); // Paper
while (objectbidirectionaliterator.hasNext()) {
Entry<ChunkHolder> entry = (Entry) objectbidirectionaliterator.next();
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public int getTileEntityCount() {
// We don't use the full world tile entity list, so we must iterate chunks
- Long2ObjectLinkedOpenHashMap<ChunkHolder> chunks = world.getChunkSource().chunkMap.visibleChunkMap;
+ Long2ObjectLinkedOpenHashMap<ChunkHolder> chunks = world.getChunkSource().chunkMap.updatingChunks.getVisibleMap(); // Paper - change updating chunks map
int size = 0;
for (ChunkHolder playerchunk : chunks.values()) {
net.minecraft.world.level.chunk.LevelChunk chunk = playerchunk.getTickingChunk();
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
public int getChunkCount() {
int ret = 0;
- for (ChunkHolder chunkHolder : world.getChunkSource().chunkMap.visibleChunkMap.values()) {
+ for (ChunkHolder chunkHolder : world.getChunkSource().chunkMap.updatingChunks.getVisibleMap().values()) { // Paper - change updating chunks map
if (chunkHolder.getTickingChunk() != null) {
++ret;
}
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public Chunk[] getLoadedChunks() {
- Long2ObjectLinkedOpenHashMap<ChunkHolder> chunks = this.world.getChunkSource().chunkMap.visibleChunkMap;
+ // Paper start
+ if (Thread.currentThread() != world.getLevel().thread) {
+ // Paper start - change updating chunks map
+ Long2ObjectLinkedOpenHashMap<ChunkHolder> chunks;
+ synchronized (world.getChunkSource().chunkMap.updatingChunks) {
+ chunks = world.getChunkSource().chunkMap.updatingChunks.getVisibleMap().clone();
+ }
+ return chunks.values().stream().map(ChunkHolder::getFullChunkNow).filter(Objects::nonNull).map(net.minecraft.world.level.chunk.LevelChunk::getBukkitChunk).toArray(Chunk[]::new);
+ // Paper end - change updating chunks map
+ }
+ // Paper end
+ Long2ObjectLinkedOpenHashMap<ChunkHolder> chunks = this.world.getChunkSource().chunkMap.updatingChunks.getVisibleMap(); // Paper - change updating chunks map
return chunks.values().stream().map(ChunkHolder::getFullChunkNow).filter(Objects::nonNull).map(net.minecraft.world.level.chunk.LevelChunk::getBukkitChunk).toArray(Chunk[]::new);
}
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean refreshChunk(int x, int z) {
- ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.visibleChunkMap.get(ChunkPos.asLong(x, z));
+ ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.updatingChunks.getVisibleMap().get(ChunkPos.asLong(x, z));
if (playerChunk == null) return false;
playerChunk.getTickingChunkFuture().thenAccept(either -> {

View File

@ -1,42 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 16 Oct 2021 01:36:00 -0700
Subject: [PATCH] Do not overload I/O threads with chunk data while flush
saving
If the chunk count is high, then the memory used by the
chunks adds up and could cause problems. By flushing
every so many chunks, the server will not become
stressed for memory. It will also not increase the total
time to save, as flush saving performs a full flush at
the end anyways.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end
protected void saveAllChunks(boolean flush) {
+ // Paper start - do not overload I/O threads with too much work when saving
+ int[] saved = new int[1];
+ int maxAsyncSaves = 50;
+ Runnable onChunkSave = () -> {
+ if (++saved[0] >= maxAsyncSaves) {
+ saved[0] = 0;
+ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.flush();
+ }
+ };
+ // Paper end - do not overload I/O threads with too much work when saving
if (flush) {
List<ChunkHolder> list = (List) this.updatingChunks.getVisibleValuesCopy().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); // Paper
MutableBoolean mutableboolean = new MutableBoolean();
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}).filter((ichunkaccess) -> {
return ichunkaccess instanceof ImposterProtoChunk || ichunkaccess instanceof LevelChunk;
}).filter(this::save).forEach((ichunkaccess) -> {
+ onChunkSave.run(); // Paper - do not overload I/O threads with too much work when saving
mutableboolean.setTrue();
});
} while (mutableboolean.isTrue());

View File

@ -32,6 +32,21 @@ But for those who are ok with leaving this inconsistent behavior, you may use WA
It is recommended you regenerate the entities, as these were legit entities, and deserve your love. It is recommended you regenerate the entities, as these were legit entities, and deserve your love.
diff --git a/src/main/java/net/minecraft/server/ChunkSystem.java b/src/main/java/net/minecraft/server/ChunkSystem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/ChunkSystem.java
+++ b/src/main/java/net/minecraft/server/ChunkSystem.java
@@ -0,0 +0,0 @@ public final class ChunkSystem {
}
public static void onEntityPreAdd(final ServerLevel level, final Entity entity) {
-
+ if (net.minecraft.server.level.ChunkMap.checkDupeUUID(level, entity)) {
+ return;
+ }
}
public static void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
@ -49,22 +64,27 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
} }
+ // Paper start + // Paper start
+ private static void checkDupeUUID(ServerLevel level, Entity entity) { + // rets true if to prevent the entity from being added
+ public static boolean checkDupeUUID(ServerLevel level, Entity entity) {
+ io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode mode = level.paperConfig().entities.spawning.duplicateUuid.mode; + io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode mode = level.paperConfig().entities.spawning.duplicateUuid.mode;
+ if (mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.WARN + if (mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.WARN
+ && mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.DELETE + && mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.DELETE
+ && mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.SAFE_REGEN) { + && mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.SAFE_REGEN) {
+ return; + return false;
+ } + }
+ Entity other = level.getEntity(entity.getUUID()); + Entity other = level.getEntity(entity.getUUID());
+ +
+ if (other == null || other == entity) {
+ return false;
+ }
+
+ if (mode == io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.SAFE_REGEN && other != null && !other.isRemoved() + if (mode == io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.SAFE_REGEN && other != null && !other.isRemoved()
+ && Objects.equals(other.getEncodeId(), entity.getEncodeId()) + && Objects.equals(other.getEncodeId(), entity.getEncodeId())
+ && entity.getBukkitEntity().getLocation().distance(other.getBukkitEntity().getLocation()) < level.paperConfig().entities.spawning.duplicateUuid.safeRegenDeleteRange + && entity.getBukkitEntity().getLocation().distance(other.getBukkitEntity().getLocation()) < level.paperConfig().entities.spawning.duplicateUuid.safeRegenDeleteRange
+ ) { + ) {
+ if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + " because it was near the duplicate and likely an actual duplicate. See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about."); + if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + " because it was near the duplicate and likely an actual duplicate. See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
+ entity.discard(); + entity.discard();
+ return; + return true;
+ } + }
+ if (other != null && !other.isRemoved()) { + if (other != null && !other.isRemoved()) {
+ switch (mode) { + switch (mode) {
@ -76,13 +96,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ case DELETE: { + case DELETE: {
+ if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about."); + if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
+ entity.discard(); + entity.discard();
+ break; + return true;
+ } + }
+ default: + default:
+ if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", doing nothing to " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about."); + if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", doing nothing to " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
+ break; + break;
+ } + }
+ } + }
+ return false;
+ } + }
+ // Paper end + // Paper end
public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> prepareTickingChunk(ChunkHolder holder) { public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> prepareTickingChunk(ChunkHolder holder) {

View File

@ -136,11 +136,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -0,0 +0,0 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { @@ -0,0 +0,0 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
return this.spigot;
return ret;
} }
// Spigot end
+ +
+ // Paper start
+ @Override + @Override
+ public Location getOrigin() { + public Location getOrigin() {
+ Vector originVector = this.getHandle().getOriginVector(); + Vector originVector = this.getHandle().getOriginVector();
@ -155,5 +154,5 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ //noinspection ConstantConditions + //noinspection ConstantConditions
+ return originVector.toLocation(world); + return originVector.toLocation(world);
+ } + }
+ // Paper end // Paper end
} }

View File

@ -67,6 +67,90 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
} }
} }
diff --git a/src/main/java/net/minecraft/server/ChunkSystem.java b/src/main/java/net/minecraft/server/ChunkSystem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/ChunkSystem.java
+++ b/src/main/java/net/minecraft/server/ChunkSystem.java
@@ -0,0 +0,0 @@ public final class ChunkSystem {
static final TicketType<Long> CHUNK_LOAD = TicketType.create("chunk_load", Long::compareTo);
+ // Paper start - priority
+ private static int getPriorityBoost(final PrioritisedExecutor.Priority priority) {
+ if (priority.isLowerOrEqualPriority(PrioritisedExecutor.Priority.NORMAL)) {
+ return 0;
+ }
+
+ int dist = PrioritisedExecutor.Priority.BLOCKING.ordinal() - PrioritisedExecutor.Priority.NORMAL.ordinal();
+
+
+ return (net.minecraft.server.level.DistanceManager.URGENT_PRIORITY * (priority.ordinal() - PrioritisedExecutor.Priority.NORMAL.ordinal())) / dist;
+ }
+ // Paper end - priority
+
private static long chunkLoadCounter = 0L;
public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
final boolean addTicket, final PrioritisedExecutor.Priority priority, final Consumer<ChunkAccess> onComplete) {
@@ -0,0 +0,0 @@ public final class ChunkSystem {
final int minLevel = 33 + ChunkStatus.getDistance(toStatus);
final Long chunkReference = addTicket ? Long.valueOf(++chunkLoadCounter) : null;
final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+ final int priorityBoost = getPriorityBoost(priority);
if (addTicket) {
level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
}
level.chunkSource.runDistanceManagerUpdates();
+ if (priorityBoost == net.minecraft.server.level.DistanceManager.URGENT_PRIORITY) {
+ level.chunkSource.markUrgent(chunkPos);
+ } else if (priorityBoost != 0) {
+ level.chunkSource.markHighPriority(chunkPos, priorityBoost);
+ }
+
final Consumer<ChunkAccess> loadCallback = (final ChunkAccess chunk) -> {
try {
if (onComplete != null) {
@@ -0,0 +0,0 @@ public final class ChunkSystem {
level.chunkSource.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, minLevel, chunkPos);
level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
}
+ if (priorityBoost == net.minecraft.server.level.DistanceManager.URGENT_PRIORITY) {
+ level.chunkSource.clearUrgent(chunkPos);
+ } else if (priorityBoost != 0) {
+ level.chunkSource.clearPriorityTickets(chunkPos);
+ }
}
};
@@ -0,0 +0,0 @@ public final class ChunkSystem {
final int radius = toStatus.ordinal() - 1;
final Long chunkReference = addTicket ? Long.valueOf(++chunkLoadCounter) : null;
final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+ final int priorityBoost = getPriorityBoost(priority);
if (addTicket) {
level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
}
level.chunkSource.runDistanceManagerUpdates();
+ if (priorityBoost != 0) {
+ level.chunkSource.markAreaHighPriority(chunkPos, priorityBoost, radius);
+ }
+
final Consumer<LevelChunk> loadCallback = (final LevelChunk chunk) -> {
try {
if (onComplete != null) {
@@ -0,0 +0,0 @@ public final class ChunkSystem {
level.chunkSource.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, minLevel, chunkPos);
level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
}
+ if (priorityBoost != 0) {
+ level.chunkSource.clearAreaPriorityTickets(chunkPos, radius);
+ }
}
};
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java --- a/src/main/java/net/minecraft/server/MCUtil.java
@ -150,11 +234,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ io.papermc.paper.util.TickThread.ensureTickThread("Async full chunk future completion"); // Paper + io.papermc.paper.util.TickThread.ensureTickThread("Async full chunk future completion"); // Paper
final Optional<LevelChunk> left = either.left(); final Optional<LevelChunk> left = either.left();
if (left.isPresent() && ChunkHolder.this.fullChunkCreateCount == expectCreateCount) { if (left.isPresent() && ChunkHolder.this.fullChunkCreateCount == expectCreateCount) {
// note: Here is a very good place to add callbacks to logic waiting on this.
LevelChunk fullChunk = either.left().get(); LevelChunk fullChunk = either.left().get();
ChunkHolder.this.isFullChunkReady = true; ChunkHolder.this.isFullChunkReady = true;
fullChunk.playerChunk = ChunkHolder.this; net.minecraft.server.ChunkSystem.onChunkBorder(fullChunk, this);
+ this.chunkMap.distanceManager.clearPriorityTickets(pos); + this.chunkMap.distanceManager.clearPriorityTickets(pos); // Paper - chunk priority
} }
}); });
this.updateChunkToSave(this.fullChunkFuture, "full"); this.updateChunkToSave(this.fullChunkFuture, "full");
@ -173,7 +256,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ io.papermc.paper.util.TickThread.ensureTickThread("Async full chunk future completion"); // Paper + io.papermc.paper.util.TickThread.ensureTickThread("Async full chunk future completion"); // Paper
either.ifLeft(chunk -> { either.ifLeft(chunk -> {
ChunkHolder.this.isEntityTickingReady = true; ChunkHolder.this.isEntityTickingReady = true;
// Paper start - entity ticking chunk set net.minecraft.server.ChunkSystem.onChunkEntityTicking(chunk, this);
@@ -0,0 +0,0 @@ public class ChunkHolder { @@ -0,0 +0,0 @@ public class ChunkHolder {
this.demoteFullChunk(chunkStorage, playerchunk_state1); this.demoteFullChunk(chunkStorage, playerchunk_state1);
} }
@ -422,9 +505,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ // Paper end + // Paper end
+ +
// Paper start private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
public void updatePlayerMobTypeMap(Entity entity) { double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { double d1 = (double) SectionPos.sectionToBlockCoord(pos.z, 8);
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
List<ChunkHolder> list1 = new ArrayList(); List<ChunkHolder> list1 = new ArrayList();
int j = centerChunk.x; int j = centerChunk.x;
@ -542,7 +625,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
@@ -0,0 +0,0 @@ public abstract class DistanceManager { @@ -0,0 +0,0 @@ public abstract class DistanceManager {
public boolean runAllUpdates(ChunkMap chunkStorage) { public boolean runAllUpdates(ChunkMap chunkStorage) {
//this.f.a(); // Paper - no longer used this.naturalSpawnChunkCounter.runAllUpdates();
this.tickingTicketsTracker.runAllUpdates(); this.tickingTicketsTracker.runAllUpdates();
+ org.spigotmc.AsyncCatcher.catchOp("DistanceManagerTick"); // Paper + org.spigotmc.AsyncCatcher.catchOp("DistanceManagerTick"); // Paper
this.playerTicketManager.runAllUpdates(); this.playerTicketManager.runAllUpdates();
@ -737,6 +820,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ +
+ public void clearPriorityTickets(ChunkPos coords) { + public void clearPriorityTickets(ChunkPos coords) {
+ this.distanceManager.clearPriorityTickets(coords); + this.distanceManager.clearPriorityTickets(coords);
+ }
+
+ public void clearUrgent(ChunkPos coords) {
+ this.distanceManager.clearUrgent(coords);
+ } + }
// Paper end - async chunk io // Paper end - async chunk io
@ -808,8 +895,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private int lastSentFood = -99999999; private int lastSentFood = -99999999;
private boolean lastFoodSaturationZero = true; private boolean lastFoodSaturationZero = true;
@@ -0,0 +0,0 @@ public class ServerPlayer extends Player { @@ -0,0 +0,0 @@ public class ServerPlayer extends Player {
this.bukkitPickUpLoot = true;
this.maxHealthCache = this.getMaxHealth(); this.maxHealthCache = this.getMaxHealth();
this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
} }
+ // Paper start - Chunk priority + // Paper start - Chunk priority
+ public BlockPos getPointInFront(double inFront) { + public BlockPos getPointInFront(double inFront) {
@ -1120,20 +1207,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
public void placeNewPlayer(Connection connection, ServerPlayer player) { public void placeNewPlayer(Connection connection, ServerPlayer player) {
+ player.isRealPlayer = true; // Paper - Chunk priority + player.isRealPlayer = true; // Paper - Chunk priority
ServerPlayer prev = pendingPlayers.put(player.getUUID(), player);// Paper GameProfile gameprofile = player.getGameProfile();
if (prev != null) { GameProfileCache usercache = this.server.getProfileCache();
disconnectPendingPlayer(prev); Optional<GameProfile> optional = usercache.get(gameprofile.getId());
@@ -0,0 +0,0 @@ public abstract class PlayerList {
net.minecraft.server.level.ChunkMap playerChunkMap = worldserver1.getChunkSource().chunkMap;
net.minecraft.server.level.DistanceManager distanceManager = playerChunkMap.distanceManager;
distanceManager.addTicket(net.minecraft.server.level.TicketType.LOGIN, pos, 31, pos.toLong());
- worldserver1.getChunkSource().runDistanceManagerUpdates();
- worldserver1.getChunkSource().getChunkAtAsynchronously(chunkX, chunkZ, true, true).thenApply(chunk -> {
+ worldserver1.getChunkSource().markAreaHighPriority(pos, 28, 3); // Paper - Chunk priority
+ worldserver1.getChunkSource().getChunkAtAsynchronously(chunkX, chunkZ, true, false).thenApply(chunk -> { // Paper - Chunk priority
net.minecraft.server.level.ChunkHolder updatingChunk = playerChunkMap.getUpdatingChunkIfPresent(pos.toLong());
if (updatingChunk != null) {
return updatingChunk.getEntityTickingChunkFuture();
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java --- a/src/main/java/net/minecraft/world/entity/Entity.java
@ -1176,41 +1252,3 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
org.bukkit.Server server = this.level.getCraftServer(); org.bukkit.Server server = this.level.getCraftServer();
org.bukkit.event.world.ChunkUnloadEvent unloadEvent = new org.bukkit.event.world.ChunkUnloadEvent(this.bukkitChunk, this.isUnsaved()); org.bukkit.event.world.ChunkUnloadEvent unloadEvent = new org.bukkit.event.world.ChunkUnloadEvent(this.bukkitChunk, this.isUnsaved());
server.getPluginManager().callEvent(unloadEvent); server.getPluginManager().callEvent(unloadEvent);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return future;
}
+ // Paper start - Chunk priority
+ if (!urgent) {
+ // If not urgent, at least use a slightly boosted priority
+ world.getChunkSource().markHighPriority(new ChunkPos(x, z), 1);
+ }
+ // Paper end
return this.world.getChunkSource().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> {
net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) either.left().orElse(null);
if (chunk != null) addTicket(x, z); // Paper
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -0,0 +0,0 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
throw new UnsupportedOperationException("Cannot set rotation of players. Consider teleporting instead.");
}
+ // Paper start - Chunk priority
+ @Override
+ public java.util.concurrent.CompletableFuture<Boolean> teleportAsync(Location loc, @javax.annotation.Nonnull PlayerTeleportEvent.TeleportCause cause) {
+ ((CraftWorld)loc.getWorld()).getHandle().getChunkSource().markAreaHighPriority(
+ new net.minecraft.world.level.ChunkPos(net.minecraft.util.Mth.floor(loc.getX()) >> 4,
+ net.minecraft.util.Mth.floor(loc.getZ()) >> 4), 28, 3); // Load area high priority
+ return super.teleportAsync(loc, cause);
+ }
+ // Paper end
+
@Override
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
Preconditions.checkArgument(location != null, "location");

View File

@ -115,17 +115,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
// private final Map<UUID, ServerStatisticManager> stats; // private final Map<UUID, ServerStatisticManager> stats;
// private final Map<UUID, AdvancementDataPlayer> advancements; // private final Map<UUID, AdvancementDataPlayer> advancements;
@@ -0,0 +0,0 @@ public abstract class PlayerList { @@ -0,0 +0,0 @@ public abstract class PlayerList {
}
public void placeNewPlayer(Connection connection, ServerPlayer player) { public void placeNewPlayer(Connection connection, ServerPlayer player) {
+ ServerPlayer prev = pendingPlayers.put(player.getUUID(), player);// Paper player.isRealPlayer = true; // Paper - Chunk priority
player.loginTime = System.currentTimeMillis(); // Paper
+ // Paper start
+ ServerPlayer prev = pendingPlayers.put(player.getUUID(), player);
+ if (prev != null) { + if (prev != null) {
+ disconnectPendingPlayer(prev); + disconnectPendingPlayer(prev);
+ } + }
+ player.networkManager = connection; // Paper + player.networkManager = connection;
player.loginTime = System.currentTimeMillis(); // Paper + // Paper end
GameProfile gameprofile = player.getGameProfile(); GameProfile gameprofile = player.getGameProfile();
GameProfileCache usercache = this.server.getProfileCache(); GameProfileCache usercache = this.server.getProfileCache();
Optional<GameProfile> optional = usercache.get(gameprofile.getId());
@@ -0,0 +0,0 @@ public abstract class PlayerList { @@ -0,0 +0,0 @@ public abstract class PlayerList {
if (nbttagcompound != null && nbttagcompound.contains("bukkit")) { if (nbttagcompound != null && nbttagcompound.contains("bukkit")) {
CompoundTag bukkit = nbttagcompound.getCompound("bukkit"); CompoundTag bukkit = nbttagcompound.getCompound("bukkit");
@ -164,31 +166,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ final net.minecraft.world.level.ChunkPos pos = new net.minecraft.world.level.ChunkPos(chunkX, chunkZ); + final net.minecraft.world.level.ChunkPos pos = new net.minecraft.world.level.ChunkPos(chunkX, chunkZ);
+ net.minecraft.server.level.ChunkMap playerChunkMap = worldserver1.getChunkSource().chunkMap; + net.minecraft.server.level.ChunkMap playerChunkMap = worldserver1.getChunkSource().chunkMap;
+ net.minecraft.server.level.DistanceManager distanceManager = playerChunkMap.distanceManager; + net.minecraft.server.level.DistanceManager distanceManager = playerChunkMap.distanceManager;
+ distanceManager.addTicket(net.minecraft.server.level.TicketType.LOGIN, pos, 31, pos.toLong()); + net.minecraft.server.ChunkSystem.scheduleTickingState(
+ worldserver1.getChunkSource().runDistanceManagerUpdates(); + worldserver1, chunkX, chunkZ, net.minecraft.server.level.ChunkHolder.FullChunkStatus.ENTITY_TICKING, true,
+ worldserver1.getChunkSource().getChunkAtAsynchronously(chunkX, chunkZ, true, true).thenApply(chunk -> { + ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHEST,
+ net.minecraft.server.level.ChunkHolder updatingChunk = playerChunkMap.getUpdatingChunkIfPresent(pos.toLong()); + (chunk) -> {
+ if (updatingChunk != null) { + MinecraftServer.getServer().scheduleOnMain(() -> {
+ return updatingChunk.getEntityTickingChunkFuture(); + try {
+ } else { + if (!playerconnection.connection.isConnected()) {
+ return java.util.concurrent.CompletableFuture.completedFuture(chunk); + return;
+ } + }
+ }).thenAccept(chunk -> { + PlayerList.this.postChunkLoadJoin(
+ MinecraftServer.getServer().scheduleOnMain(() -> { + player, finalWorldserver, connection, playerconnection,
+ try { + nbttagcompound, s1, lastKnownName
+ if (!playerconnection.connection.isConnected()) { + );
+ return; + distanceManager.addTicket(net.minecraft.server.level.TicketType.LOGIN, pos, 31, pos.toLong());
+ } finally {
+ finalWorldserver.pendingLogin.remove(player);
+ } + }
+ PlayerList.this.postChunkLoadJoin( + });
+ player, finalWorldserver, connection, playerconnection, + }
+ nbttagcompound, s1, lastKnownName + );
+ );
+ distanceManager.addTicket(net.minecraft.server.level.TicketType.LOGIN, pos, 31, pos.toLong());
+ } finally {
+ finalWorldserver.pendingLogin.remove(player);
+ }
+ });
+ });
+ } + }
+ +
+ public ServerPlayer getActivePlayer(UUID uuid) { + public ServerPlayer getActivePlayer(UUID uuid) {

View File

@ -4528,6 +4528,281 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (packet.isSkippable()) { if (packet.isSkippable()) {
throw new SkipPacketException(var10); throw new SkipPacketException(var10);
} else { } else {
diff --git a/src/main/java/net/minecraft/server/ChunkSystem.java b/src/main/java/net/minecraft/server/ChunkSystem.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/net/minecraft/server/ChunkSystem.java
@@ -0,0 +0,0 @@
+package net.minecraft.server;
+
+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
+import com.destroystokyo.paper.util.SneakyThrow;
+import com.mojang.datafixers.util.Either;
+import com.mojang.logging.LogUtils;
+import io.papermc.paper.util.CoordinateUtils;
+import net.minecraft.server.level.ChunkHolder;
+import net.minecraft.server.level.ChunkMap;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.TicketType;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import net.minecraft.world.level.chunk.LevelChunk;
+import org.bukkit.Bukkit;
+import org.slf4j.Logger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+
+public final class ChunkSystem {
+
+ private static final Logger LOGGER = LogUtils.getLogger();
+
+ public static void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run) {
+ scheduleChunkTask(level, chunkX, chunkZ, run, PrioritisedExecutor.Priority.NORMAL);
+ }
+
+ public static void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final PrioritisedExecutor.Priority priority) {
+ level.chunkSource.mainThreadProcessor.execute(run);
+ }
+
+ public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
+ final ChunkStatus toStatus, final boolean addTicket, final PrioritisedExecutor.Priority priority,
+ final Consumer<ChunkAccess> onComplete) {
+ if (gen) {
+ scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+ return;
+ }
+ scheduleChunkLoad(level, chunkX, chunkZ, ChunkStatus.EMPTY, addTicket, priority, (final ChunkAccess chunk) -> {
+ if (chunk == null) {
+ onComplete.accept(null);
+ } else {
+ if (chunk.getStatus().isOrAfter(toStatus)) {
+ scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+ } else {
+ onComplete.accept(null);
+ }
+ }
+ });
+ }
+
+ static final TicketType<Long> CHUNK_LOAD = TicketType.create("chunk_load", Long::compareTo);
+
+ private static long chunkLoadCounter = 0L;
+ public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
+ final boolean addTicket, final PrioritisedExecutor.Priority priority, final Consumer<ChunkAccess> onComplete) {
+ if (!Bukkit.isPrimaryThread()) {
+ scheduleChunkTask(level, chunkX, chunkZ, () -> {
+ scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+ }, priority);
+ return;
+ }
+
+ final int minLevel = 33 + ChunkStatus.getDistance(toStatus);
+ final Long chunkReference = addTicket ? Long.valueOf(++chunkLoadCounter) : null;
+ final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+
+ if (addTicket) {
+ level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+ }
+ level.chunkSource.runDistanceManagerUpdates();
+
+ final Consumer<ChunkAccess> loadCallback = (final ChunkAccess chunk) -> {
+ try {
+ if (onComplete != null) {
+ onComplete.accept(chunk);
+ }
+ } catch (final ThreadDeath death) {
+ throw death;
+ } catch (final Throwable thr) {
+ LOGGER.error("Exception handling chunk load callback", thr);
+ SneakyThrow.sneaky(thr);
+ } finally {
+ if (addTicket) {
+ level.chunkSource.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, minLevel, chunkPos);
+ level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+ }
+ }
+ };
+
+ final ChunkHolder holder = level.chunkSource.chunkMap.getUpdatingChunkIfPresent(CoordinateUtils.getChunkKey(chunkX, chunkZ));
+
+ if (holder == null || holder.getTicketLevel() > minLevel) {
+ loadCallback.accept(null);
+ return;
+ }
+
+ final CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> loadFuture = holder.getOrScheduleFuture(toStatus, level.chunkSource.chunkMap);
+
+ if (loadFuture.isDone()) {
+ loadCallback.accept(loadFuture.join().left().orElse(null));
+ return;
+ }
+
+ loadFuture.whenCompleteAsync((final Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either, final Throwable thr) -> {
+ if (thr != null) {
+ loadCallback.accept(null);
+ return;
+ }
+ loadCallback.accept(either.left().orElse(null));
+ }, (final Runnable r) -> {
+ scheduleChunkTask(level, chunkX, chunkZ, r, PrioritisedExecutor.Priority.HIGHEST);
+ });
+ }
+
+ public static void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
+ final ChunkHolder.FullChunkStatus toStatus, final boolean addTicket,
+ final PrioritisedExecutor.Priority priority, final Consumer<LevelChunk> onComplete) {
+ if (toStatus == ChunkHolder.FullChunkStatus.INACCESSIBLE) {
+ throw new IllegalArgumentException("Cannot wait for INACCESSIBLE status");
+ }
+
+ if (!Bukkit.isPrimaryThread()) {
+ scheduleChunkTask(level, chunkX, chunkZ, () -> {
+ scheduleTickingState(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+ }, priority);
+ return;
+ }
+
+ final int minLevel = 33 - (toStatus.ordinal() - 1);
+ final int radius = toStatus.ordinal() - 1;
+ final Long chunkReference = addTicket ? Long.valueOf(++chunkLoadCounter) : null;
+ final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+
+ if (addTicket) {
+ level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+ }
+ level.chunkSource.runDistanceManagerUpdates();
+
+ final Consumer<LevelChunk> loadCallback = (final LevelChunk chunk) -> {
+ try {
+ if (onComplete != null) {
+ onComplete.accept(chunk);
+ }
+ } catch (final ThreadDeath death) {
+ throw death;
+ } catch (final Throwable thr) {
+ LOGGER.error("Exception handling chunk load callback", thr);
+ SneakyThrow.sneaky(thr);
+ } finally {
+ if (addTicket) {
+ level.chunkSource.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, minLevel, chunkPos);
+ level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+ }
+ }
+ };
+
+ final ChunkHolder holder = level.chunkSource.chunkMap.getUpdatingChunkIfPresent(CoordinateUtils.getChunkKey(chunkX, chunkZ));
+
+ if (holder == null || holder.getTicketLevel() > minLevel) {
+ loadCallback.accept(null);
+ return;
+ }
+
+ final CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> tickingState;
+ switch (toStatus) {
+ case BORDER: {
+ tickingState = holder.getFullChunkFuture();
+ break;
+ }
+ case TICKING: {
+ tickingState = holder.getTickingChunkFuture();
+ break;
+ }
+ case ENTITY_TICKING: {
+ tickingState = holder.getEntityTickingChunkFuture();
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Cannot reach here");
+ }
+ }
+
+ if (tickingState.isDone()) {
+ loadCallback.accept(tickingState.join().left().orElse(null));
+ return;
+ }
+
+ tickingState.whenCompleteAsync((final Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either, final Throwable thr) -> {
+ if (thr != null) {
+ loadCallback.accept(null);
+ return;
+ }
+ loadCallback.accept(either.left().orElse(null));
+ }, (final Runnable r) -> {
+ scheduleChunkTask(level, chunkX, chunkZ, r, PrioritisedExecutor.Priority.HIGHEST);
+ });
+ }
+
+ public static List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
+ return new ArrayList<>(level.chunkSource.chunkMap.visibleChunkMap.values());
+ }
+
+ public static List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
+ return new ArrayList<>(level.chunkSource.chunkMap.updatingChunkMap.values());
+ }
+
+ public static int getVisibleChunkHolderCount(final ServerLevel level) {
+ return level.chunkSource.chunkMap.visibleChunkMap.size();
+ }
+
+ public static int getUpdatingChunkHolderCount(final ServerLevel level) {
+ return level.chunkSource.chunkMap.updatingChunkMap.size();
+ }
+
+ public static boolean hasAnyChunkHolders(final ServerLevel level) {
+ return getUpdatingChunkHolderCount(level) != 0;
+ }
+
+ public static void onEntityPreAdd(final ServerLevel level, final Entity entity) {
+
+ }
+
+ public static void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
+ final ChunkMap chunkMap = level.chunkSource.chunkMap;
+ for (int index = 0, len = chunkMap.regionManagers.size(); index < len; ++index) {
+ chunkMap.regionManagers.get(index).addChunk(holder.pos.x, holder.pos.z);
+ }
+ }
+
+ public static void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
+ final ChunkMap chunkMap = level.chunkSource.chunkMap;
+ for (int index = 0, len = chunkMap.regionManagers.size(); index < len; ++index) {
+ chunkMap.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
+ }
+ }
+
+ public static void onChunkBorder(LevelChunk chunk, ChunkHolder holder) {
+ chunk.playerChunk = holder;
+ }
+
+ public static void onChunkNotBorder(LevelChunk chunk, ChunkHolder holder) {
+
+ }
+
+ public static void onChunkTicking(LevelChunk chunk, ChunkHolder holder) {
+ chunk.level.getChunkSource().tickingChunks.add(chunk);
+ }
+
+ public static void onChunkNotTicking(LevelChunk chunk, ChunkHolder holder) {
+ chunk.level.getChunkSource().tickingChunks.remove(chunk);
+ }
+
+ public static void onChunkEntityTicking(LevelChunk chunk, ChunkHolder holder) {
+ chunk.level.getChunkSource().entityTickingChunks.add(chunk);
+ }
+
+ public static void onChunkNotEntityTicking(LevelChunk chunk, ChunkHolder holder) {
+ chunk.level.getChunkSource().entityTickingChunks.remove(chunk);
+ }
+
+ private ChunkSystem() {
+ throw new RuntimeException();
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
@ -4845,7 +5120,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * @return + * @return
+ */ + */
+ public static void ensureMain(String reason, Runnable run) { + public static void ensureMain(String reason, Runnable run) {
+ if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().serverThread) { + if (!isMainThread()) {
+ if (reason != null) { + if (reason != null) {
+ new IllegalStateException("Asynchronous " + reason + "!").printStackTrace(); + new IllegalStateException("Asynchronous " + reason + "!").printStackTrace();
+ } + }
@ -4870,7 +5145,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * @return + * @return
+ */ + */
+ public static <T> T ensureMain(String reason, Supplier<T> run) { + public static <T> T ensureMain(String reason, Supplier<T> run) {
+ if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().serverThread) { + if (!isMainThread()) {
+ if (reason != null) { + if (reason != null) {
+ new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace(); + new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace();
+ } + }
@ -5118,6 +5393,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private CompletableFuture<Void> pendingFullStateConfirmation; private CompletableFuture<Void> pendingFullStateConfirmation;
+ private final ChunkMap chunkMap; // Paper + private final ChunkMap chunkMap; // Paper
+
+ // Paper start
+ public void onChunkAdd() {
+
+ }
+
+ public void onChunkRemove() {
+
+ }
+ // Paper end
+ +
public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) { public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) {
this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size()); this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
@ -5236,16 +5521,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ this.fullChunkFuture.thenAccept(either -> { + this.fullChunkFuture.thenAccept(either -> {
+ final Optional<LevelChunk> left = either.left(); + final Optional<LevelChunk> left = either.left();
+ if (left.isPresent() && ChunkHolder.this.fullChunkCreateCount == expectCreateCount) { + if (left.isPresent() && ChunkHolder.this.fullChunkCreateCount == expectCreateCount) {
+ // note: Here is a very good place to add callbacks to logic waiting on this.
+ LevelChunk fullChunk = either.left().get(); + LevelChunk fullChunk = either.left().get();
+ ChunkHolder.this.isFullChunkReady = true; + ChunkHolder.this.isFullChunkReady = true;
+ fullChunk.playerChunk = ChunkHolder.this; + net.minecraft.server.ChunkSystem.onChunkBorder(fullChunk, this);
+ } + }
+ }); + });
this.updateChunkToSave(this.fullChunkFuture, "full"); this.updateChunkToSave(this.fullChunkFuture, "full");
} }
if (flag2 && !flag3) { if (flag2 && !flag3) {
+ // Paper start
+ if (this.isFullChunkReady) {
+ net.minecraft.server.ChunkSystem.onChunkNotBorder(this.fullChunkFuture.join().left().get(), this); // Paper
+ }
+ // Paper end
this.fullChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); this.fullChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK);
this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
+ ++this.fullChunkCreateCount; // Paper - cache ticking ready status + ++this.fullChunkCreateCount; // Paper - cache ticking ready status
@ -5262,9 +5551,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ either.ifLeft(chunk -> { + either.ifLeft(chunk -> {
+ // note: Here is a very good place to add callbacks to logic waiting on this. + // note: Here is a very good place to add callbacks to logic waiting on this.
+ ChunkHolder.this.isTickingReady = true; + ChunkHolder.this.isTickingReady = true;
+ // Paper start - ticking chunk set + net.minecraft.server.ChunkSystem.onChunkTicking(chunk, this);
+ ChunkHolder.this.chunkMap.level.getChunkSource().tickingChunks.add(chunk);
+ // Paper end - ticking chunk set
+ }); + });
+ }); + });
+ // Paper end + // Paper end
@ -5273,17 +5560,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (flag4 && !flag5) { if (flag4 && !flag5) {
- this.tickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); - this.tickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK);
+ // Paper start
+ if (this.isTickingReady) {
+ net.minecraft.server.ChunkSystem.onChunkNotTicking(this.tickingChunkFuture.join().left().get(), this); // Paper
+ }
+ // Paper end
+ this.tickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); this.isTickingReady = false; // Paper - cache chunk ticking stage + this.tickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); this.isTickingReady = false; // Paper - cache chunk ticking stage
this.tickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; this.tickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
+ // Paper start - ticking chunk set
+ LevelChunk chunkIfCached = this.getFullChunkNowUnchecked();
+ if (chunkIfCached != null) {
+ this.chunkMap.level.getChunkSource().tickingChunks.remove(chunkIfCached);
+ }
+ // Paper end - ticking chunk set
} }
boolean flag6 = playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.ENTITY_TICKING);
@@ -0,0 +0,0 @@ public class ChunkHolder { @@ -0,0 +0,0 @@ public class ChunkHolder {
this.entityTickingChunkFuture = chunkStorage.prepareEntityTickingChunk(this.pos); this.entityTickingChunkFuture = chunkStorage.prepareEntityTickingChunk(this.pos);
@ -5292,9 +5577,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ this.entityTickingChunkFuture.thenAccept(either -> { + this.entityTickingChunkFuture.thenAccept(either -> {
+ either.ifLeft(chunk -> { + either.ifLeft(chunk -> {
+ ChunkHolder.this.isEntityTickingReady = true; + ChunkHolder.this.isEntityTickingReady = true;
+ // Paper start - entity ticking chunk set + net.minecraft.server.ChunkSystem.onChunkEntityTicking(chunk, this);
+ ChunkHolder.this.chunkMap.level.getChunkSource().entityTickingChunks.add(chunk);
+ // Paper end - entity ticking chunk set
+ }); + });
+ }); + });
+ // Paper end + // Paper end
@ -5303,17 +5586,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (flag6 && !flag7) { if (flag6 && !flag7) {
- this.entityTickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); - this.entityTickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK);
+ // Paper start
+ if (this.isEntityTickingReady) {
+ net.minecraft.server.ChunkSystem.onChunkNotEntityTicking(this.entityTickingChunkFuture.join().left().get(), this);
+ }
+ // Paper end
+ this.entityTickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); this.isEntityTickingReady = false; // Paper - cache chunk ticking stage + this.entityTickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); this.isEntityTickingReady = false; // Paper - cache chunk ticking stage
this.entityTickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; this.entityTickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
+ // Paper start - entity ticking chunk set
+ LevelChunk chunkIfCached = this.getFullChunkNowUnchecked();
+ if (chunkIfCached != null) {
+ this.chunkMap.level.getChunkSource().entityTickingChunks.remove(chunkIfCached);
+ }
+ // Paper end - entity ticking chunk set
} }
if (!playerchunk_state1.isOrAfter(playerchunk_state)) {
@@ -0,0 +0,0 @@ public class ChunkHolder { @@ -0,0 +0,0 @@ public class ChunkHolder {
} }
}; };
@ -5428,18 +5709,77 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private CompletableFuture<Either<List<ChunkAccess>, ChunkHolder.ChunkLoadingFailure>> getChunkRangeFuture(ChunkPos centerChunk, int margin, IntFunction<ChunkStatus> distanceToStatus) { private CompletableFuture<Either<List<ChunkAccess>, ChunkHolder.ChunkLoadingFailure>> getChunkRangeFuture(ChunkPos centerChunk, int margin, IntFunction<ChunkStatus> distanceToStatus) {
List<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> list = new ArrayList(); List<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> list = new ArrayList();
List<ChunkHolder> list1 = new ArrayList(); List<ChunkHolder> list1 = new ArrayList();
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
};
stringbuilder.append("Updating:").append(System.lineSeparator());
- this.updatingChunkMap.values().forEach(consumer);
+ net.minecraft.server.ChunkSystem.getUpdatingChunkHolders(this.level).forEach(consumer); // Paper
stringbuilder.append("Visible:").append(System.lineSeparator());
- this.visibleChunkMap.values().forEach(consumer);
+ net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).forEach(consumer); // Paper
CrashReport crashreport = CrashReport.forThrowable(exception, "Chunk loading");
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Chunk loading");
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
holder.setTicketLevel(level); holder.setTicketLevel(level);
} else { } else {
holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this); holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this);
+ // Paper start + // Paper start
+ for (int index = 0, len = this.regionManagers.size(); index < len; ++index) { + net.minecraft.server.ChunkSystem.onChunkHolderCreate(this.level, holder);
+ this.regionManagers.get(index).addChunk(holder.pos.x, holder.pos.z);
+ }
+ // Paper end + // Paper end
} }
+ // Paper start
+ holder.onChunkAdd();
+ // Paper end
this.updatingChunkMap.put(pos, holder); this.updatingChunkMap.put(pos, holder);
this.modified = true;
}
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
protected void saveAllChunks(boolean flush) {
if (flush) {
- List<ChunkHolder> list = (List) this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList());
+ List<ChunkHolder> list = (List) net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); // Paper
MutableBoolean mutableboolean = new MutableBoolean();
do {
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
});
this.flushWorker();
} else {
- this.visibleChunkMap.values().forEach(this::saveChunkIfNeeded);
+ net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).forEach(this::saveChunkIfNeeded);
}
}
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public boolean hasWork() {
- return this.lightEngine.hasLightWork() || !this.pendingUnloads.isEmpty() || !this.updatingChunkMap.isEmpty() || this.poiManager.hasWork() || !this.toDrop.isEmpty() || !this.unloadQueue.isEmpty() || this.queueSorter.hasWork() || this.distanceManager.hasTickets();
+ return this.lightEngine.hasLightWork() || !this.pendingUnloads.isEmpty() || net.minecraft.server.ChunkSystem.hasAnyChunkHolders(this.level) || this.poiManager.hasWork() || !this.toDrop.isEmpty() || !this.unloadQueue.isEmpty() || this.queueSorter.hasWork() || this.distanceManager.hasTickets(); // Paper
}
private void processUnloads(BooleanSupplier shouldKeepTicking) {
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j);
if (playerchunk != null) {
+ playerchunk.onChunkRemove(); // Paper
this.pendingUnloads.put(j, playerchunk);
this.modified = true;
++i;
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
int l = 0;
- ObjectIterator objectiterator = this.visibleChunkMap.values().iterator();
+ Iterator objectiterator = net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).iterator(); // Paper
while (l < 20 && shouldKeepTicking.getAsBoolean() && objectiterator.hasNext()) {
if (this.saveChunkIfNeeded((ChunkHolder) objectiterator.next())) {
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (completablefuture1 != completablefuture) { if (completablefuture1 != completablefuture) {
this.scheduleUnload(pos, holder); this.scheduleUnload(pos, holder);
@ -5448,9 +5788,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper start + // Paper start
+ boolean removed; + boolean removed;
+ if ((removed = this.pendingUnloads.remove(pos, holder)) && ichunkaccess != null) { + if ((removed = this.pendingUnloads.remove(pos, holder)) && ichunkaccess != null) {
+ for (int index = 0, len = this.regionManagers.size(); index < len; ++index) { + net.minecraft.server.ChunkSystem.onChunkHolderDelete(this.level, holder);
+ this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
+ }
+ // Paper end + // Paper end
if (ichunkaccess instanceof LevelChunk) { if (ichunkaccess instanceof LevelChunk) {
((LevelChunk) ichunkaccess).setLoaded(false); ((LevelChunk) ichunkaccess).setLoaded(false);
@ -5459,15 +5797,57 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
this.lightEngine.tryScheduleUpdate(); this.lightEngine.tryScheduleUpdate();
this.progressListener.onStatusChange(ichunkaccess.getPos(), (ChunkStatus) null); this.progressListener.onStatusChange(ichunkaccess.getPos(), (ChunkStatus) null);
this.chunkSaveCooldowns.remove(ichunkaccess.getPos().toLong()); this.chunkSaveCooldowns.remove(ichunkaccess.getPos().toLong());
- }
+ } else if (removed) { // Paper start + } else if (removed) { // Paper start
+ for (int index = 0, len = this.regionManagers.size(); index < len; ++index) { + net.minecraft.server.ChunkSystem.onChunkHolderDelete(this.level, holder);
+ this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z); + } // Paper end
}
+ } // Paper end
} }
}; };
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.viewDistance = j;
this.distanceManager.updatePlayerTickets(this.viewDistance + 1);
- ObjectIterator objectiterator = this.updatingChunkMap.values().iterator();
+ Iterator objectiterator = net.minecraft.server.ChunkSystem.getUpdatingChunkHolders(this.level).iterator(); // Paper
while (objectiterator.hasNext()) {
ChunkHolder playerchunk = (ChunkHolder) objectiterator.next();
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public int size() {
- return this.visibleChunkMap.size();
+ return net.minecraft.server.ChunkSystem.getVisibleChunkHolderCount(this.level); // Paper
}
public DistanceManager getDistanceManager() {
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
protected Iterable<ChunkHolder> getChunks() {
- return Iterables.unmodifiableIterable(this.visibleChunkMap.values());
+ return Iterables.unmodifiableIterable(net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level)); // Paper
}
void dumpChunks(Writer writer) throws IOException {
CsvOutput csvwriter = CsvOutput.builder().addColumn("x").addColumn("z").addColumn("level").addColumn("in_memory").addColumn("status").addColumn("full_status").addColumn("accessible_ready").addColumn("ticking_ready").addColumn("entity_ticking_ready").addColumn("ticket").addColumn("spawning").addColumn("block_entity_count").addColumn("ticking_ticket").addColumn("ticking_level").addColumn("block_ticks").addColumn("fluid_ticks").build(writer);
TickingTracker tickingtracker = this.distanceManager.tickingTracker();
- ObjectBidirectionalIterator objectbidirectionaliterator = this.visibleChunkMap.long2ObjectEntrySet().iterator();
+ Iterator<ChunkHolder> objectbidirectionaliterator = net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).iterator(); // Paper
while (objectbidirectionaliterator.hasNext()) {
- Entry<ChunkHolder> entry = (Entry) objectbidirectionaliterator.next();
- long i = entry.getLongKey();
+ ChunkHolder playerchunk = objectbidirectionaliterator.next(); // Paper
+ long i = playerchunk.pos.toLong(); // Paper
ChunkPos chunkcoordintpair = new ChunkPos(i);
- ChunkHolder playerchunk = (ChunkHolder) entry.getValue();
+ // Paper
Optional<ChunkAccess> optional = Optional.ofNullable(playerchunk.getLastAvailable());
Optional<LevelChunk> optional1 = optional.flatMap((ichunkaccess) -> {
return ichunkaccess instanceof LevelChunk ? Optional.of((LevelChunk) ichunkaccess) : Optional.empty();
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (!flag1) { if (!flag1) {
this.distanceManager.addPlayer(SectionPos.of((EntityAccess) player), player); this.distanceManager.addPlayer(SectionPos.of((EntityAccess) player), player);
} }
@ -5597,7 +5977,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ +
+ LevelChunk cachedChunk = this.lastLoadedChunks[cacheKey]; + LevelChunk cachedChunk = this.lastLoadedChunks[cacheKey];
+ if (cachedChunk != null && cachedChunk.locX == x & cachedChunk.locZ == z) { + if (cachedChunk != null && cachedChunk.locX == x & cachedChunk.locZ == z) {
+ return this.lastLoadedChunks[cacheKey]; + return cachedChunk;
+ } + }
+ +
+ long chunkKey = ChunkPos.asLong(x, z); + long chunkKey = ChunkPos.asLong(x, z);
@ -5623,80 +6003,24 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ long chunkFutureAwaitCounter; // Paper - private -> package private + long chunkFutureAwaitCounter; // Paper - private -> package private
+ +
+ public void getEntityTickingChunkAsync(int x, int z, java.util.function.Consumer<LevelChunk> onLoad) { + public void getEntityTickingChunkAsync(int x, int z, java.util.function.Consumer<LevelChunk> onLoad) {
+ if (Thread.currentThread() != this.mainThread) { + net.minecraft.server.ChunkSystem.scheduleTickingState(
+ this.mainThreadProcessor.execute(() -> { + this.level, x, z, ChunkHolder.FullChunkStatus.ENTITY_TICKING, true,
+ ServerChunkCache.this.getEntityTickingChunkAsync(x, z, onLoad); + ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL, onLoad
+ }); + );
+ return;
+ }
+ this.getChunkFutureAsynchronously(x, z, 31, ChunkHolder::getEntityTickingChunkFuture, onLoad);
+ } + }
+ +
+ public void getTickingChunkAsync(int x, int z, java.util.function.Consumer<LevelChunk> onLoad) { + public void getTickingChunkAsync(int x, int z, java.util.function.Consumer<LevelChunk> onLoad) {
+ if (Thread.currentThread() != this.mainThread) { + net.minecraft.server.ChunkSystem.scheduleTickingState(
+ this.mainThreadProcessor.execute(() -> { + this.level, x, z, ChunkHolder.FullChunkStatus.TICKING, true,
+ ServerChunkCache.this.getTickingChunkAsync(x, z, onLoad); + ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL, onLoad
+ }); + );
+ return;
+ }
+ this.getChunkFutureAsynchronously(x, z, 32, ChunkHolder::getTickingChunkFuture, onLoad);
+ } + }
+ +
+ public void getFullChunkAsync(int x, int z, java.util.function.Consumer<LevelChunk> onLoad) { + public void getFullChunkAsync(int x, int z, java.util.function.Consumer<LevelChunk> onLoad) {
+ if (Thread.currentThread() != this.mainThread) { + net.minecraft.server.ChunkSystem.scheduleTickingState(
+ this.mainThreadProcessor.execute(() -> { + this.level, x, z, ChunkHolder.FullChunkStatus.BORDER, true,
+ ServerChunkCache.this.getFullChunkAsync(x, z, onLoad); + ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL, onLoad
+ }); + );
+ return;
+ }
+ this.getChunkFutureAsynchronously(x, z, 33, ChunkHolder::getFullChunkFuture, onLoad);
+ }
+
+ private void getChunkFutureAsynchronously(int x, int z, int ticketLevel, java.util.function.Function<ChunkHolder, CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>>> futureGet, java.util.function.Consumer<LevelChunk> onLoad) {
+ if (Thread.currentThread() != this.mainThread) {
+ throw new IllegalStateException();
+ }
+ ChunkPos chunkPos = new ChunkPos(x, z);
+ Long identifier = this.chunkFutureAwaitCounter++;
+ this.distanceManager.addTicket(TicketType.FUTURE_AWAIT, chunkPos, ticketLevel, identifier);
+ this.runDistanceManagerUpdates();
+
+ ChunkHolder chunk = this.chunkMap.getUpdatingChunkIfPresent(chunkPos.toLong());
+
+ if (chunk == null) {
+ throw new IllegalStateException("Expected playerchunk " + chunkPos + " in world '" + this.level.getWorld().getName() + "'");
+ }
+
+ CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> future = futureGet.apply(chunk);
+
+ future.whenCompleteAsync((either, throwable) -> {
+ try {
+ if (throwable != null) {
+ if (throwable instanceof ThreadDeath) {
+ throw (ThreadDeath)throwable;
+ }
+ net.minecraft.server.MinecraftServer.LOGGER.error("Failed to complete future await for chunk " + chunkPos.toString() + " in world '" + ServerChunkCache.this.level.getWorld().getName() + "'", throwable);
+ } else if (either.right().isPresent()) {
+ net.minecraft.server.MinecraftServer.LOGGER.error("Failed to complete future await for chunk " + chunkPos.toString() + " in world '" + ServerChunkCache.this.level.getWorld().getName() + "': " + either.right().get().toString());
+ }
+
+ try {
+ if (onLoad != null) {
+ onLoad.accept(either == null ? null : either.left().orElse(null)); // indicate failure to the callback.
+ }
+ } catch (Throwable thr) {
+ if (thr instanceof ThreadDeath) {
+ throw (ThreadDeath)thr;
+ }
+ net.minecraft.server.MinecraftServer.LOGGER.error("Load callback for future await failed " + chunkPos.toString() + " in world '" + ServerChunkCache.this.level.getWorld().getName() + "'", thr);
+ return;
+ }
+ } finally {
+ // due to odd behaviour with CB unload implementation we need to have these AFTER the load callback.
+ ServerChunkCache.this.distanceManager.addTicket(TicketType.UNKNOWN, chunkPos, ticketLevel, chunkPos);
+ ServerChunkCache.this.distanceManager.removeTicket(TicketType.FUTURE_AWAIT, chunkPos, ticketLevel, identifier);
+ }
+ }, this.mainThreadProcessor);
+ } + }
+ // Paper end + // Paper end
+ +
@ -5739,67 +6063,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ } + }
+ +
+ void getChunkAtAsynchronously(int chunkX, int chunkZ, int ticketLevel,
+ java.util.function.Consumer<ChunkAccess> consumer) {
+ this.getChunkAtAsynchronously(chunkX, chunkZ, ticketLevel, (ChunkHolder chunkHolder) -> {
+ if (ticketLevel <= 33) {
+ return (CompletableFuture)chunkHolder.getFullChunkFuture();
+ } else {
+ return chunkHolder.getOrScheduleFuture(ChunkHolder.getStatus(ticketLevel), ServerChunkCache.this.chunkMap);
+ }
+ }, consumer);
+ }
+
+ void getChunkAtAsynchronously(int chunkX, int chunkZ, int ticketLevel,
+ java.util.function.Function<ChunkHolder, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> function,
+ java.util.function.Consumer<ChunkAccess> consumer) {
+ if (Thread.currentThread() != this.mainThread) {
+ throw new IllegalStateException();
+ }
+
+ ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+ Long identifier = Long.valueOf(this.chunkFutureAwaitCounter++);
+ this.addTicketAtLevel(TicketType.FUTURE_AWAIT, chunkPos, ticketLevel, identifier);
+ this.runDistanceManagerUpdates();
+
+ ChunkHolder chunk = this.chunkMap.getUpdatingChunkIfPresent(chunkPos.toLong());
+
+ if (chunk == null) {
+ throw new IllegalStateException("Expected playerchunk " + chunkPos + " in world '" + this.level.getWorld().getName() + "'");
+ }
+
+ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = function.apply(chunk);
+
+ future.whenCompleteAsync((either, throwable) -> {
+ try {
+ if (throwable != null) {
+ if (throwable instanceof ThreadDeath) {
+ throw (ThreadDeath)throwable;
+ }
+ LOGGER.error("Failed to complete future await for chunk " + chunkPos.toString() + " in world '" + ServerChunkCache.this.level.getWorld().getName() + "'", throwable);
+ } else if (either.right().isPresent()) {
+ LOGGER.error("Failed to complete future await for chunk " + chunkPos.toString() + " in world '" + ServerChunkCache.this.level.getWorld().getName() + "': " + either.right().get().toString());
+ }
+
+ try {
+ if (consumer != null) {
+ consumer.accept(either == null ? null : either.left().orElse(null)); // indicate failure to the callback.
+ }
+ } catch (Throwable thr) {
+ if (thr instanceof ThreadDeath) {
+ throw (ThreadDeath)thr;
+ }
+ LOGGER.error("Load callback for future await failed " + chunkPos.toString() + " in world '" + ServerChunkCache.this.level.getWorld().getName() + "'", thr);
+ return;
+ }
+ } finally {
+ // due to odd behaviour with CB unload implementation we need to have these AFTER the load callback.
+ ServerChunkCache.this.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, ticketLevel, chunkPos);
+ ServerChunkCache.this.removeTicketAtLevel(TicketType.FUTURE_AWAIT, chunkPos, ticketLevel, identifier);
+ }
+ }, this.mainThreadProcessor);
+ }
+
+ public <T> void addTicketAtLevel(TicketType<T> ticketType, ChunkPos chunkPos, int ticketLevel, T identifier) { + public <T> void addTicketAtLevel(TicketType<T> ticketType, ChunkPos chunkPos, int ticketLevel, T identifier) {
+ this.distanceManager.addTicket(ticketType, chunkPos, ticketLevel, identifier); + this.distanceManager.addTicket(ticketType, chunkPos, ticketLevel, identifier);
+ } + }
@ -5808,74 +6071,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ this.distanceManager.removeTicket(ticketType, chunkPos, ticketLevel, identifier); + this.distanceManager.removeTicket(ticketType, chunkPos, ticketLevel, identifier);
+ } + }
+ +
+ void chunkLoadAccept(int chunkX, int chunkZ, ChunkAccess chunk, java.util.function.Consumer<ChunkAccess> consumer) { + public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<LevelChunk> tickingChunks = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true);
+ try { + public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<LevelChunk> entityTickingChunks = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true);
+ consumer.accept(chunk);
+ } catch (Throwable throwable) {
+ if (throwable instanceof ThreadDeath) {
+ throw (ThreadDeath)throwable;
+ }
+ LOGGER.error("Load callback for chunk " + chunkX + "," + chunkZ + " in world '" + this.level.getWorld().getName() + "' threw an exception", throwable);
+ }
+ }
+
+ public final void getChunkAtAsynchronously(int chunkX, int chunkZ, ChunkStatus status, boolean gen, boolean allowSubTicketLevel, java.util.function.Consumer<ChunkAccess> onLoad) {
+ // try to fire sync
+ int chunkStatusTicketLevel = 33 + ChunkStatus.getDistance(status);
+ ChunkHolder playerChunk = this.chunkMap.getUpdatingChunkIfPresent(io.papermc.paper.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
+ if (playerChunk != null) {
+ ChunkStatus holderStatus = playerChunk.getChunkHolderStatus();
+ ChunkAccess immediate = playerChunk.getAvailableChunkNow();
+ if (immediate != null) {
+ if (allowSubTicketLevel ? immediate.getStatus().isOrAfter(status) : (playerChunk.getTicketLevel() <= chunkStatusTicketLevel && holderStatus != null && holderStatus.isOrAfter(status))) {
+ this.chunkLoadAccept(chunkX, chunkZ, immediate, onLoad);
+ return;
+ } else {
+ if (gen || (!allowSubTicketLevel && immediate.getStatus().isOrAfter(status))) {
+ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad);
+ return;
+ } else {
+ this.chunkLoadAccept(chunkX, chunkZ, null, onLoad);
+ return;
+ }
+ }
+ }
+ }
+
+ // need to fire async
+
+ if (gen && !allowSubTicketLevel) {
+ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad);
+ return;
+ }
+
+ this.getChunkAtAsynchronously(chunkX, chunkZ, net.minecraft.server.MCUtil.getTicketLevelFor(ChunkStatus.EMPTY), (ChunkAccess chunk) -> {
+ if (chunk == null) {
+ throw new IllegalStateException("Chunk cannot be null");
+ }
+
+ if (!chunk.getStatus().isOrAfter(status)) {
+ if (gen) {
+ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad);
+ return;
+ } else {
+ ServerChunkCache.this.chunkLoadAccept(chunkX, chunkZ, null, onLoad);
+ return;
+ }
+ } else {
+ if (allowSubTicketLevel) {
+ ServerChunkCache.this.chunkLoadAccept(chunkX, chunkZ, chunk, onLoad);
+ return;
+ } else {
+ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad);
+ return;
+ }
+ }
+ });
+ }
+
+ final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<LevelChunk> tickingChunks = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true);
+ final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<LevelChunk> entityTickingChunks = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true);
+ // Paper end + // Paper end
public ServerChunkCache(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, int simulationDistance, boolean dsync, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory) { public ServerChunkCache(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, int simulationDistance, boolean dsync, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory) {
@ -5989,11 +6186,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return true; + return true;
+ } + }
+ +
+ public final void loadChunksForMoveAsync(AABB axisalignedbb, double toX, double toZ, + public final void loadChunksForMoveAsync(AABB axisalignedbb, ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority priority,
+ java.util.function.Consumer<List<net.minecraft.world.level.chunk.ChunkAccess>> onLoad) { + java.util.function.Consumer<List<net.minecraft.world.level.chunk.ChunkAccess>> onLoad) {
+ if (Thread.currentThread() != this.thread) { + if (Thread.currentThread() != this.thread) {
+ this.getChunkSource().mainThreadProcessor.execute(() -> { + this.getChunkSource().mainThreadProcessor.execute(() -> {
+ this.loadChunksForMoveAsync(axisalignedbb, toX, toZ, onLoad); + this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad);
+ }); + });
+ return; + return;
+ } + }
@ -6043,7 +6240,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ +
+ for (int cx = minChunkX; cx <= maxChunkX; ++cx) { + for (int cx = minChunkX; cx <= maxChunkX; ++cx) {
+ for (int cz = minChunkZ; cz <= maxChunkZ; ++cz) { + for (int cz = minChunkZ; cz <= maxChunkZ; ++cz) {
+ chunkProvider.getChunkAtAsynchronously(cx, cz, net.minecraft.world.level.chunk.ChunkStatus.FULL, true, false, consumer); + net.minecraft.server.ChunkSystem.scheduleChunkLoad(
+ this, cx, cz, net.minecraft.world.level.chunk.ChunkStatus.FULL, true, priority, consumer
+ );
+ } + }
+ } + }
+ } + }
@ -6790,6 +6989,128 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
@Override @Override
public BlockState getBlockState(BlockPos pos) { public BlockState getBlockState(BlockPos pos) {
int i = pos.getY(); int i = pos.getY();
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
@@ -0,0 +0,0 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private boolean addEntity(T entity, boolean existing) {
+ // Paper start - chunk system hooks
+ if (existing) {
+ // I don't want to know why this is a generic type.
+ Entity entityCasted = (Entity)entity;
+ boolean wasRemoved = entityCasted.isRemoved();
+ net.minecraft.server.ChunkSystem.onEntityPreAdd((net.minecraft.server.level.ServerLevel)entityCasted.level, entityCasted);
+ if (!wasRemoved && entityCasted.isRemoved()) {
+ // removed by callback
+ return false;
+ }
+ }
+ // Paper end - chunk system hooks
if (!this.addEntityUuid(entity)) {
return false;
} else {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public Chunk[] getLoadedChunks() {
- Long2ObjectLinkedOpenHashMap<ChunkHolder> chunks = this.world.getChunkSource().chunkMap.visibleChunkMap;
- return chunks.values().stream().map(ChunkHolder::getFullChunkNow).filter(Objects::nonNull).map(net.minecraft.world.level.chunk.LevelChunk::getBukkitChunk).toArray(Chunk[]::new);
+ List<ChunkHolder> chunks = net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.world); // Paper
+ return chunks.stream().map(ChunkHolder::getFullChunkNow).filter(Objects::nonNull).map(net.minecraft.world.level.chunk.LevelChunk::getBukkitChunk).toArray(Chunk[]::new);
}
@Override
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean refreshChunk(int x, int z) {
- ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.visibleChunkMap.get(ChunkPos.asLong(x, z));
+ ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z));
if (playerChunk == null) return false;
playerChunk.getTickingChunkFuture().thenAccept(either -> {
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return this.spigot;
}
// Spigot end
+ // Paper start
+ public java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) {
+ if (Bukkit.isPrimaryThread()) {
+ net.minecraft.world.level.chunk.LevelChunk immediate = this.world.getChunkSource().getChunkAtIfLoadedImmediately(x, z);
+ if (immediate != null) {
+ return java.util.concurrent.CompletableFuture.completedFuture(immediate.getBukkitChunk());
+ }
+ }
+
+ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority priority;
+ if (urgent) {
+ priority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHER;
+ } else {
+ priority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL;
+ }
+
+ java.util.concurrent.CompletableFuture<Chunk> ret = new java.util.concurrent.CompletableFuture<>();
+
+ net.minecraft.server.ChunkSystem.scheduleChunkLoad(this.getHandle(), x, z, gen, ChunkStatus.FULL, true, priority, (c) -> {
+ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> {
+ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)c;
+ ret.complete(chunk == null ? null : chunk.getBukkitChunk());
+ });
+ });
+
+ return ret;
+ }
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -0,0 +0,0 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
return this.spigot;
}
// Spigot end
+
+ // Paper start
+ @Override
+ public java.util.concurrent.CompletableFuture<Boolean> teleportAsync(Location location, TeleportCause cause) {
+ Preconditions.checkArgument(location != null, "location");
+ location.checkFinite();
+ Location locationClone = location.clone(); // clone so we don't need to worry about mutations after this call.
+
+ net.minecraft.server.level.ServerLevel world = ((CraftWorld)locationClone.getWorld()).getHandle();
+ java.util.concurrent.CompletableFuture<Boolean> ret = new java.util.concurrent.CompletableFuture<>();
+
+ world.loadChunksForMoveAsync(getHandle().getBoundingBoxAt(locationClone.getX(), locationClone.getY(), locationClone.getZ()),
+ this instanceof CraftPlayer ? ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHER : ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL, (list) -> {
+ net.minecraft.server.level.ServerChunkCache chunkProviderServer = world.getChunkSource();
+ for (net.minecraft.world.level.chunk.ChunkAccess chunk : list) {
+ chunkProviderServer.addTicketAtLevel(net.minecraft.server.level.TicketType.POST_TELEPORT, chunk.getPos(), 33, CraftEntity.this.getEntityId());
+ }
+ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> {
+ try {
+ ret.complete(CraftEntity.this.teleport(locationClone, cause) ? Boolean.TRUE : Boolean.FALSE);
+ } catch (Throwable throwable) {
+ if (throwable instanceof ThreadDeath) {
+ throw (ThreadDeath)throwable;
+ }
+ net.minecraft.server.MinecraftServer.LOGGER.error("Failed to teleport entity " + CraftEntity.this, throwable);
+ ret.completeExceptionally(throwable);
+ }
+ });
+ });
+
+ return ret;
+ }
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java --- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java

View File

@ -24,7 +24,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- -
for (int i = 0; longiterator.hasNext() && (shouldKeepTicking.getAsBoolean() || i < 200 || this.toDrop.size() > 2000); longiterator.remove()) { for (int i = 0; longiterator.hasNext() && (shouldKeepTicking.getAsBoolean() || i < 200 || this.toDrop.size() > 2000); longiterator.remove()) {
long j = longiterator.nextLong(); long j = longiterator.nextLong();
ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j); ChunkHolder playerchunk = this.updatingChunks.queueRemove(j); // Paper - Don't copy
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} }
} }

View File

@ -88,9 +88,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end + // Paper end
} }
// Paper start - Chunk priority
@@ -0,0 +0,0 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override @Override
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) { public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
+ // Paper start - Teleport API + // Paper start - Teleport API

View File

@ -14,6 +14,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+package io.papermc.paper.util; +package io.papermc.paper.util;
+ +
+import net.minecraft.server.MinecraftServer; +import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Entity;
+import org.bukkit.Bukkit; +import org.bukkit.Bukkit;
+import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicInteger;
@ -42,15 +43,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ } + }
+ +
+ public static void ensureTickThread(final int chunkX, final int chunkZ, final String reason) { + public static void ensureTickThread(final ServerLevel world, final int chunkX, final int chunkZ, final String reason) {
+ if (!isTickThreadFor(chunkX, chunkZ)) { + if (!isTickThreadFor(world, chunkX, chunkZ)) {
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); + MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
+ throw new IllegalStateException(reason); + throw new IllegalStateException(reason);
+ } + }
+ } + }
+ +
+ public static void ensureTickThread(final Entity entity, final String reason) { + public static void ensureTickThread(final Entity entity, final String reason) {
+ if (!isTickThreadFor(entity.chunkPosition().x, entity.chunkPosition().z)) { + if (!isTickThreadFor(entity)) {
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); + MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
+ throw new IllegalStateException(reason); + throw new IllegalStateException(reason);
+ } + }
@ -81,7 +82,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return Bukkit.isPrimaryThread(); + return Bukkit.isPrimaryThread();
+ } + }
+ +
+ public static boolean isTickThreadFor(final int chunkX, final int chunkZ) { + public static boolean isTickThreadFor(final ServerLevel world, final int chunkX, final int chunkZ) {
+ return Bukkit.isPrimaryThread(); + return Bukkit.isPrimaryThread();
+ } + }
+ +

View File

@ -79,8 +79,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- } - }
+ // Paper - move into try block to only write if successfully serialized + // Paper - move into try block to only write if successfully serialized
} }
// Paper start
// Paper start return;
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { @@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
} }

View File

@ -10,9 +10,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -0,0 +0,0 @@ public class ChunkHolder { @@ -0,0 +0,0 @@ public class ChunkHolder {
long key = net.minecraft.server.MCUtil.getCoordinateKey(this.pos);
this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key); this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key); this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
// Paper end - optimise anyPlayerCloseEnoughForSpawning
+ // Paper start - optimise chunk tick iteration + // Paper start - optimise chunk tick iteration
+ if (this.needsBroadcastChanges()) { + if (this.needsBroadcastChanges()) {
+ this.chunkMap.needsChangeBroadcasting.add(this); + this.chunkMap.needsChangeBroadcasting.add(this);
@ -20,17 +20,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - optimise chunk tick iteration + // Paper end - optimise chunk tick iteration
} }
void onChunkRemove() { public void onChunkRemove() {
@@ -0,0 +0,0 @@ public class ChunkHolder {
this.playersInMobSpawnRange = null; this.playersInMobSpawnRange = null;
this.playersInChunkTickRange = null; this.playersInChunkTickRange = null;
// Paper end - optimise anyPlayerCloseEnoughForSpawning
+ // Paper start - optimise chunk tick iteration + // Paper start - optimise chunk tick iteration
+ if (this.needsBroadcastChanges()) { + if (this.needsBroadcastChanges()) {
+ this.chunkMap.needsChangeBroadcasting.remove(this); + this.chunkMap.needsChangeBroadcasting.remove(this);
+ } + }
+ // Paper end - optimise chunk tick iteration + // Paper end - optimise chunk tick iteration
} }
// Paper end - optimise anyPlayerCloseEnoughForSpawning // Paper end
long lastAutoSaveTime; // Paper - incremental autosave
@@ -0,0 +0,0 @@ public class ChunkHolder { @@ -0,0 +0,0 @@ public class ChunkHolder {
if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296 if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
@ -158,13 +160,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
} }
+ // Paper start - optimise chunk tick iteration + // Paper start - optimise chunk tick iteration
+ } + }
} + }
+ +
+ } finally { + } finally {
+ if (iterator1 instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator safeIterator) { + if (iterator1 instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator safeIterator) {
+ safeIterator.finishedIterating(); + safeIterator.finishedIterating();
+ } + }
+ } }
+ // Paper end - optimise chunk tick iteration + // Paper end - optimise chunk tick iteration
this.level.timings.chunkTicks.stopTiming(); // Paper this.level.timings.chunkTicks.stopTiming(); // Paper
gameprofilerfiller.popPush("customSpawners"); gameprofilerfiller.popPush("customSpawners");

View File

@ -24,7 +24,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - optimise checkDespawn + // Paper end - optimise checkDespawn
} }
void onChunkRemove() { public void onChunkRemove() {
@@ -0,0 +0,0 @@ public class ChunkHolder { @@ -0,0 +0,0 @@ public class ChunkHolder {
this.chunkMap.needsChangeBroadcasting.remove(this); this.chunkMap.needsChangeBroadcasting.remove(this);
} }
@ -36,8 +36,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ // Paper end - optimise checkDespawn + // Paper end - optimise checkDespawn
} }
// Paper end - optimise anyPlayerCloseEnoughForSpawning // Paper end
long lastAutoSaveTime; // Paper - incremental autosave
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java --- a/src/main/java/net/minecraft/server/level/ChunkMap.java

View File

@ -10,37 +10,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -0,0 +0,0 @@ public class ChunkHolder { @@ -0,0 +0,0 @@ public class ChunkHolder {
boolean isUpdateQueued = false; // Paper
private final ChunkMap chunkMap; // Paper // Paper start
public void onChunkAdd() {
-
+ // Paper start - optimise anyPlayerCloseEnoughForSpawning
+ long key = net.minecraft.server.MCUtil.getCoordinateKey(this.pos);
+ this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
+ this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
+ // Paper end - optimise anyPlayerCloseEnoughForSpawning
}
public void onChunkRemove() {
-
+ // Paper start - optimise anyPlayerCloseEnoughForSpawning
+ this.playersInMobSpawnRange = null;
+ this.playersInChunkTickRange = null;
+ // Paper end - optimise anyPlayerCloseEnoughForSpawning
}
// Paper end
+ // Paper start - optimise anyPlayerCloseEnoughForSpawning + // Paper start - optimise anyPlayerCloseEnoughForSpawning
+ // cached here to avoid a map lookup + // cached here to avoid a map lookup
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInMobSpawnRange; + com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInMobSpawnRange;
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInChunkTickRange; + com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInChunkTickRange;
+
+ void onChunkAdd() {
+ long key = net.minecraft.server.MCUtil.getCoordinateKey(this.pos);
+ this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
+ this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
+ }
+
+ void onChunkRemove() {
+ this.playersInMobSpawnRange = null;
+ this.playersInChunkTickRange = null;
+ }
+ // Paper end - optimise anyPlayerCloseEnoughForSpawning + // Paper end - optimise anyPlayerCloseEnoughForSpawning
+ +
public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) { public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) {
this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size()); this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
@@ -0,0 +0,0 @@ public class ChunkHolder {
this.setTicketLevel(level);
this.changedBlocksPerSection = new ShortSet[world.getSectionsCount()];
this.chunkMap = (ChunkMap)playersWatchingChunkProvider; // Paper
+ this.onChunkAdd(); // Paper - optimise anyPlayerCloseEnoughForSpawning
}
// Paper start
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
@ -127,22 +125,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
} }
protected ChunkGenerator generator() { protected ChunkGenerator generator() {
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
holder = (ChunkHolder) this.pendingUnloads.remove(pos);
if (holder != null) {
holder.setTicketLevel(level);
+ holder.onChunkAdd(); // Paper - optimise anyPlayerCloseEnoughForSpawning - PUT HERE AFTER RE-ADDING ONLY
} else {
holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this);
// Paper start
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j);
if (playerchunk != null) {
+ playerchunk.onChunkRemove(); // Paper
this.pendingUnloads.put(j, playerchunk);
this.modified = true;
++i;
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return this.anyPlayerCloseEnoughForSpawning(pos, false); return this.anyPlayerCloseEnoughForSpawning(pos, false);
} }
@ -243,8 +225,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- this.naturalSpawnChunkCounter.runAllUpdates(); - this.naturalSpawnChunkCounter.runAllUpdates();
+ //this.f.a(); // Paper - no longer used + //this.f.a(); // Paper - no longer used
this.tickingTicketsTracker.runAllUpdates(); this.tickingTicketsTracker.runAllUpdates();
org.spigotmc.AsyncCatcher.catchOp("DistanceManagerTick"); // Paper
this.playerTicketManager.runAllUpdates(); this.playerTicketManager.runAllUpdates();
int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE);
@@ -0,0 +0,0 @@ public abstract class DistanceManager { @@ -0,0 +0,0 @@ public abstract class DistanceManager {
((ObjectSet) this.playersPerChunk.computeIfAbsent(i, (j) -> { ((ObjectSet) this.playersPerChunk.computeIfAbsent(i, (j) -> {
return new ObjectOpenHashSet(); return new ObjectOpenHashSet();

View File

@ -42,9 +42,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ @Override + @Override
+ public int getTileEntityCount() { + public int getTileEntityCount() {
+ // We don't use the full world tile entity list, so we must iterate chunks + // We don't use the full world tile entity list, so we must iterate chunks
+ Long2ObjectLinkedOpenHashMap<ChunkHolder> chunks = world.getChunkSource().chunkMap.visibleChunkMap;
+ int size = 0; + int size = 0;
+ for (ChunkHolder playerchunk : chunks.values()) { + for (ChunkHolder playerchunk : net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.world)) {
+ net.minecraft.world.level.chunk.LevelChunk chunk = playerchunk.getTickingChunk(); + net.minecraft.world.level.chunk.LevelChunk chunk = playerchunk.getTickingChunk();
+ if (chunk == null) { + if (chunk == null) {
+ continue; + continue;
@ -63,7 +62,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ public int getChunkCount() { + public int getChunkCount() {
+ int ret = 0; + int ret = 0;
+ +
+ for (ChunkHolder chunkHolder : world.getChunkSource().chunkMap.visibleChunkMap.values()) { + for (ChunkHolder chunkHolder : net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.world)) {
+ if (chunkHolder.getTickingChunk() != null) { + if (chunkHolder.getTickingChunk() != null) {
+ ++ret; + ++ret;
+ } + }

View File

@ -1284,7 +1284,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ worldData.addProperty("tick-view-distance", world.getChunkSource().chunkMap.playerChunkManager.getTargetTickViewDistance()); // Paper - replace chunk loader system + worldData.addProperty("tick-view-distance", world.getChunkSource().chunkMap.playerChunkManager.getTargetTickViewDistance()); // Paper - replace chunk loader system
worldData.addProperty("keep-spawn-loaded", world.keepSpawnInMemory); worldData.addProperty("keep-spawn-loaded", world.keepSpawnInMemory);
worldData.addProperty("keep-spawn-loaded-range", world.paperConfig().spawn.keepSpawnLoadedRange * 16); worldData.addProperty("keep-spawn-loaded-range", world.paperConfig().spawn.keepSpawnLoadedRange * 16);
worldData.addProperty("visible-chunk-count", visibleChunks.size()); worldData.addProperty("visible-chunk-count", allChunks.size());
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
@ -1305,8 +1305,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ // Paper end - no-tick view distance + // Paper end - no-tick view distance
// Paper start - optimise anyPlayerCloseEnoughForSpawning // Paper start
// cached here to avoid a map lookup public void onChunkAdd() {
@@ -0,0 +0,0 @@ public class ChunkHolder { @@ -0,0 +0,0 @@ public class ChunkHolder {
public void blockChanged(BlockPos pos) { public void blockChanged(BlockPos pos) {
@ -1440,7 +1440,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
this.viewDistance = j; this.viewDistance = j;
- this.distanceManager.updatePlayerTickets(this.viewDistance + 1); - this.distanceManager.updatePlayerTickets(this.viewDistance + 1);
- Iterator objectiterator = this.updatingChunks.getVisibleValuesCopy().iterator(); // Paper - Iterator objectiterator = net.minecraft.server.ChunkSystem.getUpdatingChunkHolders(this.level).iterator(); // Paper
- -
- while (objectiterator.hasNext()) { - while (objectiterator.hasNext()) {
- ChunkHolder playerchunk = (ChunkHolder) objectiterator.next(); - ChunkHolder playerchunk = (ChunkHolder) objectiterator.next();
@ -1484,7 +1484,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
CsvOutput csvwriter = CsvOutput.builder().addColumn("x").addColumn("z").addColumn("level").addColumn("in_memory").addColumn("status").addColumn("full_status").addColumn("accessible_ready").addColumn("ticking_ready").addColumn("entity_ticking_ready").addColumn("ticket").addColumn("spawning").addColumn("block_entity_count").addColumn("ticking_ticket").addColumn("ticking_level").addColumn("block_ticks").addColumn("fluid_ticks").build(writer); CsvOutput csvwriter = CsvOutput.builder().addColumn("x").addColumn("z").addColumn("level").addColumn("in_memory").addColumn("status").addColumn("full_status").addColumn("accessible_ready").addColumn("ticking_ready").addColumn("entity_ticking_ready").addColumn("ticket").addColumn("spawning").addColumn("block_entity_count").addColumn("ticking_ticket").addColumn("ticking_level").addColumn("block_ticks").addColumn("fluid_ticks").build(writer);
- TickingTracker tickingtracker = this.distanceManager.tickingTracker(); - TickingTracker tickingtracker = this.distanceManager.tickingTracker();
+ // Paper - replace loader system + // Paper - replace loader system
ObjectBidirectionalIterator objectbidirectionaliterator = this.updatingChunks.getVisibleMap().clone().long2ObjectEntrySet().fastIterator(); // Paper Iterator<ChunkHolder> objectbidirectionaliterator = net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).iterator(); // Paper
while (objectbidirectionaliterator.hasNext()) { while (objectbidirectionaliterator.hasNext()) {
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider

View File

@ -15,7 +15,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java --- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { @@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
if (regionfile != null) { // Paper end
return regionfile; return regionfile;
} else { } else {
- if (this.regionCache.size() >= 256) { - if (this.regionCache.size() >= 256) {

View File

@ -99,6 +99,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "------------------------------" );
- log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Spigot!):" ); - log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Spigot!):" );
+ log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper + log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "------------------------------" );
//

View File

@ -87,8 +87,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
return this.world.getChunk(x >> 4, z >> 4).getHeight(CraftHeightMap.toNMS(heightMap), x, z); return this.world.getChunk(x >> 4, z >> 4).getHeight(CraftHeightMap.toNMS(heightMap), x, z);
} }
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
// Spigot end
// Paper start // Paper start
@Override
public java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) { public java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) {
+ warnUnsafeChunk("getting a faraway chunk async", x, z); // Paper + warnUnsafeChunk("getting a faraway chunk async", x, z); // Paper
if (Bukkit.isPrimaryThread()) { if (Bukkit.isPrimaryThread()) {

View File

@ -304,9 +304,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
protected ChunkGenerator generator() { protected ChunkGenerator generator() {
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}); }
} }
// Paper end
+ // Paper start + // Paper start
+ public void updatePlayerMobTypeMap(Entity entity) { + public void updatePlayerMobTypeMap(Entity entity) {
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { + if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
@ -331,10 +331,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return entityPlayer.mobCounts[mobCategory.ordinal()]; + return entityPlayer.mobCounts[mobCategory.ordinal()];
+ } + }
+ // Paper end + // Paper end
+
private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) { private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8); double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
double d1 = (double) SectionPos.sectionToBlockCoord(pos.z, 8);
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java --- a/src/main/java/net/minecraft/server/level/DistanceManager.java
@ -398,8 +397,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
this.maxHealthCache = this.getMaxHealth(); this.maxHealthCache = this.getMaxHealth();
+ this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper + this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
} }
// Paper start - Chunk priority
// Yes, this doesn't match Vanilla, but it's the best we can do for now. public BlockPos getPointInFront(double inFront) {
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java --- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java

View File

@ -57,8 +57,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -0,0 +0,0 @@ public class ChunkHolder { @@ -0,0 +0,0 @@ public class ChunkHolder {
this.playersInChunkTickRange = null; com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInMobSpawnRange;
} com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInChunkTickRange;
// Paper end - optimise anyPlayerCloseEnoughForSpawning // Paper end - optimise anyPlayerCloseEnoughForSpawning
+ long lastAutoSaveTime; // Paper - incremental autosave + long lastAutoSaveTime; // Paper - incremental autosave
+ long inactiveTimeStart; // Paper - incremental autosave + long inactiveTimeStart; // Paper - incremental autosave
@ -107,18 +107,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ } + }
+ // Paper end + // Paper end
} + }
+
+ // Paper start - incremental autosave + // Paper start - incremental autosave
+ public boolean setHasBeenLoaded() { + public boolean setHasBeenLoaded() {
+ this.wasAccessibleSinceLastSave = getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER); + this.wasAccessibleSinceLastSave = getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
+ return this.wasAccessibleSinceLastSave; + return this.wasAccessibleSinceLastSave;
+ } }
+ // Paper end + // Paper end
+
public void replaceProtoChunk(ImposterProtoChunk chunk) { public void replaceProtoChunk(ImposterProtoChunk chunk) {
for (int i = 0; i < this.futures.length(); ++i) { for (int i = 0; i < this.futures.length(); ++i) {
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = (CompletableFuture) this.futures.get(i);
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
@ -194,13 +193,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end + // Paper end
+ +
protected void saveAllChunks(boolean flush) { protected void saveAllChunks(boolean flush) {
if (flush) { // Paper start - do not overload I/O threads with too much work when saving
List<ChunkHolder> list = (List) this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); int[] saved = new int[1];
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} }
int l = 0; int l = 0;
- ObjectIterator objectiterator = this.visibleChunkMap.values().iterator(); - Iterator objectiterator = net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).iterator(); // Paper
- -
- while (l < 20 && shouldKeepTicking.getAsBoolean() && objectiterator.hasNext()) { - while (l < 20 && shouldKeepTicking.getAsBoolean() && objectiterator.hasNext()) {
- if (this.saveChunkIfNeeded((ChunkHolder) objectiterator.next())) { - if (this.saveChunkIfNeeded((ChunkHolder) objectiterator.next())) {