mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-09 19:49:35 +01:00
373ed1ddd5
properly removed from the chunk. This could lead to dead entities accumulating in memory over time if the chunk never gets fully unloaded (as it is the case for chunks around the spawn region). The issue is that Minecraft processes the removal of these entities during the next tick, when the chunk has already switched to state INACCESSIBLE and can no longer be retrieved as usual. For the purpose of removing dead entities from their still loaded but no longer accessible chunk, this adds and uses a new method with which a chunk can be accessed without checking its current state first. By: blablubbabc <lukas@wirsindwir.de>
167 lines
7.2 KiB
Diff
167 lines
7.2 KiB
Diff
--- a/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -54,6 +54,24 @@
|
|
this.clearCache();
|
|
}
|
|
|
|
+ // CraftBukkit start - properly implement isChunkLoaded
|
|
+ public boolean isChunkLoaded(int chunkX, int chunkZ) {
|
|
+ PlayerChunk chunk = this.playerChunkMap.getUpdatingChunk(ChunkCoordIntPair.pair(chunkX, chunkZ));
|
|
+ if (chunk == null) {
|
|
+ return false;
|
|
+ }
|
|
+ return chunk.getFullChunk() != null;
|
|
+ }
|
|
+
|
|
+ public Chunk getChunkUnchecked(int chunkX, int chunkZ) {
|
|
+ PlayerChunk chunk = this.playerChunkMap.getUpdatingChunk(ChunkCoordIntPair.pair(chunkX, chunkZ));
|
|
+ if (chunk == null) {
|
|
+ return null;
|
|
+ }
|
|
+ return chunk.getFullChunkUnchecked();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
@Override
|
|
public LightEngineThreaded getLightEngine() {
|
|
return this.lightEngine;
|
|
@@ -98,7 +116,7 @@
|
|
for (int l = 0; l < 4; ++l) {
|
|
if (k == this.cachePos[l] && chunkstatus == this.cacheStatus[l]) {
|
|
ichunkaccess = this.cacheChunk[l];
|
|
- if (ichunkaccess != null || !flag) {
|
|
+ if (ichunkaccess != null) { // CraftBukkit - the chunk can become accessible in the meantime TODO for non-null chunks it might also make sense to check that the chunk's state hasn't changed in the meantime
|
|
return ichunkaccess;
|
|
}
|
|
}
|
|
@@ -144,12 +162,12 @@
|
|
if (playerchunk == null) {
|
|
return null;
|
|
} else {
|
|
- Either<IChunkAccess, PlayerChunk.Failure> either = (Either) playerchunk.b(ChunkStatus.FULL).getNow((Object) null);
|
|
+ Either<IChunkAccess, PlayerChunk.Failure> either = (Either) playerchunk.b(ChunkStatus.FULL).getNow(null); // CraftBukkit - decompile error
|
|
|
|
if (either == null) {
|
|
return null;
|
|
} else {
|
|
- IChunkAccess ichunkaccess1 = (IChunkAccess) either.left().orElse((Object) null);
|
|
+ IChunkAccess ichunkaccess1 = (IChunkAccess) either.left().orElse(null); // CraftBukkit - decompile error
|
|
|
|
if (ichunkaccess1 != null) {
|
|
this.a(k, ichunkaccess1, ChunkStatus.FULL);
|
|
@@ -176,7 +194,15 @@
|
|
int l = 33 + ChunkStatus.a(chunkstatus);
|
|
PlayerChunk playerchunk = this.getChunk(k);
|
|
|
|
- if (flag) {
|
|
+ // CraftBukkit start - don't add new ticket for currently unloading chunk
|
|
+ boolean currentlyUnloading = false;
|
|
+ if (playerchunk != null) {
|
|
+ PlayerChunk.State oldChunkState = PlayerChunk.getChunkState(playerchunk.oldTicketLevel);
|
|
+ PlayerChunk.State currentChunkState = PlayerChunk.getChunkState(playerchunk.getTicketLevel());
|
|
+ currentlyUnloading = (oldChunkState.isAtLeast(PlayerChunk.State.BORDER) && !currentChunkState.isAtLeast(PlayerChunk.State.BORDER));
|
|
+ }
|
|
+ if (flag && !currentlyUnloading) {
|
|
+ // CraftBukkit end
|
|
this.chunkMapDistance.a(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair);
|
|
if (this.a(playerchunk, l)) {
|
|
GameProfilerFiller gameprofilerfiller = this.world.getMethodProfiler();
|
|
@@ -195,7 +221,7 @@
|
|
}
|
|
|
|
private boolean a(@Nullable PlayerChunk playerchunk, int i) {
|
|
- return playerchunk == null || playerchunk.getTicketLevel() > i;
|
|
+ return playerchunk == null || playerchunk.oldTicketLevel > i; // CraftBukkit using oldTicketLevel for isLoaded checks
|
|
}
|
|
|
|
public boolean isLoaded(int i, int j) {
|
|
@@ -257,19 +283,19 @@
|
|
public boolean a(Entity entity) {
|
|
long i = ChunkCoordIntPair.pair(MathHelper.floor(entity.locX()) >> 4, MathHelper.floor(entity.locZ()) >> 4);
|
|
|
|
- return this.a(i, PlayerChunk::b);
|
|
+ return this.a(i, (Function<PlayerChunk, CompletableFuture<Either<Chunk, PlayerChunk.Failure>>>) PlayerChunk::b); // CraftBukkit - decompile error
|
|
}
|
|
|
|
@Override
|
|
public boolean a(ChunkCoordIntPair chunkcoordintpair) {
|
|
- return this.a(chunkcoordintpair.pair(), PlayerChunk::b);
|
|
+ return this.a(chunkcoordintpair.pair(), (Function<PlayerChunk, CompletableFuture<Either<Chunk, PlayerChunk.Failure>>>) PlayerChunk::b); // CraftBukkit - decompile error
|
|
}
|
|
|
|
@Override
|
|
public boolean a(BlockPosition blockposition) {
|
|
long i = ChunkCoordIntPair.pair(blockposition.getX() >> 4, blockposition.getZ() >> 4);
|
|
|
|
- return this.a(i, PlayerChunk::a);
|
|
+ return this.a(i, (Function<PlayerChunk, CompletableFuture<Either<Chunk, PlayerChunk.Failure>>>) PlayerChunk::a); // CraftBukkit - decompile error
|
|
}
|
|
|
|
private boolean a(long i, Function<PlayerChunk, CompletableFuture<Either<Chunk, PlayerChunk.Failure>>> function) {
|
|
@@ -291,11 +317,31 @@
|
|
|
|
@Override
|
|
public void close() throws IOException {
|
|
- this.save(true);
|
|
+ // CraftBukkit start
|
|
+ close(true);
|
|
+ }
|
|
+
|
|
+ public void close(boolean save) throws IOException {
|
|
+ if (save) {
|
|
+ this.save(true);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.lightEngine.close();
|
|
this.playerChunkMap.close();
|
|
}
|
|
|
|
+ // CraftBukkit start - modelled on below
|
|
+ public void purgeUnload() {
|
|
+ this.world.getMethodProfiler().enter("purge");
|
|
+ this.chunkMapDistance.purgeTickets();
|
|
+ this.tickDistanceManager();
|
|
+ this.world.getMethodProfiler().exitEnter("unload");
|
|
+ this.playerChunkMap.unloadChunks(() -> true);
|
|
+ this.world.getMethodProfiler().exit();
|
|
+ this.clearCache();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
public void tick(BooleanSupplier booleansupplier) {
|
|
this.world.getMethodProfiler().enter("purge");
|
|
this.chunkMapDistance.purgeTickets();
|
|
@@ -315,12 +361,12 @@
|
|
this.lastTickTime = i;
|
|
WorldData worlddata = this.world.getWorldData();
|
|
boolean flag = this.world.isDebugWorld();
|
|
- boolean flag1 = this.world.getGameRules().getBoolean(GameRules.DO_MOB_SPAWNING);
|
|
+ boolean flag1 = this.world.getGameRules().getBoolean(GameRules.DO_MOB_SPAWNING) && !world.getPlayers().isEmpty(); // CraftBukkit
|
|
|
|
if (!flag) {
|
|
this.world.getMethodProfiler().enter("pollingChunks");
|
|
int k = this.world.getGameRules().getInt(GameRules.RANDOM_TICK_SPEED);
|
|
- boolean flag2 = worlddata.getTime() % 400L == 0L;
|
|
+ boolean flag2 = world.ticksPerAnimalSpawns != 0L && worlddata.getTime() % world.ticksPerAnimalSpawns == 0L; // CraftBukkit
|
|
|
|
this.world.getMethodProfiler().enter("naturalSpawnCount");
|
|
int l = this.chunkMapDistance.b();
|
|
@@ -507,12 +553,18 @@
|
|
|
|
@Override
|
|
protected boolean executeNext() {
|
|
+ // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task
|
|
+ try {
|
|
if (ChunkProviderServer.this.tickDistanceManager()) {
|
|
return true;
|
|
} else {
|
|
ChunkProviderServer.this.lightEngine.queueUpdate();
|
|
return super.executeNext();
|
|
}
|
|
+ } finally {
|
|
+ playerChunkMap.callbackExecutor.run();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
}
|
|
}
|
|
}
|