diff options
Diffstat (limited to 'patch-remap/mache-vineflower/net/minecraft/server/level/ServerChunkCache.java.patch')
-rw-r--r-- | patch-remap/mache-vineflower/net/minecraft/server/level/ServerChunkCache.java.patch | 644 |
1 files changed, 644 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/server/level/ServerChunkCache.java.patch b/patch-remap/mache-vineflower/net/minecraft/server/level/ServerChunkCache.java.patch new file mode 100644 index 0000000000..1f70a0e9ed --- /dev/null +++ b/patch-remap/mache-vineflower/net/minecraft/server/level/ServerChunkCache.java.patch @@ -0,0 +1,644 @@ +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -7,10 +7,11 @@ + import java.io.File; + import java.io.IOException; + import java.util.Arrays; ++import java.util.Iterator; + import java.util.List; ++import java.util.Objects; + import java.util.Optional; + import java.util.concurrent.CompletableFuture; +-import java.util.concurrent.CompletionStage; + import java.util.concurrent.Executor; + import java.util.function.BooleanSupplier; + import java.util.function.Consumer; +@@ -27,9 +28,9 @@ + import net.minecraft.world.entity.Entity; + import net.minecraft.world.entity.ai.village.poi.PoiManager; + import net.minecraft.world.level.ChunkPos; ++import net.minecraft.world.level.EnumSkyBlock; + import net.minecraft.world.level.GameRules; + import net.minecraft.world.level.Level; +-import net.minecraft.world.level.LightLayer; + import net.minecraft.world.level.LocalMobCapCalculator; + import net.minecraft.world.level.NaturalSpawner; + import net.minecraft.world.level.chunk.ChunkAccess; +@@ -47,6 +48,7 @@ + import net.minecraft.world.level.storage.LevelStorageSource; + + public class ServerChunkCache extends ChunkSource { ++ + private static final List<ChunkStatus> CHUNK_STATUSES = ChunkStatus.getStatusList(); + private final DistanceManager distanceManager; + final ServerLevel level; +@@ -56,8 +58,8 @@ + public final ChunkMap chunkMap; + private final DimensionDataStorage dataStorage; + private long lastInhabitedUpdate; +- private boolean spawnEnemies = true; +- private boolean spawnFriendlies = true; ++ public boolean spawnEnemies = true; ++ public boolean spawnFriendlies = true; + private static final int CACHE_SIZE = 4; + private final long[] lastChunkPos = new long[4]; + private final ChunkStatus[] lastChunkStatus = new ChunkStatus[4]; +@@ -66,47 +68,31 @@ + @VisibleForDebug + private NaturalSpawner.SpawnState lastSpawnState; + +- public ServerChunkCache( +- ServerLevel level, +- LevelStorageSource.LevelStorageAccess levelStorageAccess, +- DataFixer fixerUpper, +- StructureTemplateManager structureManager, +- Executor dispatcher, +- ChunkGenerator generator, +- int viewDistance, +- int simulationDistance, +- boolean sync, +- ChunkProgressListener progressListener, +- ChunkStatusUpdateListener chunkStatusListener, +- Supplier<DimensionDataStorage> overworldDataStorage +- ) { ++ public ServerChunkCache(ServerLevel level, LevelStorageSource.LevelStorageAccess levelStorageAccess, DataFixer fixerUpper, StructureTemplateManager structureManager, Executor dispatcher, ChunkGenerator generator, int viewDistance, int simulationDistance, boolean sync, ChunkProgressListener progressListener, ChunkStatusUpdateListener chunkStatusListener, Supplier<DimensionDataStorage> overworldDataStorage) { + this.level = level; + this.mainThreadProcessor = new ServerChunkCache.MainThreadExecutor(level); + this.mainThread = Thread.currentThread(); + File file = levelStorageAccess.getDimensionPath(level.dimension()).resolve("data").toFile(); ++ + file.mkdirs(); + this.dataStorage = new DimensionDataStorage(file, fixerUpper); +- this.chunkMap = new ChunkMap( +- level, +- levelStorageAccess, +- fixerUpper, +- structureManager, +- dispatcher, +- this.mainThreadProcessor, +- this, +- generator, +- progressListener, +- chunkStatusListener, +- overworldDataStorage, +- viewDistance, +- sync +- ); ++ this.chunkMap = new ChunkMap(level, levelStorageAccess, fixerUpper, structureManager, dispatcher, this.mainThreadProcessor, this, generator, progressListener, chunkStatusListener, overworldDataStorage, viewDistance, sync); + this.lightEngine = this.chunkMap.getLightEngine(); + this.distanceManager = this.chunkMap.getDistanceManager(); + this.distanceManager.updateSimulationDistance(simulationDistance); + this.clearCache(); + } + ++ // CraftBukkit start - properly implement isChunkLoaded ++ public boolean isChunkLoaded(int chunkX, int chunkZ) { ++ ChunkHolder chunk = this.chunkMap.getUpdatingChunkIfPresent(ChunkPos.asLong(chunkX, chunkZ)); ++ if (chunk == null) { ++ return false; ++ } ++ return chunk.getFullChunkNow() != null; ++ } ++ // CraftBukkit end ++ + @Override + public ThreadedLevelLightEngine getLightEngine() { + return this.lightEngine; +@@ -121,51 +107,59 @@ + return this.chunkMap.getTickingGenerated(); + } + +- private void storeInCache(long chunkPos, ChunkAccess chunk, ChunkStatus chunkStatus) { +- for (int i = 3; i > 0; i--) { +- this.lastChunkPos[i] = this.lastChunkPos[i - 1]; +- this.lastChunkStatus[i] = this.lastChunkStatus[i - 1]; +- this.lastChunk[i] = this.lastChunk[i - 1]; ++ private void storeInCache(long chunkPos, ChunkAccess ichunkaccess, ChunkStatus chunk) { ++ for (int j = 3; j > 0; --j) { ++ this.lastChunkPos[j] = this.lastChunkPos[j - 1]; ++ this.lastChunkStatus[j] = this.lastChunkStatus[j - 1]; ++ this.lastChunk[j] = this.lastChunk[j - 1]; + } + + this.lastChunkPos[0] = chunkPos; +- this.lastChunkStatus[0] = chunkStatus; +- this.lastChunk[0] = chunk; ++ this.lastChunkStatus[0] = chunk; ++ this.lastChunk[0] = ichunkaccess; + } + + @Nullable + @Override + public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus requiredStatus, boolean load) { + if (Thread.currentThread() != this.mainThread) { +- return CompletableFuture.<ChunkAccess>supplyAsync(() -> this.getChunk(chunkX, chunkZ, requiredStatus, load), this.mainThreadProcessor).join(); ++ return (ChunkAccess) CompletableFuture.supplyAsync(() -> { ++ return this.getChunk(chunkX, chunkZ, requiredStatus, load); ++ }, this.mainThreadProcessor).join(); + } else { +- ProfilerFiller profiler = this.level.getProfiler(); +- profiler.incrementCounter("getChunk"); +- long _long = ChunkPos.asLong(chunkX, chunkZ); ++ ProfilerFiller gameprofilerfiller = this.level.getProfiler(); + +- for (int i = 0; i < 4; i++) { +- if (_long == this.lastChunkPos[i] && requiredStatus == this.lastChunkStatus[i]) { +- ChunkAccess chunkAccess = this.lastChunk[i]; +- if (chunkAccess != null || !load) { +- return chunkAccess; ++ gameprofilerfiller.incrementCounter("getChunk"); ++ long k = ChunkPos.asLong(chunkX, chunkZ); ++ ++ ChunkAccess ichunkaccess; ++ ++ for (int l = 0; l < 4; ++l) { ++ if (k == this.lastChunkPos[l] && requiredStatus == this.lastChunkStatus[l]) { ++ ichunkaccess = this.lastChunk[l]; ++ 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; + } + } + } + +- profiler.incrementCounter("getChunkCacheMiss"); +- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> chunkFutureMainThread = this.getChunkFutureMainThread( +- chunkX, chunkZ, requiredStatus, load +- ); +- this.mainThreadProcessor.managedBlock(chunkFutureMainThread::isDone); +- ChunkAccess chunkAccess = chunkFutureMainThread.join().map(chunkAccess1 -> (ChunkAccess)chunkAccess1, chunkLoadingFailure -> { ++ gameprofilerfiller.incrementCounter("getChunkCacheMiss"); ++ CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> completablefuture = this.getChunkFutureMainThread(chunkX, chunkZ, requiredStatus, load); ++ ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor; ++ ++ Objects.requireNonNull(completablefuture); ++ chunkproviderserver_b.managedBlock(completablefuture::isDone); ++ ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { ++ return ichunkaccess1; ++ }, (playerchunk_failure) -> { + if (load) { +- throw (IllegalStateException)Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + chunkLoadingFailure)); ++ throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + playerchunk_failure)); + } else { + return null; + } + }); +- this.storeInCache(_long, chunkAccess, requiredStatus); +- return chunkAccess; ++ this.storeInCache(k, ichunkaccess, requiredStatus); ++ return ichunkaccess; + } + } + +@@ -176,28 +170,32 @@ + return null; + } else { + this.level.getProfiler().incrementCounter("getChunkNow"); +- long _long = ChunkPos.asLong(chunkX, chunkZ); ++ long k = ChunkPos.asLong(chunkX, chunkZ); + +- for (int i = 0; i < 4; i++) { +- if (_long == this.lastChunkPos[i] && this.lastChunkStatus[i] == ChunkStatus.FULL) { +- ChunkAccess chunkAccess = this.lastChunk[i]; +- return chunkAccess instanceof LevelChunk ? (LevelChunk)chunkAccess : null; ++ for (int l = 0; l < 4; ++l) { ++ if (k == this.lastChunkPos[l] && this.lastChunkStatus[l] == ChunkStatus.FULL) { ++ ChunkAccess ichunkaccess = this.lastChunk[l]; ++ ++ return ichunkaccess instanceof LevelChunk ? (LevelChunk) ichunkaccess : null; + } + } + +- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(_long); +- if (visibleChunkIfPresent == null) { ++ ChunkHolder playerchunk = this.getVisibleChunkIfPresent(k); ++ ++ if (playerchunk == null) { + return null; + } else { +- Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = visibleChunkIfPresent.getFutureIfPresent(ChunkStatus.FULL).getNow(null); ++ Either<ChunkAccess, ChunkHolder.Failure> either = (Either) playerchunk.getFutureIfPresent(ChunkStatus.FULL).getNow(null); // CraftBukkit - decompile error ++ + if (either == null) { + return null; + } else { +- ChunkAccess chunkAccess1 = either.left().orElse(null); +- if (chunkAccess1 != null) { +- this.storeInCache(_long, chunkAccess1, ChunkStatus.FULL); +- if (chunkAccess1 instanceof LevelChunk) { +- return (LevelChunk)chunkAccess1; ++ ChunkAccess ichunkaccess1 = (ChunkAccess) either.left().orElse(null); // CraftBukkit - decompile error ++ ++ if (ichunkaccess1 != null) { ++ this.storeInCache(k, ichunkaccess1, ChunkStatus.FULL); ++ if (ichunkaccess1 instanceof LevelChunk) { ++ return (LevelChunk) ichunkaccess1; + } + } + +@@ -209,85 +207,99 @@ + + private void clearCache() { + Arrays.fill(this.lastChunkPos, ChunkPos.INVALID_CHUNK_POS); +- Arrays.fill(this.lastChunkStatus, null); +- Arrays.fill(this.lastChunk, null); ++ Arrays.fill(this.lastChunkStatus, (Object) null); ++ Arrays.fill(this.lastChunk, (Object) null); + } + +- public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFuture(int x, int y, ChunkStatus chunkStatus, boolean load) { +- boolean flag = Thread.currentThread() == this.mainThread; +- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> chunkFutureMainThread; +- if (flag) { +- chunkFutureMainThread = this.getChunkFutureMainThread(x, y, chunkStatus, load); +- this.mainThreadProcessor.managedBlock(chunkFutureMainThread::isDone); ++ public CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> getChunkFuture(int x, int y, ChunkStatus chunkStatus, boolean load) { ++ boolean flag1 = Thread.currentThread() == this.mainThread; ++ CompletableFuture completablefuture; ++ ++ if (flag1) { ++ completablefuture = this.getChunkFutureMainThread(x, y, chunkStatus, load); ++ ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor; ++ ++ Objects.requireNonNull(completablefuture); ++ chunkproviderserver_b.managedBlock(completablefuture::isDone); + } else { +- chunkFutureMainThread = CompletableFuture.<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>>supplyAsync( +- () -> this.getChunkFutureMainThread(x, y, chunkStatus, load), this.mainThreadProcessor +- ) +- .thenCompose(completableFuture -> (CompletionStage<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>)completableFuture); ++ completablefuture = CompletableFuture.supplyAsync(() -> { ++ return this.getChunkFutureMainThread(x, y, chunkStatus, load); ++ }, this.mainThreadProcessor).thenCompose((completablefuture1) -> { ++ return completablefuture1; ++ }); + } + +- return chunkFutureMainThread; ++ return completablefuture; + } + +- private CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFutureMainThread( +- int x, int y, ChunkStatus chunkStatus, boolean load +- ) { +- ChunkPos chunkPos = new ChunkPos(x, y); +- long l = chunkPos.toLong(); +- int i = ChunkLevel.byStatus(chunkStatus); +- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(l); +- if (load) { +- this.distanceManager.addTicket(TicketType.UNKNOWN, chunkPos, i, chunkPos); +- if (this.chunkAbsent(visibleChunkIfPresent, i)) { +- ProfilerFiller profiler = this.level.getProfiler(); +- profiler.push("chunkLoad"); ++ private CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> getChunkFutureMainThread(int x, int y, ChunkStatus chunkStatus, boolean load) { ++ ChunkPos chunkcoordintpair = new ChunkPos(x, y); ++ long k = chunkcoordintpair.toLong(); ++ int l = ChunkLevel.byStatus(chunkStatus); ++ ChunkHolder playerchunk = this.getVisibleChunkIfPresent(k); ++ ++ // CraftBukkit start - don't add new ticket for currently unloading chunk ++ boolean currentlyUnloading = false; ++ if (playerchunk != null) { ++ FullChunkStatus oldChunkState = ChunkLevel.fullStatus(playerchunk.oldTicketLevel); ++ FullChunkStatus currentChunkState = ChunkLevel.fullStatus(playerchunk.getTicketLevel()); ++ currentlyUnloading = (oldChunkState.isOrAfter(FullChunkStatus.FULL) && !currentChunkState.isOrAfter(FullChunkStatus.FULL)); ++ } ++ if (load && !currentlyUnloading) { ++ // CraftBukkit end ++ this.distanceManager.addTicket(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair); ++ if (this.chunkAbsent(playerchunk, l)) { ++ ProfilerFiller gameprofilerfiller = this.level.getProfiler(); ++ ++ gameprofilerfiller.push("chunkLoad"); + this.runDistanceManagerUpdates(); +- visibleChunkIfPresent = this.getVisibleChunkIfPresent(l); +- profiler.pop(); +- if (this.chunkAbsent(visibleChunkIfPresent, i)) { +- throw (IllegalStateException)Util.pauseInIde(new IllegalStateException("No chunk holder after ticket has been added")); ++ playerchunk = this.getVisibleChunkIfPresent(k); ++ gameprofilerfiller.pop(); ++ if (this.chunkAbsent(playerchunk, l)) { ++ throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("No chunk holder after ticket has been added")); + } + } + } + +- return this.chunkAbsent(visibleChunkIfPresent, i) +- ? ChunkHolder.UNLOADED_CHUNK_FUTURE +- : visibleChunkIfPresent.getOrScheduleFuture(chunkStatus, this.chunkMap); ++ return this.chunkAbsent(playerchunk, l) ? ChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.getOrScheduleFuture(chunkStatus, this.chunkMap); + } + + private boolean chunkAbsent(@Nullable ChunkHolder chunkHolder, int status) { +- return chunkHolder == null || chunkHolder.getTicketLevel() > status; ++ return chunkHolder == null || chunkHolder.oldTicketLevel > status; // CraftBukkit using oldTicketLevel for isLoaded checks + } + + @Override + public boolean hasChunk(int x, int z) { +- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(new ChunkPos(x, z).toLong()); +- int i = ChunkLevel.byStatus(ChunkStatus.FULL); +- return !this.chunkAbsent(visibleChunkIfPresent, i); ++ ChunkHolder playerchunk = this.getVisibleChunkIfPresent((new ChunkPos(x, z)).toLong()); ++ int k = ChunkLevel.byStatus(ChunkStatus.FULL); ++ ++ return !this.chunkAbsent(playerchunk, k); + } + + @Nullable + @Override + public LightChunk getChunkForLighting(int chunkX, int chunkZ) { +- long _long = ChunkPos.asLong(chunkX, chunkZ); +- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(_long); +- if (visibleChunkIfPresent == null) { ++ long k = ChunkPos.asLong(chunkX, chunkZ); ++ ChunkHolder playerchunk = this.getVisibleChunkIfPresent(k); ++ ++ if (playerchunk == null) { + return null; + } else { +- int i = CHUNK_STATUSES.size() - 1; ++ int l = ServerChunkCache.CHUNK_STATUSES.size() - 1; + + while (true) { +- ChunkStatus chunkStatus = CHUNK_STATUSES.get(i); +- Optional<ChunkAccess> optional = visibleChunkIfPresent.getFutureIfPresentUnchecked(chunkStatus).getNow(ChunkHolder.UNLOADED_CHUNK).left(); ++ ChunkStatus chunkstatus = (ChunkStatus) ServerChunkCache.CHUNK_STATUSES.get(l); ++ Optional<ChunkAccess> optional = ((Either) playerchunk.getFutureIfPresentUnchecked(chunkstatus).getNow(ChunkHolder.UNLOADED_CHUNK)).left(); ++ + if (optional.isPresent()) { +- return optional.get(); ++ return (LightChunk) optional.get(); + } + +- if (chunkStatus == ChunkStatus.INITIALIZE_LIGHT.getParent()) { ++ if (chunkstatus == ChunkStatus.INITIALIZE_LIGHT.getParent()) { + return null; + } + +- i--; ++ --l; + } + } + } +@@ -304,6 +316,7 @@ + boolean runDistanceManagerUpdates() { + boolean flag = this.distanceManager.runAllUpdates(this.chunkMap); + boolean flag1 = this.chunkMap.promoteChunkMap(); ++ + if (!flag && !flag1) { + return false; + } else { +@@ -313,13 +326,15 @@ + } + + public boolean isPositionTicking(long chunkPos) { +- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(chunkPos); +- if (visibleChunkIfPresent == null) { ++ ChunkHolder playerchunk = this.getVisibleChunkIfPresent(chunkPos); ++ ++ if (playerchunk == null) { + return false; + } else if (!this.level.shouldTickBlocksAt(chunkPos)) { + return false; + } else { +- Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = visibleChunkIfPresent.getTickingChunkFuture().getNow(null); ++ Either<LevelChunk, ChunkHolder.Failure> either = (Either) playerchunk.getTickingChunkFuture().getNow(null); // CraftBukkit - decompile error ++ + return either != null && either.left().isPresent(); + } + } +@@ -331,11 +346,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.chunkMap.close(); + } + ++ // CraftBukkit start - modelled on below ++ public void purgeUnload() { ++ this.level.getProfiler().push("purge"); ++ this.distanceManager.purgeStaleTickets(); ++ this.runDistanceManagerUpdates(); ++ this.level.getProfiler().popPush("unload"); ++ this.chunkMap.tick(() -> true); ++ this.level.getProfiler().pop(); ++ this.clearCache(); ++ } ++ // CraftBukkit end ++ + @Override + public void tick(BooleanSupplier hasTimeLeft, boolean tickChunks) { + this.level.getProfiler().push("purge"); +@@ -354,68 +389,80 @@ + } + + private void tickChunks() { +- long gameTime = this.level.getGameTime(); +- long l = gameTime - this.lastInhabitedUpdate; +- this.lastInhabitedUpdate = gameTime; ++ long i = this.level.getGameTime(); ++ long j = i - this.lastInhabitedUpdate; ++ ++ this.lastInhabitedUpdate = i; + if (!this.level.isDebug()) { +- ProfilerFiller profiler = this.level.getProfiler(); +- profiler.push("pollingChunks"); +- profiler.push("filteringLoadedChunks"); +- List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(this.chunkMap.size()); ++ ProfilerFiller gameprofilerfiller = this.level.getProfiler(); + +- for (ChunkHolder chunkHolder : this.chunkMap.getChunks()) { +- LevelChunk tickingChunk = chunkHolder.getTickingChunk(); +- if (tickingChunk != null) { +- list.add(new ServerChunkCache.ChunkAndHolder(tickingChunk, chunkHolder)); ++ gameprofilerfiller.push("pollingChunks"); ++ gameprofilerfiller.push("filteringLoadedChunks"); ++ List<ServerChunkCache.a> list = Lists.newArrayListWithCapacity(this.chunkMap.size()); ++ Iterator iterator = this.chunkMap.getChunks().iterator(); ++ ++ while (iterator.hasNext()) { ++ ChunkHolder playerchunk = (ChunkHolder) iterator.next(); ++ LevelChunk chunk = playerchunk.getTickingChunk(); ++ ++ if (chunk != null) { ++ list.add(new ServerChunkCache.a(chunk, playerchunk)); + } + } + + if (this.level.getServer().tickRateManager().runsNormally()) { +- profiler.popPush("naturalSpawnCount"); +- int naturalSpawnChunkCount = this.distanceManager.getNaturalSpawnChunkCount(); +- NaturalSpawner.SpawnState spawnState = NaturalSpawner.createState( +- naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap) +- ); +- this.lastSpawnState = spawnState; +- profiler.popPush("spawnAndTick"); +- boolean _boolean = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING); ++ gameprofilerfiller.popPush("naturalSpawnCount"); ++ int k = this.distanceManager.getNaturalSpawnChunkCount(); ++ NaturalSpawner.SpawnState spawnercreature_d = NaturalSpawner.createState(k, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap)); ++ ++ this.lastSpawnState = spawnercreature_d; ++ gameprofilerfiller.popPush("spawnAndTick"); ++ boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit ++ + Util.shuffle(list, this.level.random); +- int _int = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); +- boolean flag = this.level.getLevelData().getGameTime() % 400L == 0L; ++ int l = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); ++ boolean flag1 = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit ++ Iterator iterator1 = list.iterator(); + +- for (ServerChunkCache.ChunkAndHolder chunkAndHolder : list) { +- LevelChunk levelChunk = chunkAndHolder.chunk; +- ChunkPos pos = levelChunk.getPos(); +- if (this.level.isNaturalSpawningAllowed(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos)) { +- levelChunk.incrementInhabitedTime(l); +- if (_boolean && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(pos)) { +- NaturalSpawner.spawnForChunk(this.level, levelChunk, spawnState, this.spawnFriendlies, this.spawnEnemies, flag); ++ while (iterator1.hasNext()) { ++ ServerChunkCache.a chunkproviderserver_a = (ServerChunkCache.a) iterator1.next(); ++ LevelChunk chunk1 = chunkproviderserver_a.chunk; ++ ChunkPos chunkcoordintpair = chunk1.getPos(); ++ ++ if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair)) { ++ chunk1.incrementInhabitedTime(j); ++ if (flag && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair)) { ++ NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1); + } + +- if (this.level.shouldTickBlocksAt(pos.toLong())) { +- this.level.tickChunk(levelChunk, _int); ++ if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { ++ this.level.tickChunk(chunk1, l); + } + } + } + +- profiler.popPush("customSpawners"); +- if (_boolean) { ++ gameprofilerfiller.popPush("customSpawners"); ++ if (flag) { + this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); + } + } + +- profiler.popPush("broadcast"); +- list.forEach(chunkAndHolder1 -> chunkAndHolder1.holder.broadcastChanges(chunkAndHolder1.chunk)); +- profiler.pop(); +- profiler.pop(); ++ gameprofilerfiller.popPush("broadcast"); ++ list.forEach((chunkproviderserver_a1) -> { ++ chunkproviderserver_a1.holder.broadcastChanges(chunkproviderserver_a1.chunk); ++ }); ++ gameprofilerfiller.pop(); ++ gameprofilerfiller.pop(); + } + } + +- private void getFullChunk(long chunkPos, Consumer<LevelChunk> fullChunkGetter) { +- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(chunkPos); +- if (visibleChunkIfPresent != null) { +- visibleChunkIfPresent.getFullChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).left().ifPresent(fullChunkGetter); ++ private void getFullChunk(long chunkPos, Consumer<LevelChunk> consumer) { ++ ChunkHolder playerchunk = this.getVisibleChunkIfPresent(chunkPos); ++ ++ if (playerchunk != null) { ++ ((Either) playerchunk.getFullChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left().ifPresent(consumer); + } ++ + } + + @Override +@@ -447,20 +494,24 @@ + + public void blockChanged(BlockPos pos) { + int i = SectionPos.blockToSectionCoord(pos.getX()); +- int i1 = SectionPos.blockToSectionCoord(pos.getZ()); +- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(ChunkPos.asLong(i, i1)); +- if (visibleChunkIfPresent != null) { +- visibleChunkIfPresent.blockChanged(pos); ++ int j = SectionPos.blockToSectionCoord(pos.getZ()); ++ ChunkHolder playerchunk = this.getVisibleChunkIfPresent(ChunkPos.asLong(i, j)); ++ ++ if (playerchunk != null) { ++ playerchunk.blockChanged(pos); + } ++ + } + + @Override +- public void onLightUpdate(LightLayer type, SectionPos pos) { ++ public void onLightUpdate(EnumSkyBlock type, SectionPos pos) { + this.mainThreadProcessor.execute(() -> { +- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(pos.chunk().toLong()); +- if (visibleChunkIfPresent != null) { +- visibleChunkIfPresent.sectionLightChanged(type, pos.y()); ++ ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos.chunk().toLong()); ++ ++ if (playerchunk != null) { ++ playerchunk.sectionLightChanged(type, pos.y()); + } ++ + }); + } + +@@ -481,6 +532,7 @@ + if (!player.isRemoved()) { + this.chunkMap.move(player); + } ++ + } + + public void removeEntity(Entity entity) { +@@ -539,12 +591,10 @@ + this.distanceManager.removeTicketsOnClosing(); + } + +- static record ChunkAndHolder(LevelChunk chunk, ChunkHolder holder) { +- } ++ private final class MainThreadExecutor extends BlockableEventLoop<Runnable> { + +- final class MainThreadExecutor extends BlockableEventLoop<Runnable> { +- MainThreadExecutor(Level level) { +- super("Chunk source main thread executor for " + level.dimension().location()); ++ MainThreadExecutor(Level world) { ++ super("Chunk source main thread executor for " + world.dimension().location()); + } + + @Override +@@ -574,13 +624,23 @@ + } + + @Override ++ // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task + public boolean pollTask() { ++ try { + if (ServerChunkCache.this.runDistanceManagerUpdates()) { + return true; + } else { + ServerChunkCache.this.lightEngine.tryScheduleUpdate(); + return super.pollTask(); + } ++ } finally { ++ chunkMap.callbackExecutor.run(); + } ++ // CraftBukkit end ++ } + } ++ ++ private static record a(LevelChunk chunk, ChunkHolder holder) { ++ ++ } + } |