mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-15 14:13:56 +01:00
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:
parent
61a8488806
commit
90da9124c5
38 changed files with 7792 additions and 795 deletions
|
@ -9,18 +9,30 @@ sometimes it is.
|
|||
This patch also prevents the saving/unloading of POI data when
|
||||
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
|
||||
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
|
||||
}
|
||||
+ 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
|
||||
|
||||
private void processUnloads(BooleanSupplier shouldKeepTicking) {
|
||||
|
@ -30,22 +42,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
long j = longiterator.nextLong();
|
||||
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
|
||||
}
|
||||
this.poiManager.loadInData(pos, chunkHolder.poiData);
|
||||
|
|
|
@ -32,9 +32,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- a/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 {
|
||||
}
|
||||
|
||||
public void placeNewPlayer(Connection connection, ServerPlayer player) {
|
||||
player.isRealPlayer = true; // Paper - Chunk priority
|
||||
+ player.loginTime = System.currentTimeMillis(); // Paper
|
||||
GameProfile gameprofile = player.getGameProfile();
|
||||
GameProfileCache usercache = this.server.getProfileCache();
|
||||
|
|
|
@ -122,6 +122,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ // Paper end - Different message for short timeout
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
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 );
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
//
|
||||
|
|
|
@ -112,10 +112,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
return true;
|
||||
// Paper end
|
||||
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
|
||||
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
|
||||
return java.util.concurrent.CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk());
|
||||
}, net.minecraft.server.MinecraftServer.getServer());
|
||||
}
|
||||
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;
|
||||
+ if (chunk != null) addTicket(x, z); // Paper
|
||||
ret.complete(chunk == null ? null : chunk.getBukkitChunk());
|
||||
});
|
||||
});
|
||||
|
|
|
@ -85,4 +85,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ // Paper end
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
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
|
||||
|
|
|
@ -2703,9 +2703,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
private static final Random rand = new Random();
|
||||
|
||||
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
return this.spigot;
|
||||
|
||||
return ret;
|
||||
}
|
||||
// Spigot end
|
||||
+
|
||||
+ // Paper start - implement pointers
|
||||
+ @Override
|
||||
|
@ -2719,7 +2719,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+
|
||||
+ 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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
|
|
|
@ -16,7 +16,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ this.getProfileCache().save(false); // Paper
|
||||
}
|
||||
// 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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
|
|
|
@ -24,9 +24,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
|
||||
private boolean addEntity(T entity, boolean existing) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity add"); // Paper
|
||||
if (!this.addEntityUuid(entity)) {
|
||||
return false;
|
||||
} else {
|
||||
// Paper start - chunk system hooks
|
||||
if (existing) {
|
||||
// I don't want to know why this is a generic type.
|
||||
@@ -0,0 +0,0 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]))); // Paper
|
||||
return;
|
||||
}
|
||||
// Paper start
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
}
|
||||
// Paper end
|
||||
// CraftBukkit end
|
||||
+ // Paper start - async tab completion
|
||||
+ TAB_COMPLETE_EXECUTOR.execute(() -> {
|
||||
|
|
|
@ -2282,7 +2282,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- a/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
|
||||
this.getProfileCache().save(false); // Paper
|
||||
this.getProfileCache().save();
|
||||
}
|
||||
// Spigot end
|
||||
-
|
||||
|
@ -2302,6 +2302,31 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
this.poiManager.close();
|
||||
} finally {
|
||||
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
|
||||
this.processUnloads(() -> {
|
||||
return true;
|
||||
|
@ -2310,7 +2335,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ //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);
|
||||
net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).forEach(this::saveChunkIfNeeded);
|
||||
}
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
protected void tick(BooleanSupplier shouldKeepTicking) {
|
||||
|
@ -2824,7 +2849,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- a/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
|
||||
server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]))); // Paper
|
||||
this.disconnect(Component.translatable("disconnect.spam"));
|
||||
return;
|
||||
}
|
||||
+ // Paper start
|
||||
|
@ -2835,8 +2860,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ // Paper end
|
||||
// CraftBukkit end
|
||||
// Paper start - async tab completion
|
||||
TAB_COMPLETE_EXECUTOR.execute(() -> {
|
||||
StringReader stringreader = new StringReader(packet.getCommand());
|
||||
|
||||
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
|
||||
--- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
|
||||
|
@ -3365,7 +3390,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ // Paper end
|
||||
return regionfile;
|
||||
} 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 {
|
||||
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, true); // CraftBukkit // Paper
|
||||
+ try { // Paper
|
||||
int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper
|
||||
|
||||
if (nbt == null) {
|
||||
regionfile.clear(pos);
|
||||
@@ -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
|
||||
+ regionfile.fileLock.unlock();
|
||||
+ } // Paper end
|
||||
|
@ -3554,95 +3579,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
}
|
||||
+ // 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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
@@ -0,0 +0,0 @@ public class WatchdogThread extends Thread
|
||||
// Paper end - Different message for short timeout
|
||||
//
|
||||
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
|
||||
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
|
|
|
@ -564,11 +564,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
|
||||
+import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet;
|
||||
import java.lang.ref.Cleaner;
|
||||
@@ -0,0 +0,0 @@ import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
@@ -0,0 +0,0 @@ import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
+import com.mojang.authlib.GameProfile;
|
||||
import org.bukkit.Location;
|
||||
|
|
|
@ -22,9 +22,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
import net.minecraft.core.Direction;
|
||||
+import net.minecraft.nbt.CompoundTag;
|
||||
+import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
@@ -0,0 +0,0 @@ public final class MCUtil {
|
||||
}
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ return null;
|
||||
+ }
|
||||
+
|
||||
public static int getTicketLevelFor(net.minecraft.world.level.chunk.ChunkStatus status) {
|
||||
return net.minecraft.server.level.ChunkMap.MAX_VIEW_DISTANCE + net.minecraft.world.level.chunk.ChunkStatus.getDistance(status);
|
||||
public static ChunkStatus getChunkStatus(ChunkHolder chunk) {
|
||||
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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
|
|
|
@ -23,29 +23,29 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- a/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 {
|
||||
|
||||
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
|
||||
|
||||
if (nbt == null) {
|
||||
regionfile.clear(pos);
|
||||
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
dataoutputstream.close();
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ return;
|
||||
+ // Paper start
|
||||
+ return;
|
||||
+ } catch (Exception ex) {
|
||||
+ laste = ex;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
|
||||
+ if (laste != null) {
|
||||
+ 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
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
} finally { // Paper start
|
||||
regionfile.fileLock.unlock();
|
||||
} // Paper end
|
||||
|
|
|
@ -148,14 +148,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ int ticking = 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) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ ++total;
|
||||
+
|
||||
+ ChunkHolder.FullChunkStatus state = ChunkHolder.getFullChunkStatus(chunk.getTicketLevel());
|
||||
+ ChunkHolder.FullChunkStatus state = chunk.getFullStatus();
|
||||
+
|
||||
+ switch (state) {
|
||||
+ 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
|
||||
--- a/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.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
+import net.minecraft.server.level.ChunkHolder;
|
||||
+import net.minecraft.server.level.ChunkMap;
|
||||
+import net.minecraft.server.level.DistanceManager;
|
||||
+import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
+import net.minecraft.server.level.ServerPlayer;
|
||||
+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.ChunkStatus;
|
||||
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.block.BlockFace;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
@@ -0,0 +0,0 @@ import org.spigotmc.AsyncCatcher;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
+import java.io.*;
|
||||
+import java.util.ArrayList;
|
||||
+import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
+import java.util.Set;
|
||||
|
@ -267,19 +267,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
@@ -0,0 +0,0 @@ public final class MCUtil {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+ public static ChunkStatus getChunkStatus(ChunkHolder chunk) {
|
||||
+ List<ChunkStatus> statuses = net.minecraft.server.level.ServerChunkCache.CHUNK_STATUSES;
|
||||
+ 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
|
||||
+ return chunk.getChunkHolderStatus();
|
||||
+ }
|
||||
+
|
||||
+ public static void dumpChunks(File file) throws IOException {
|
||||
|
@ -337,9 +329,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+
|
||||
+ ServerLevel world = ((org.bukkit.craftbukkit.CraftWorld)bukkitWorld).getHandle();
|
||||
+ ChunkMap chunkMap = world.getChunkSource().chunkMap;
|
||||
+ Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunks = chunkMap.visibleChunkMap;
|
||||
+ 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;
|
||||
+
|
||||
+ int fullLoadedChunks = 0;
|
||||
|
@ -362,7 +353,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ worldData.addProperty("view-distance", world.spigotConfig.viewDistance);
|
||||
+ worldData.addProperty("keep-spawn-loaded", world.keepSpawnInMemory);
|
||||
+ 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("verified-fully-loaded-chunks", fullLoadedChunks);
|
||||
+
|
||||
|
@ -431,7 +422,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+
|
||||
+ 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);
|
||||
+ }
|
||||
+ }
|
||||
|
|
|
@ -15,7 +15,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ boolean isUpdateQueued = false; // 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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
|
|
6864
patches/server/ConcurrentUtil.patch
Normal file
6864
patches/server/ConcurrentUtil.patch
Normal file
File diff suppressed because it is too large
Load diff
|
@ -18,7 +18,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ 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) {
|
||||
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
|
||||
|
||||
@Nullable
|
||||
|
@ -41,9 +41,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
boolean removed;
|
||||
if ((removed = this.pendingUnloads.remove(pos, holder)) && ichunkaccess != null) {
|
||||
@@ -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
|
||||
} else if (removed) { // Paper start
|
||||
net.minecraft.server.ChunkSystem.onChunkHolderDelete(this.level, holder);
|
||||
} // Paper end
|
||||
+ } finally { this.unloadingPlayerChunk = unloadingBefore; } // Paper - do not allow ticket level changes while unloading chunks
|
||||
|
||||
}
|
||||
|
|
|
@ -8,32 +8,39 @@ tickDistanceManager call can take up quite a bit in
|
|||
the function. I saw approximately 1/3rd of the function
|
||||
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
|
||||
--- a/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
|
||||
@@ -0,0 +0,0 @@ public final class ChunkDebugCommand implements PaperSubcommand {
|
||||
int ticking = 0;
|
||||
int entityTicking = 0;
|
||||
--- 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 (final ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunkMap.values()) {
|
||||
+ for (final ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunks.getUpdatingMap().values()) { // Paper - change updating chunks map
|
||||
if (chunk.getFullChunkNowUnchecked() == null) {
|
||||
continue;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- 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 {
|
||||
public static List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
|
||||
- return new ArrayList<>(level.chunkSource.chunkMap.visibleChunkMap.values());
|
||||
+ if (Bukkit.isPrimaryThread()) {
|
||||
+ return level.chunkSource.chunkMap.updatingChunks.getVisibleValuesCopy();
|
||||
+ }
|
||||
+ synchronized (level.chunkSource.chunkMap.updatingChunks) {
|
||||
+ return level.chunkSource.chunkMap.updatingChunks.getVisibleValuesCopy();
|
||||
+ }
|
||||
}
|
||||
|
||||
ServerLevel world = ((org.bukkit.craftbukkit.CraftWorld)bukkitWorld).getHandle();
|
||||
ChunkMap chunkMap = world.getChunkSource().chunkMap;
|
||||
- Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunks = chunkMap.visibleChunkMap;
|
||||
+ Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunks = chunkMap.updatingChunks.getVisibleMap(); // Paper
|
||||
DistanceManager chunkMapDistance = chunkMap.distanceManager;
|
||||
List<ChunkHolder> allChunks = new ArrayList<>(visibleChunks.values());
|
||||
List<ServerPlayer> players = world.players;
|
||||
public static List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
|
||||
- return new ArrayList<>(level.chunkSource.chunkMap.updatingChunkMap.values());
|
||||
+ return level.chunkSource.chunkMap.updatingChunks.getUpdatingValuesCopy();
|
||||
}
|
||||
|
||||
public static int getVisibleChunkHolderCount(final ServerLevel level) {
|
||||
- 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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
|
@ -53,7 +60,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
public final LongSet entitiesInLevel;
|
||||
public final ServerLevel level;
|
||||
@@ -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) {
|
||||
super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
|
||||
- this.visibleChunkMap = this.updatingChunkMap.clone();
|
||||
|
@ -82,21 +89,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
|
||||
protected IntSupplier getChunkQueueLevel(long pos) {
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
};
|
||||
|
||||
stringbuilder.append("Updating:").append(System.lineSeparator());
|
||||
- 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
|
||||
}
|
||||
|
||||
// Paper start
|
||||
holder.onChunkAdd();
|
||||
// Paper end
|
||||
- this.updatingChunkMap.put(pos, holder);
|
||||
+ this.updatingChunks.queueUpdate(pos, holder); // Paper - Don't copy
|
||||
this.modified = true;
|
||||
|
@ -104,32 +99,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
|
||||
@@ -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()) {
|
||||
long j = longiterator.nextLong();
|
||||
- ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j);
|
||||
|
@ -151,88 +120,3 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
this.modified = false;
|
||||
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 -> {
|
||||
|
|
|
@ -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());
|
|
@ -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.
|
||||
|
||||
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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
|
@ -49,22 +64,27 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
}
|
||||
|
||||
+ // 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;
|
||||
+ 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.SAFE_REGEN) {
|
||||
+ return;
|
||||
+ return false;
|
||||
+ }
|
||||
+ 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()
|
||||
+ && Objects.equals(other.getEncodeId(), entity.getEncodeId())
|
||||
+ && 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.");
|
||||
+ entity.discard();
|
||||
+ return;
|
||||
+ return true;
|
||||
+ }
|
||||
+ if (other != null && !other.isRemoved()) {
|
||||
+ switch (mode) {
|
||||
|
@ -76,13 +96,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ 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.");
|
||||
+ entity.discard();
|
||||
+ break;
|
||||
+ return true;
|
||||
+ }
|
||||
+ 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.");
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end
|
||||
public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> prepareTickingChunk(ChunkHolder holder) {
|
||||
|
|
|
@ -136,11 +136,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- 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;
|
||||
|
||||
return ret;
|
||||
}
|
||||
// Spigot end
|
||||
+
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public Location getOrigin() {
|
||||
+ Vector originVector = this.getHandle().getOriginVector();
|
||||
|
@ -155,5 +154,5 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ //noinspection ConstantConditions
|
||||
+ return originVector.toLocation(world);
|
||||
+ }
|
||||
+ // Paper end
|
||||
// Paper end
|
||||
}
|
||||
|
|
|
@ -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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- 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
|
||||
final Optional<LevelChunk> left = either.left();
|
||||
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();
|
||||
ChunkHolder.this.isFullChunkReady = true;
|
||||
fullChunk.playerChunk = ChunkHolder.this;
|
||||
+ this.chunkMap.distanceManager.clearPriorityTickets(pos);
|
||||
net.minecraft.server.ChunkSystem.onChunkBorder(fullChunk, this);
|
||||
+ this.chunkMap.distanceManager.clearPriorityTickets(pos); // Paper - chunk priority
|
||||
}
|
||||
});
|
||||
this.updateChunkToSave(this.fullChunkFuture, "full");
|
||||
|
@ -173,7 +256,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ io.papermc.paper.util.TickThread.ensureTickThread("Async full chunk future completion"); // Paper
|
||||
either.ifLeft(chunk -> {
|
||||
ChunkHolder.this.isEntityTickingReady = true;
|
||||
// Paper start - entity ticking chunk set
|
||||
net.minecraft.server.ChunkSystem.onChunkEntityTicking(chunk, this);
|
||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
||||
this.demoteFullChunk(chunkStorage, playerchunk_state1);
|
||||
}
|
||||
|
@ -422,9 +505,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
// Paper start
|
||||
public void updatePlayerMobTypeMap(Entity entity) {
|
||||
if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
||||
private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
|
||||
double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
|
||||
double d1 = (double) SectionPos.sectionToBlockCoord(pos.z, 8);
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
List<ChunkHolder> list1 = new ArrayList();
|
||||
int j = centerChunk.x;
|
||||
|
@ -542,7 +625,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
|
||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
||||
public boolean runAllUpdates(ChunkMap chunkStorage) {
|
||||
//this.f.a(); // Paper - no longer used
|
||||
this.naturalSpawnChunkCounter.runAllUpdates();
|
||||
this.tickingTicketsTracker.runAllUpdates();
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("DistanceManagerTick"); // Paper
|
||||
this.playerTicketManager.runAllUpdates();
|
||||
|
@ -737,6 +820,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+
|
||||
+ public void clearPriorityTickets(ChunkPos coords) {
|
||||
+ this.distanceManager.clearPriorityTickets(coords);
|
||||
+ }
|
||||
+
|
||||
+ public void clearUrgent(ChunkPos coords) {
|
||||
+ this.distanceManager.clearUrgent(coords);
|
||||
+ }
|
||||
// Paper end - async chunk io
|
||||
|
||||
|
@ -808,8 +895,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
private int lastSentFood = -99999999;
|
||||
private boolean lastFoodSaturationZero = true;
|
||||
@@ -0,0 +0,0 @@ public class ServerPlayer extends Player {
|
||||
this.bukkitPickUpLoot = true;
|
||||
this.maxHealthCache = this.getMaxHealth();
|
||||
this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
|
||||
}
|
||||
+ // Paper start - Chunk priority
|
||||
+ public BlockPos getPointInFront(double inFront) {
|
||||
|
@ -1120,20 +1207,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
|
||||
public void placeNewPlayer(Connection connection, ServerPlayer player) {
|
||||
+ player.isRealPlayer = true; // Paper - Chunk priority
|
||||
ServerPlayer prev = pendingPlayers.put(player.getUUID(), player);// Paper
|
||||
if (prev != null) {
|
||||
disconnectPendingPlayer(prev);
|
||||
@@ -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();
|
||||
GameProfile gameprofile = player.getGameProfile();
|
||||
GameProfileCache usercache = this.server.getProfileCache();
|
||||
Optional<GameProfile> optional = usercache.get(gameprofile.getId());
|
||||
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
|
||||
--- 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.event.world.ChunkUnloadEvent unloadEvent = new org.bukkit.event.world.ChunkUnloadEvent(this.bukkitChunk, this.isUnsaved());
|
||||
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");
|
||||
|
|
|
@ -115,17 +115,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
// private final Map<UUID, ServerStatisticManager> stats;
|
||||
// private final Map<UUID, AdvancementDataPlayer> advancements;
|
||||
@@ -0,0 +0,0 @@ public abstract class PlayerList {
|
||||
}
|
||||
|
||||
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) {
|
||||
+ disconnectPendingPlayer(prev);
|
||||
+ }
|
||||
+ player.networkManager = connection; // Paper
|
||||
player.loginTime = System.currentTimeMillis(); // Paper
|
||||
+ player.networkManager = connection;
|
||||
+ // Paper end
|
||||
GameProfile gameprofile = player.getGameProfile();
|
||||
GameProfileCache usercache = this.server.getProfileCache();
|
||||
Optional<GameProfile> optional = usercache.get(gameprofile.getId());
|
||||
@@ -0,0 +0,0 @@ public abstract class PlayerList {
|
||||
if (nbttagcompound != null && nbttagcompound.contains("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);
|
||||
+ 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 -> {
|
||||
+ net.minecraft.server.level.ChunkHolder updatingChunk = playerChunkMap.getUpdatingChunkIfPresent(pos.toLong());
|
||||
+ if (updatingChunk != null) {
|
||||
+ return updatingChunk.getEntityTickingChunkFuture();
|
||||
+ } else {
|
||||
+ return java.util.concurrent.CompletableFuture.completedFuture(chunk);
|
||||
+ }
|
||||
+ }).thenAccept(chunk -> {
|
||||
+ MinecraftServer.getServer().scheduleOnMain(() -> {
|
||||
+ try {
|
||||
+ if (!playerconnection.connection.isConnected()) {
|
||||
+ return;
|
||||
+ net.minecraft.server.ChunkSystem.scheduleTickingState(
|
||||
+ worldserver1, chunkX, chunkZ, net.minecraft.server.level.ChunkHolder.FullChunkStatus.ENTITY_TICKING, true,
|
||||
+ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHEST,
|
||||
+ (chunk) -> {
|
||||
+ MinecraftServer.getServer().scheduleOnMain(() -> {
|
||||
+ try {
|
||||
+ if (!playerconnection.connection.isConnected()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ 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);
|
||||
+ }
|
||||
+ 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) {
|
||||
|
|
|
@ -4528,6 +4528,281 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
if (packet.isSkippable()) {
|
||||
throw new SkipPacketException(var10);
|
||||
} 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
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
|
||||
|
@ -4845,7 +5120,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ * @return
|
||||
+ */
|
||||
+ public static void ensureMain(String reason, Runnable run) {
|
||||
+ if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().serverThread) {
|
||||
+ if (!isMainThread()) {
|
||||
+ if (reason != null) {
|
||||
+ new IllegalStateException("Asynchronous " + reason + "!").printStackTrace();
|
||||
+ }
|
||||
|
@ -4870,7 +5145,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ * @return
|
||||
+ */
|
||||
+ public static <T> T ensureMain(String reason, Supplier<T> run) {
|
||||
+ if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().serverThread) {
|
||||
+ if (!isMainThread()) {
|
||||
+ if (reason != null) {
|
||||
+ new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace();
|
||||
+ }
|
||||
|
@ -5118,6 +5393,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
private CompletableFuture<Void> pendingFullStateConfirmation;
|
||||
|
||||
+ 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) {
|
||||
this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
|
||||
|
@ -5236,16 +5521,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ this.fullChunkFuture.thenAccept(either -> {
|
||||
+ final Optional<LevelChunk> left = either.left();
|
||||
+ 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();
|
||||
+ ChunkHolder.this.isFullChunkReady = true;
|
||||
+ fullChunk.playerChunk = ChunkHolder.this;
|
||||
+ net.minecraft.server.ChunkSystem.onChunkBorder(fullChunk, this);
|
||||
+ }
|
||||
+ });
|
||||
this.updateChunkToSave(this.fullChunkFuture, "full");
|
||||
}
|
||||
|
||||
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 = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
+ ++this.fullChunkCreateCount; // Paper - cache ticking ready status
|
||||
|
@ -5262,9 +5551,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ either.ifLeft(chunk -> {
|
||||
+ // note: Here is a very good place to add callbacks to logic waiting on this.
|
||||
+ ChunkHolder.this.isTickingReady = true;
|
||||
+ // Paper start - ticking chunk set
|
||||
+ ChunkHolder.this.chunkMap.level.getChunkSource().tickingChunks.add(chunk);
|
||||
+ // Paper end - ticking chunk set
|
||||
+ net.minecraft.server.ChunkSystem.onChunkTicking(chunk, this);
|
||||
+ });
|
||||
+ });
|
||||
+ // Paper end
|
||||
|
@ -5273,17 +5560,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
|
||||
if (flag4 && !flag5) {
|
||||
- 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 = 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 {
|
||||
|
||||
this.entityTickingChunkFuture = chunkStorage.prepareEntityTickingChunk(this.pos);
|
||||
|
@ -5292,9 +5577,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ this.entityTickingChunkFuture.thenAccept(either -> {
|
||||
+ either.ifLeft(chunk -> {
|
||||
+ ChunkHolder.this.isEntityTickingReady = true;
|
||||
+ // Paper start - entity ticking chunk set
|
||||
+ ChunkHolder.this.chunkMap.level.getChunkSource().entityTickingChunks.add(chunk);
|
||||
+ // Paper end - entity ticking chunk set
|
||||
+ net.minecraft.server.ChunkSystem.onChunkEntityTicking(chunk, this);
|
||||
+ });
|
||||
+ });
|
||||
+ // Paper end
|
||||
|
@ -5303,17 +5586,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
|
||||
if (flag6 && !flag7) {
|
||||
- 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 = 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 {
|
||||
}
|
||||
};
|
||||
|
@ -5428,18 +5709,77 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
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<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
|
||||
holder.setTicketLevel(level);
|
||||
} else {
|
||||
holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this);
|
||||
+ // Paper start
|
||||
+ for (int index = 0, len = this.regionManagers.size(); index < len; ++index) {
|
||||
+ this.regionManagers.get(index).addChunk(holder.pos.x, holder.pos.z);
|
||||
+ }
|
||||
+ net.minecraft.server.ChunkSystem.onChunkHolderCreate(this.level, holder);
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ holder.onChunkAdd();
|
||||
+ // Paper end
|
||||
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
|
||||
if (completablefuture1 != completablefuture) {
|
||||
this.scheduleUnload(pos, holder);
|
||||
|
@ -5448,9 +5788,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ // Paper start
|
||||
+ boolean removed;
|
||||
+ if ((removed = this.pendingUnloads.remove(pos, holder)) && ichunkaccess != null) {
|
||||
+ for (int index = 0, len = this.regionManagers.size(); index < len; ++index) {
|
||||
+ this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
|
||||
+ }
|
||||
+ net.minecraft.server.ChunkSystem.onChunkHolderDelete(this.level, holder);
|
||||
+ // Paper end
|
||||
if (ichunkaccess instanceof LevelChunk) {
|
||||
((LevelChunk) ichunkaccess).setLoaded(false);
|
||||
|
@ -5459,15 +5797,57 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
this.lightEngine.tryScheduleUpdate();
|
||||
this.progressListener.onStatusChange(ichunkaccess.getPos(), (ChunkStatus) null);
|
||||
this.chunkSaveCooldowns.remove(ichunkaccess.getPos().toLong());
|
||||
- }
|
||||
+ } else if (removed) { // Paper start
|
||||
+ for (int index = 0, len = this.regionManagers.size(); index < len; ++index) {
|
||||
+ this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
|
||||
}
|
||||
+ } // Paper end
|
||||
+ net.minecraft.server.ChunkSystem.onChunkHolderDelete(this.level, holder);
|
||||
+ } // Paper end
|
||||
|
||||
}
|
||||
};
|
||||
@@ -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) {
|
||||
this.distanceManager.addPlayer(SectionPos.of((EntityAccess) player), player);
|
||||
}
|
||||
|
@ -5597,7 +5977,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+
|
||||
+ LevelChunk cachedChunk = this.lastLoadedChunks[cacheKey];
|
||||
+ if (cachedChunk != null && cachedChunk.locX == x & cachedChunk.locZ == z) {
|
||||
+ return this.lastLoadedChunks[cacheKey];
|
||||
+ return cachedChunk;
|
||||
+ }
|
||||
+
|
||||
+ long chunkKey = ChunkPos.asLong(x, z);
|
||||
|
@ -5623,80 +6003,24 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ long chunkFutureAwaitCounter; // Paper - private -> package private
|
||||
+
|
||||
+ public void getEntityTickingChunkAsync(int x, int z, java.util.function.Consumer<LevelChunk> onLoad) {
|
||||
+ if (Thread.currentThread() != this.mainThread) {
|
||||
+ this.mainThreadProcessor.execute(() -> {
|
||||
+ ServerChunkCache.this.getEntityTickingChunkAsync(x, z, onLoad);
|
||||
+ });
|
||||
+ return;
|
||||
+ }
|
||||
+ this.getChunkFutureAsynchronously(x, z, 31, ChunkHolder::getEntityTickingChunkFuture, onLoad);
|
||||
+ net.minecraft.server.ChunkSystem.scheduleTickingState(
|
||||
+ this.level, x, z, ChunkHolder.FullChunkStatus.ENTITY_TICKING, true,
|
||||
+ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL, onLoad
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ public void getTickingChunkAsync(int x, int z, java.util.function.Consumer<LevelChunk> onLoad) {
|
||||
+ if (Thread.currentThread() != this.mainThread) {
|
||||
+ this.mainThreadProcessor.execute(() -> {
|
||||
+ ServerChunkCache.this.getTickingChunkAsync(x, z, onLoad);
|
||||
+ });
|
||||
+ return;
|
||||
+ }
|
||||
+ this.getChunkFutureAsynchronously(x, z, 32, ChunkHolder::getTickingChunkFuture, onLoad);
|
||||
+ net.minecraft.server.ChunkSystem.scheduleTickingState(
|
||||
+ this.level, x, z, ChunkHolder.FullChunkStatus.TICKING, true,
|
||||
+ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL, onLoad
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ public void getFullChunkAsync(int x, int z, java.util.function.Consumer<LevelChunk> onLoad) {
|
||||
+ if (Thread.currentThread() != this.mainThread) {
|
||||
+ this.mainThreadProcessor.execute(() -> {
|
||||
+ ServerChunkCache.this.getFullChunkAsync(x, z, 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);
|
||||
+ net.minecraft.server.ChunkSystem.scheduleTickingState(
|
||||
+ this.level, x, z, ChunkHolder.FullChunkStatus.BORDER, true,
|
||||
+ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL, onLoad
|
||||
+ );
|
||||
+ }
|
||||
+ // 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) {
|
||||
+ this.distanceManager.addTicket(ticketType, chunkPos, ticketLevel, identifier);
|
||||
+ }
|
||||
|
@ -5808,74 +6071,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ this.distanceManager.removeTicket(ticketType, chunkPos, ticketLevel, identifier);
|
||||
+ }
|
||||
+
|
||||
+ void chunkLoadAccept(int chunkX, int chunkZ, ChunkAccess chunk, java.util.function.Consumer<ChunkAccess> consumer) {
|
||||
+ try {
|
||||
+ 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);
|
||||
+ public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<LevelChunk> tickingChunks = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true);
|
||||
+ public 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
|
||||
|
||||
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;
|
||||
+ }
|
||||
+
|
||||
+ 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) {
|
||||
+ if (Thread.currentThread() != this.thread) {
|
||||
+ this.getChunkSource().mainThreadProcessor.execute(() -> {
|
||||
+ this.loadChunksForMoveAsync(axisalignedbb, toX, toZ, onLoad);
|
||||
+ this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad);
|
||||
+ });
|
||||
+ return;
|
||||
+ }
|
||||
|
@ -6043,7 +6240,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+
|
||||
+ for (int cx = minChunkX; cx <= maxChunkX; ++cx) {
|
||||
+ 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
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
|
||||
|
|
|
@ -24,7 +24,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
-
|
||||
for (int i = 0; longiterator.hasNext() && (shouldKeepTicking.getAsBoolean() || i < 200 || this.toDrop.size() > 2000); longiterator.remove()) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,9 +88,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ // Paper end
|
||||
}
|
||||
|
||||
// Paper start - Chunk priority
|
||||
@@ -0,0 +0,0 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
|
||||
@Override
|
||||
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
|
||||
+ // Paper start - Teleport API
|
||||
|
|
|
@ -14,6 +14,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+package io.papermc.paper.util;
|
||||
+
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
+import net.minecraft.world.entity.Entity;
|
||||
+import org.bukkit.Bukkit;
|
||||
+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) {
|
||||
+ if (!isTickThreadFor(chunkX, chunkZ)) {
|
||||
+ public static void ensureTickThread(final ServerLevel world, final int chunkX, final int chunkZ, final String reason) {
|
||||
+ if (!isTickThreadFor(world, chunkX, chunkZ)) {
|
||||
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
+ throw new IllegalStateException(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());
|
||||
+ throw new IllegalStateException(reason);
|
||||
+ }
|
||||
|
@ -81,7 +82,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ 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();
|
||||
+ }
|
||||
+
|
||||
|
|
|
@ -79,8 +79,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
- }
|
||||
+ // 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 {
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- a/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 {
|
||||
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
|
||||
+ // Paper start - optimise chunk tick iteration
|
||||
+ if (this.needsBroadcastChanges()) {
|
||||
+ this.chunkMap.needsChangeBroadcasting.add(this);
|
||||
|
@ -20,17 +20,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ // Paper end - optimise chunk tick iteration
|
||||
}
|
||||
|
||||
void onChunkRemove() {
|
||||
public void onChunkRemove() {
|
||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
||||
this.playersInMobSpawnRange = null;
|
||||
this.playersInChunkTickRange = null;
|
||||
// Paper end - optimise anyPlayerCloseEnoughForSpawning
|
||||
+ // Paper start - optimise chunk tick iteration
|
||||
+ if (this.needsBroadcastChanges()) {
|
||||
+ this.chunkMap.needsChangeBroadcasting.remove(this);
|
||||
+ }
|
||||
+ // Paper end - optimise chunk tick iteration
|
||||
}
|
||||
// Paper end - optimise anyPlayerCloseEnoughForSpawning
|
||||
long lastAutoSaveTime; // Paper - incremental autosave
|
||||
// Paper end
|
||||
|
||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
||||
|
||||
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
|
||||
+ }
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ } finally {
|
||||
+ if (iterator1 instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator safeIterator) {
|
||||
+ safeIterator.finishedIterating();
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ // Paper end - optimise chunk tick iteration
|
||||
this.level.timings.chunkTicks.stopTiming(); // Paper
|
||||
gameprofilerfiller.popPush("customSpawners");
|
||||
|
|
|
@ -24,7 +24,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ // Paper end - optimise checkDespawn
|
||||
}
|
||||
|
||||
void onChunkRemove() {
|
||||
public void onChunkRemove() {
|
||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
||||
this.chunkMap.needsChangeBroadcasting.remove(this);
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ // Paper end - optimise checkDespawn
|
||||
}
|
||||
// Paper end - optimise anyPlayerCloseEnoughForSpawning
|
||||
long lastAutoSaveTime; // Paper - incremental autosave
|
||||
// Paper end
|
||||
|
||||
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
|
||||
|
|
|
@ -10,37 +10,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- a/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 {
|
||||
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
|
||||
+ // cached here to avoid a map lookup
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInMobSpawnRange;
|
||||
+ 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
|
||||
+
|
||||
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.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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
|
@ -127,22 +125,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
}
|
||||
|
||||
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
|
||||
return this.anyPlayerCloseEnoughForSpawning(pos, false);
|
||||
}
|
||||
|
@ -243,8 +225,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
- this.naturalSpawnChunkCounter.runAllUpdates();
|
||||
+ //this.f.a(); // Paper - no longer used
|
||||
this.tickingTicketsTracker.runAllUpdates();
|
||||
org.spigotmc.AsyncCatcher.catchOp("DistanceManagerTick"); // Paper
|
||||
this.playerTicketManager.runAllUpdates();
|
||||
int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE);
|
||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
||||
((ObjectSet) this.playersPerChunk.computeIfAbsent(i, (j) -> {
|
||||
return new ObjectOpenHashSet();
|
||||
|
|
|
@ -42,9 +42,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ @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;
|
||||
+ 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();
|
||||
+ if (chunk == null) {
|
||||
+ continue;
|
||||
|
@ -63,7 +62,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ public int getChunkCount() {
|
||||
+ 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) {
|
||||
+ ++ret;
|
||||
+ }
|
||||
|
|
|
@ -1284,7 +1284,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ 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-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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
|
@ -1305,8 +1305,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ // Paper end - no-tick view distance
|
||||
|
||||
// Paper start - optimise anyPlayerCloseEnoughForSpawning
|
||||
// cached here to avoid a map lookup
|
||||
// Paper start
|
||||
public void onChunkAdd() {
|
||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
||||
|
||||
public void blockChanged(BlockPos pos) {
|
||||
|
@ -1440,7 +1440,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
|
||||
this.viewDistance = j;
|
||||
- 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()) {
|
||||
- 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);
|
||||
- TickingTracker tickingtracker = this.distanceManager.tickingTracker();
|
||||
+ // 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()) {
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
|
|
|
@ -15,7 +15,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- a/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 {
|
||||
if (regionfile != null) {
|
||||
// Paper end
|
||||
return regionfile;
|
||||
} else {
|
||||
- if (this.regionCache.size() >= 256) {
|
||||
|
|
|
@ -99,6 +99,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
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 Paper!):" ); // Paper
|
||||
com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper
|
||||
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
//
|
||||
|
|
|
@ -87,8 +87,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
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 {
|
||||
// Spigot end
|
||||
// Paper start
|
||||
@Override
|
||||
public java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) {
|
||||
+ warnUnsafeChunk("getting a faraway chunk async", x, z); // Paper
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
|
|
|
@ -304,9 +304,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
|
||||
protected ChunkGenerator generator() {
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Paper end
|
||||
+ // Paper start
|
||||
+ public void updatePlayerMobTypeMap(Entity entity) {
|
||||
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
||||
|
@ -331,10 +331,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ return entityPlayer.mobCounts[mobCategory.ordinal()];
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
|
||||
private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
|
||||
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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
|
@ -398,8 +397,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
this.maxHealthCache = this.getMaxHealth();
|
||||
+ this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
|
||||
}
|
||||
|
||||
// Yes, this doesn't match Vanilla, but it's the best we can do for now.
|
||||
// Paper start - Chunk priority
|
||||
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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
|
|
|
@ -57,8 +57,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- a/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 {
|
||||
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
|
||||
+ long lastAutoSaveTime; // Paper - incremental autosave
|
||||
+ long inactiveTimeStart; // Paper - incremental autosave
|
||||
|
@ -107,18 +107,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
+ }
|
||||
+
|
||||
+ // Paper start - incremental autosave
|
||||
+ public boolean setHasBeenLoaded() {
|
||||
+ this.wasAccessibleSinceLastSave = getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
|
||||
+ return this.wasAccessibleSinceLastSave;
|
||||
+ }
|
||||
}
|
||||
+ // Paper end
|
||||
+
|
||||
|
||||
public void replaceProtoChunk(ImposterProtoChunk chunk) {
|
||||
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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
|
@ -194,13 +193,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ // Paper end
|
||||
+
|
||||
protected void saveAllChunks(boolean flush) {
|
||||
if (flush) {
|
||||
List<ChunkHolder> list = (List) this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList());
|
||||
// Paper start - do not overload I/O threads with too much work when saving
|
||||
int[] saved = new int[1];
|
||||
@@ -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())) {
|
||||
|
|
Loading…
Reference in a new issue