--- a/net/minecraft/server/level/WorldGenRegion.java
+++ b/net/minecraft/server/level/WorldGenRegion.java
@@ -78,6 +_,36 @@
     private final AtomicLong subTickCount = new AtomicLong();
     private static final ResourceLocation WORLDGEN_REGION_RANDOM = ResourceLocation.withDefaultNamespace("worldgen_region_random");
 
+    // Paper start - rewrite chunk system
+    /**
+     * During feature generation, light data is not initialised and will always return 15 in Starlight. Vanilla
+     * can possibly return 0 if partially initialised, which allows some mushroom blocks to generate.
+     * In general, the brightness value from the light engine should not be used until the chunk is ready. To emulate
+     * Vanilla behavior better, we return 0 as the brightness during world gen unless the target chunk is finished
+     * lighting.
+     */
+    @Override
+    public int getBrightness(final net.minecraft.world.level.LightLayer lightLayer, final BlockPos blockPos) {
+        final ChunkAccess chunk = this.getChunk(blockPos.getX() >> 4, blockPos.getZ() >> 4);
+        if (!chunk.isLightCorrect()) {
+            return 0;
+        }
+        return this.getLightEngine().getLayerListener(lightLayer).getLightValue(blockPos);
+    }
+
+    /**
+     * See above
+     */
+    @Override
+    public int getRawBrightness(final BlockPos blockPos, final int subtract) {
+        final ChunkAccess chunk = this.getChunk(blockPos.getX() >> 4, blockPos.getZ() >> 4);
+        if (!chunk.isLightCorrect()) {
+            return 0;
+        }
+        return this.getLightEngine().getRawBrightness(blockPos, subtract);
+    }
+    // Paper end - rewrite chunk system
+
     public WorldGenRegion(ServerLevel level, StaticCache2D<GenerationChunkHolder> cache, ChunkStep generatingStep, ChunkAccess center) {
         this.generatingStep = generatingStep;
         this.cache = cache;
@@ -151,6 +_,28 @@
         return chessboardDistance < this.generatingStep.directDependencies().size();
     }
 
+    // Paper start - if loaded util
+    @Nullable
+    @Override
+    public ChunkAccess getChunkIfLoadedImmediately(int x, int z) {
+        return this.getChunk(x, z, ChunkStatus.FULL, false);
+    }
+
+    @Nullable
+    @Override
+    public final BlockState getBlockStateIfLoaded(BlockPos pos) {
+        ChunkAccess chunk = this.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4);
+        return chunk == null ? null : chunk.getBlockState(pos);
+    }
+
+    @Nullable
+    @Override
+    public final FluidState getFluidIfLoaded(BlockPos pos) {
+        ChunkAccess chunk = this.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4);
+        return chunk == null ? null : chunk.getFluidState(pos);
+    }
+    // Paper end
+
     @Override
     public BlockState getBlockState(BlockPos pos) {
         return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())).getBlockState(pos);
@@ -198,7 +_,8 @@
         if (blockState.isAir()) {
             return false;
         } else {
-            if (dropBlock) {
+            if (dropBlock) LOGGER.warn("Potential async entity add during worldgen", new Throwable()); // Paper - Fix async entity add due to fungus trees; log when this happens
+            if (false) { // CraftBukkit - SPIGOT-6833: Do not drop during world generation
                 BlockEntity blockEntity = blockState.hasBlockEntity() ? this.getBlockEntity(pos) : null;
                 Block.dropResources(blockState, this.level, pos, blockEntity, entity, ItemStack.EMPTY);
             }
@@ -242,6 +_,7 @@
         }
     }
 
+    private boolean hasSetFarWarned = false; // Paper - Buffer OOB setBlock calls
     @Override
     public boolean ensureCanWrite(BlockPos pos) {
         int sectionPosX = SectionPos.blockToSectionCoord(pos.getX());
@@ -259,6 +_,8 @@
 
             return true;
         } else {
+            // Paper start - Buffer OOB setBlock calls
+            if (!hasSetFarWarned) {
             Util.logAndPauseIfInIde(
                 "Detected setBlock in a far chunk ["
                     + sectionPosX
@@ -270,6 +_,12 @@
                     + this.generatingStep.targetStatus()
                     + (this.currentlyGenerating == null ? "" : ", currently generating: " + this.currentlyGenerating.get())
             );
+                hasSetFarWarned = true;
+                if (this.getServer() != null && this.getServer().isDebugging()) {
+                    io.papermc.paper.util.TraceUtil.dumpTraceForThread("far setBlock call");
+                }
+            }
+            // Paper end - Buffer OOB setBlock calls
             return false;
         }
     }
@@ -280,7 +_,7 @@
             return false;
         } else {
             ChunkAccess chunk = this.getChunk(pos);
-            BlockState blockState = chunk.setBlockState(pos, state, flags);
+            BlockState blockState = chunk.setBlockState(pos, state, flags); final BlockState previousBlockState = blockState; // Paper - Clear block entity before setting up a DUMMY block entity - obfhelper
             if (blockState != null) {
                 this.level.updatePOIOnBlockStateChange(pos, blockState, state);
             }
@@ -294,6 +_,17 @@
                         chunk.removeBlockEntity(pos);
                     }
                 } else {
+                    // Paper start - Clear block entity before setting up a DUMMY block entity
+                    // The concept of removing a block entity when the block itself changes is generally lifted
+                    // from LevelChunk#setBlockState.
+                    // It is however to note that this may only run if the block actually changes.
+                    // Otherwise a chest block entity generated by a structure template that is later "updated" to
+                    // be waterlogged would remove its existing block entity (see PaperMC/Paper#10750)
+                    // This logic is *also* found in LevelChunk#setBlockState.
+                    if (previousBlockState != null && !java.util.Objects.equals(previousBlockState.getBlock(), state.getBlock())) {
+                        chunk.removeBlockEntity(pos);
+                    }
+                    // Paper end - Clear block entity before setting up a DUMMY block entity
                     CompoundTag compoundTag = new CompoundTag();
                     compoundTag.putInt("x", pos.getX());
                     compoundTag.putInt("y", pos.getY());
@@ -319,6 +_,13 @@
 
     @Override
     public boolean addFreshEntity(Entity entity) {
+        // CraftBukkit start
+        return this.addFreshEntity(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
+    }
+
+    @Override
+    public boolean addFreshEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
+        // CraftBukkit end
         int sectionPosX = SectionPos.blockToSectionCoord(entity.getBlockX());
         int sectionPosZ = SectionPos.blockToSectionCoord(entity.getBlockZ());
         this.getChunk(sectionPosX, sectionPosZ).addEntity(entity);
