--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -14,33 +14,56 @@
 import net.minecraft.world.level.material.FluidState;
 
 public class LevelChunkSection {
+
     public static final int SECTION_WIDTH = 16;
     public static final int SECTION_HEIGHT = 16;
     public static final int SECTION_SIZE = 4096;
     public static final int BIOME_CONTAINER_BITS = 2;
-    private short nonEmptyBlockCount;
+    short nonEmptyBlockCount; // Paper - package-private
     private short tickingBlockCount;
     private short tickingFluidCount;
     public final PalettedContainer<BlockState> states;
-    private PalettedContainerRO<Holder<Biome>> biomes;
+    // CraftBukkit start - read/write
+    private PalettedContainer<Holder<Biome>> biomes;
+    public final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper
+    // Paper start - optimise collisions
+    private int specialCollidingBlocks;
 
-    public LevelChunkSection(PalettedContainer<BlockState> blockStateContainer, PalettedContainerRO<Holder<Biome>> biomeContainer) {
-        this.states = blockStateContainer;
-        this.biomes = biomeContainer;
+    private void updateBlockCallback(final int x, final int y, final int z, final BlockState oldState, final BlockState newState) {
+        if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(newState)) {
+            ++this.specialCollidingBlocks;
+        }
+        if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(oldState)) {
+            --this.specialCollidingBlocks;
+        }
+    }
+
+    public final int getSpecialCollidingBlocks() {
+        return this.specialCollidingBlocks;
+    }
+    // Paper end - optimise collisions
+
+    public LevelChunkSection(PalettedContainer<BlockState> datapaletteblock, PalettedContainer<Holder<Biome>> palettedcontainerro) {
+        // CraftBukkit end
+        this.states = datapaletteblock;
+        this.biomes = palettedcontainerro;
         this.recalcBlockCounts();
     }
 
-    public LevelChunkSection(Registry<Biome> biomeRegistry) {
-        this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
-        this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
+    // Paper start - Anti-Xray - Add parameters
+    @Deprecated @io.papermc.paper.annotation.DoNotUse public LevelChunkSection(Registry<Biome> biomeRegistry) { this(biomeRegistry, null, null, 0); }
+    public LevelChunkSection(Registry<Biome> biomeRegistry, net.minecraft.world.level.Level level, net.minecraft.world.level.ChunkPos chunkPos, int chunkSectionY) {
+        // Paper end
+        this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, level == null || level.chunkPacketBlockController == null ? null : level.chunkPacketBlockController.getPresetBlockStates(level, chunkPos, chunkSectionY)); // Paper - Anti-Xray - Add preset block states
+        this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes
     }
 
     public BlockState getBlockState(int x, int y, int z) {
-        return this.states.get(x, y, z);
+        return (BlockState) this.states.get(x, y, z);
     }
 
     public FluidState getFluidState(int x, int y, int z) {
-        return this.states.get(x, y, z).getFluidState();
+        return this.states.get(x, y, z).getFluidState(); // Paper - diff on change - we expect this to be effectively just getType(x, y, z).getFluid(). If this changes we need to check other patches that use IBlockData#getFluid.
     }
 
     public void acquire() {
@@ -55,24 +78,29 @@
         return this.setBlockState(x, y, z, state, true);
     }
 
