geforkt von Mirrors/Paper
fad2494af1
The flag for getChunkAt(int, int, ChunkStatus, boolean) is actually a flag for whether to bring the underlying PlayerChunk up to the required ticket level to load the chunk. So, if the chunk is already at the required level, but has not yet loaded, the call will actually either start the load if it has not already been started and block until completion. This behaviour is not suitable for just checking if the chunk is loaded.
181 Zeilen
8.7 KiB
Diff
181 Zeilen
8.7 KiB
Diff
--- a/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -51,6 +51,16 @@
|
|
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;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
@Override
|
|
public LightEngineThreaded getLightEngine() {
|
|
return this.lightEngine;
|
|
@@ -95,7 +105,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;
|
|
}
|
|
}
|
|
@@ -141,12 +151,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);
|
|
@@ -173,7 +183,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();
|
|
@@ -192,7 +210,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) {
|
|
@@ -294,11 +312,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();
|
|
@@ -318,13 +356,19 @@
|
|
this.lastTickTime = i;
|
|
WorldData worlddata = this.world.getWorldData();
|
|
boolean flag = worlddata.getType() == WorldType.DEBUG_ALL_BLOCK_STATES;
|
|
- 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);
|
|
BlockPosition blockposition = this.world.getSpawn();
|
|
- boolean flag2 = worlddata.getTime() % 400L == 0L;
|
|
+ // CraftBukkit start - Other mob type spawn tick rate
|
|
+ boolean spawnAnimalThisTick = world.ticksPerAnimalSpawns != 0L && worlddata.getTime() % world.ticksPerAnimalSpawns == 0L;
|
|
+ boolean spawnMonsterThisTick = world.ticksPerMonsterSpawns != 0L && worlddata.getTime() % world.ticksPerMonsterSpawns == 0L;
|
|
+ boolean spawnWaterThisTick = world.ticksPerWaterSpawns != 0L && worlddata.getTime() % world.ticksPerWaterSpawns == 0L;
|
|
+ boolean spawnAmbientThisTick = world.ticksPerAmbientSpawns != 0L && worlddata.getTime() % world.ticksPerAmbientSpawns == 0L;
|
|
+ boolean flag2 = spawnAnimalThisTick;
|
|
+ // CraftBukkit end
|
|
|
|
this.world.getMethodProfiler().enter("naturalSpawnCount");
|
|
int l = this.chunkMapDistance.b();
|
|
@@ -353,8 +397,35 @@
|
|
for (int j1 = 0; j1 < i1; ++j1) {
|
|
EnumCreatureType enumcreaturetype = aenumcreaturetype1[j1];
|
|
|
|
+ // CraftBukkit start - Use per-world spawn limits
|
|
+ boolean spawnThisTick = true;
|
|
+ int limit = enumcreaturetype.b();
|
|
+ switch (enumcreaturetype) {
|
|
+ case MONSTER:
|
|
+ spawnThisTick = spawnMonsterThisTick;
|
|
+ limit = world.getWorld().getMonsterSpawnLimit();
|
|
+ break;
|
|
+ case CREATURE:
|
|
+ spawnThisTick = spawnAnimalThisTick;
|
|
+ limit = world.getWorld().getAnimalSpawnLimit();
|
|
+ break;
|
|
+ case WATER_CREATURE:
|
|
+ spawnThisTick = spawnWaterThisTick;
|
|
+ limit = world.getWorld().getWaterAnimalSpawnLimit();
|
|
+ break;
|
|
+ case AMBIENT:
|
|
+ spawnThisTick = spawnAmbientThisTick;
|
|
+ limit = world.getWorld().getAmbientSpawnLimit();
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (!spawnThisTick || limit == 0) {
|
|
+ continue;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
if (enumcreaturetype != EnumCreatureType.MISC && (!enumcreaturetype.c() || this.allowAnimals) && (enumcreaturetype.c() || this.allowMonsters) && (!enumcreaturetype.d() || flag2)) {
|
|
- int k1 = enumcreaturetype.b() * l / ChunkProviderServer.b;
|
|
+ int k1 = limit * l / ChunkProviderServer.b; // CraftBukkit - use per-world limits
|
|
|
|
if (object2intmap.getInt(enumcreaturetype) <= k1) {
|
|
SpawnerCreature.a(enumcreaturetype, this.world, chunk, blockposition);
|
|
@@ -507,12 +578,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
|
|
}
|
|
}
|
|
}
|