-    public BlockState setBlockState(int x, int y, int z, BlockState state, boolean lock) {
-        BlockState blockState;
+    public BlockState setBlockState(int x, int y, int z, BlockState state, boolean lock) {  // Paper - state -> new state
+        BlockState iblockdata1; // Paper - iblockdata1 -> oldState
+
         if (lock) {
-            blockState = this.states.getAndSet(x, y, z, state);
+            iblockdata1 = (BlockState) this.states.getAndSet(x, y, z, state);
         } else {
-            blockState = this.states.getAndSetUnchecked(x, y, z, state);
+            iblockdata1 = (BlockState) this.states.getAndSetUnchecked(x, y, z, state);
         }
 
-        FluidState fluidState = blockState.getFluidState();
-        FluidState fluidState2 = state.getFluidState();
-        if (!blockState.isAir()) {
+        FluidState fluid = iblockdata1.getFluidState();
+        FluidState fluid1 = state.getFluidState();
+
+        if (!iblockdata1.isAir()) {
             --this.nonEmptyBlockCount;
-            if (blockState.isRandomlyTicking()) {
+            if (iblockdata1.isRandomlyTicking()) {
                 --this.tickingBlockCount;
+                // Paper start
+                this.tickingList.remove(x, y, z);
+                // Paper end
             }
         }
 
-        if (!fluidState.isEmpty()) {
+        if (!fluid.isEmpty()) {
             --this.tickingFluidCount;
         }
 
@@ -80,14 +108,18 @@
             ++this.nonEmptyBlockCount;
             if (state.isRandomlyTicking()) {
                 ++this.tickingBlockCount;
+                // Paper start
+                this.tickingList.add(x, y, z, state);
+                // Paper end
             }
         }
 
-        if (!fluidState2.isEmpty()) {
+        if (!fluid1.isEmpty()) {
             ++this.tickingFluidCount;
         }
 
-        return blockState;
+        this.updateBlockCallback(x, y, z, iblockdata1, state); // Paper - optimise collisions
+        return iblockdata1;
     }
 
     public boolean hasOnlyAir() {
@@ -107,36 +139,39 @@
     }
 
     public void recalcBlockCounts() {
-        class BlockCounter implements PalettedContainer.CountConsumer<BlockState> {
-            public int nonEmptyBlockCount;
-            public int tickingBlockCount;
-            public int tickingFluidCount;
+        // Paper start - unfuck this
+        this.tickingList.clear();
+        this.nonEmptyBlockCount = 0;
+        this.tickingBlockCount = 0;
+        this.tickingFluidCount = 0;
+        if (this.maybeHas((BlockState state) -> !state.isAir() || !state.getFluidState().isEmpty())) { // Paper - do not run forEachLocation on clearly empty sections
+        this.states.forEachLocation((BlockState iblockdata, int i) -> {
+            FluidState fluid = iblockdata.getFluidState();
 
-            @Override
-            public void accept(BlockState blockState, int i) {
-                FluidState fluidState = blockState.getFluidState();
-                if (!blockState.isAir()) {
-                    this.nonEmptyBlockCount += i;
-                    if (blockState.isRandomlyTicking()) {
-                        this.tickingBlockCount += i;
-                    }
+            if (!iblockdata.isAir()) {
+                this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1);
+                if (iblockdata.isRandomlyTicking()) {
+                    this.tickingBlockCount = (short)(this.tickingBlockCount + 1);
+                    this.tickingList.add(i, iblockdata);
                 }
+            }
 
-                if (!fluidState.isEmpty()) {
-                    this.nonEmptyBlockCount += i;
-                    if (fluidState.isRandomlyTicking()) {
-                        this.tickingFluidCount += i;
-                    }
+            if (!fluid.isEmpty()) {
+                this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1);
+                if (fluid.isRandomlyTicking()) {
+                    this.tickingFluidCount = (short) (this.tickingFluidCount + 1);
                 }
+            }
 
+            // Paper start - optimise collisions
+            if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(iblockdata)) {
+                ++this.specialCollidingBlocks;
             }
-        }
+            // Paper end - optimise collisions
 
-        BlockCounter lv = new BlockCounter();
-        this.states.count(lv);
-        this.nonEmptyBlockCount = (short)lv.nonEmptyBlockCount;
-        this.tickingBlockCount = (short)lv.tickingBlockCount;
-        this.tickingFluidCount = (short)lv.tickingFluidCount;
+        });
+        } // Paper - do not run forEachLocation on clearly empty sections
+        // Paper end
     }
 
     public PalettedContainer<BlockState> getStates() {
@@ -150,21 +185,26 @@
     public void read(FriendlyByteBuf buf) {
         this.nonEmptyBlockCount = buf.readShort();
         this.states.read(buf);
-        PalettedContainer<Holder<Biome>> palettedContainer = this.biomes.recreate();
-        palettedContainer.read(buf);
-        this.biomes = palettedContainer;
+        PalettedContainer<Holder<Biome>> datapaletteblock = this.biomes.recreate();
+
+        datapaletteblock.read(buf);
+        this.biomes = datapaletteblock;
     }
 
     public void readBiomes(FriendlyByteBuf buf) {
-        PalettedContainer<Holder<Biome>> palettedContainer = this.biomes.recreate();
-        palettedContainer.read(buf);
-        this.biomes = palettedContainer;
+        PalettedContainer<Holder<Biome>> datapaletteblock = this.biomes.recreate();
+
+        datapaletteblock.read(buf);
+        this.biomes = datapaletteblock;
     }
 
-    public void write(FriendlyByteBuf buf) {
+    // Paper start - Anti-Xray - Add chunk packet info
+    @Deprecated @io.papermc.paper.annotation.DoNotUse public void write(FriendlyByteBuf buf) { this.write(buf, null, 0); }
+    public void write(FriendlyByteBuf buf, com.destroystokyo.paper.antixray.ChunkPacketInfo<BlockState> chunkPacketInfo, int chunkSectionIndex) {
         buf.writeShort(this.nonEmptyBlockCount);
-        this.states.write(buf);
-        this.biomes.write(buf);
+        this.states.write(buf, chunkPacketInfo, chunkSectionIndex);
+        this.biomes.write(buf, null, chunkSectionIndex);
+        // Paper end
     }
 
     public int getSerializedSize() {
@@ -176,21 +216,27 @@
     }
 
     public Holder<Biome> getNoiseBiome(int x, int y, int z) {
-        return this.biomes.get(x, y, z);
+        return (Holder) this.biomes.get(x, y, z);
     }
 
+    // CraftBukkit start
+    public void setBiome(int i, int j, int k, Holder<Biome> biome) {
+        this.biomes.set(i, j, k, biome);
+    }
+    // CraftBukkit end
+
     public void fillBiomesFromNoise(BiomeResolver biomeSupplier, Climate.Sampler sampler, int x, int y, int z) {
-        PalettedContainer<Holder<Biome>> palettedContainer = this.biomes.recreate();
-        int i = 4;
+        PalettedContainer<Holder<Biome>> datapaletteblock = this.biomes.recreate();
+        boolean flag = true;
 
-        for(int j = 0; j < 4; ++j) {
-            for(int k = 0; k < 4; ++k) {
-                for(int l = 0; l < 4; ++l) {
-                    palettedContainer.getAndSetUnchecked(j, k, l, biomeSupplier.getNoiseBiome(x + j, y + k, z + l, sampler));
+        for (int l = 0; l < 4; ++l) {
+            for (int i1 = 0; i1 < 4; ++i1) {
+                for (int j1 = 0; j1 < 4; ++j1) {
+                    datapaletteblock.getAndSetUnchecked(l, i1, j1, biomeSupplier.getNoiseBiome(x + l, y + i1, z + j1, sampler));
                 }
             }
         }
 
-        this.biomes = palettedContainer;
+        this.biomes = datapaletteblock;
     }
 }
