aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/1001-Anti-Xray.patch
diff options
context:
space:
mode:
authorJake Potrebic <[email protected]>2024-06-15 06:22:06 -0700
committerJake Potrebic <[email protected]>2024-06-15 06:22:06 -0700
commit5d834b1b7136de9525dfa405cf6c466c62c3abc1 (patch)
treea37d70fd22240ea2edfcc43b02716a22f78c7032 /patches/server/1001-Anti-Xray.patch
parentee9b820fbebde78cf6462e5a5b530fcd5912ab78 (diff)
downloadPaper-5d834b1b7136de9525dfa405cf6c466c62c3abc1.tar.gz
Paper-5d834b1b7136de9525dfa405cf6c466c62c3abc1.zip
rebuild patches
Diffstat (limited to 'patches/server/1001-Anti-Xray.patch')
-rw-r--r--patches/server/1001-Anti-Xray.patch1639
1 files changed, 0 insertions, 1639 deletions
diff --git a/patches/server/1001-Anti-Xray.patch b/patches/server/1001-Anti-Xray.patch
deleted file mode 100644
index ee9b38a427..0000000000
--- a/patches/server/1001-Anti-Xray.patch
+++ /dev/null
@@ -1,1639 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: stonar96 <[email protected]>
-Date: Thu, 25 Nov 2021 13:27:51 +0100
-Subject: [PATCH] Anti-Xray
-
-
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java b/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..e448c26327b5f6189c3c52e698cff66c8f9ad81a
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java
-@@ -0,0 +1,51 @@
-+package com.destroystokyo.paper.antixray;
-+
-+public final class BitStorageReader {
-+
-+ private byte[] buffer;
-+ private int bits;
-+ private int mask;
-+ private int longInBufferIndex;
-+ private int bitInLongIndex;
-+ private long current;
-+
-+ public void setBuffer(byte[] buffer) {
-+ this.buffer = buffer;
-+ }
-+
-+ public void setBits(int bits) {
-+ this.bits = bits;
-+ mask = (1 << bits) - 1;
-+ }
-+
-+ public void setIndex(int index) {
-+ longInBufferIndex = index;
-+ bitInLongIndex = 0;
-+ init();
-+ }
-+
-+ private void init() {
-+ if (buffer.length > longInBufferIndex + 7) {
-+ current = ((((long) buffer[longInBufferIndex]) << 56)
-+ | (((long) buffer[longInBufferIndex + 1] & 0xff) << 48)
-+ | (((long) buffer[longInBufferIndex + 2] & 0xff) << 40)
-+ | (((long) buffer[longInBufferIndex + 3] & 0xff) << 32)
-+ | (((long) buffer[longInBufferIndex + 4] & 0xff) << 24)
-+ | (((long) buffer[longInBufferIndex + 5] & 0xff) << 16)
-+ | (((long) buffer[longInBufferIndex + 6] & 0xff) << 8)
-+ | (((long) buffer[longInBufferIndex + 7] & 0xff)));
-+ }
-+ }
-+
-+ public int read() {
-+ if (bitInLongIndex + bits > 64) {
-+ bitInLongIndex = 0;
-+ longInBufferIndex += 8;
-+ init();
-+ }
-+
-+ int value = (int) (current >>> bitInLongIndex) & mask;
-+ bitInLongIndex += bits;
-+ return value;
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java b/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..e4540ea278f2dc871cb6a3cb8897559bfd65e134
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java
-@@ -0,0 +1,79 @@
-+package com.destroystokyo.paper.antixray;
-+
-+public final class BitStorageWriter {
-+
-+ private byte[] buffer;
-+ private int bits;
-+ private long mask;
-+ private int longInBufferIndex;
-+ private int bitInLongIndex;
-+ private long current;
-+ private boolean dirty;
-+
-+ public void setBuffer(byte[] buffer) {
-+ this.buffer = buffer;
-+ }
-+
-+ public void setBits(int bits) {
-+ this.bits = bits;
-+ mask = (1L << bits) - 1;
-+ }
-+
-+ public void setIndex(int index) {
-+ longInBufferIndex = index;
-+ bitInLongIndex = 0;
-+ init();
-+ }
-+
-+ private void init() {
-+ if (buffer.length > longInBufferIndex + 7) {
-+ current = ((((long) buffer[longInBufferIndex]) << 56)
-+ | (((long) buffer[longInBufferIndex + 1] & 0xff) << 48)
-+ | (((long) buffer[longInBufferIndex + 2] & 0xff) << 40)
-+ | (((long) buffer[longInBufferIndex + 3] & 0xff) << 32)
-+ | (((long) buffer[longInBufferIndex + 4] & 0xff) << 24)
-+ | (((long) buffer[longInBufferIndex + 5] & 0xff) << 16)
-+ | (((long) buffer[longInBufferIndex + 6] & 0xff) << 8)
-+ | (((long) buffer[longInBufferIndex + 7] & 0xff)));
-+ }
-+
-+ dirty = false;
-+ }
-+
-+ public void flush() {
-+ if (dirty && buffer.length > longInBufferIndex + 7) {
-+ buffer[longInBufferIndex] = (byte) (current >> 56 & 0xff);
-+ buffer[longInBufferIndex + 1] = (byte) (current >> 48 & 0xff);
-+ buffer[longInBufferIndex + 2] = (byte) (current >> 40 & 0xff);
-+ buffer[longInBufferIndex + 3] = (byte) (current >> 32 & 0xff);
-+ buffer[longInBufferIndex + 4] = (byte) (current >> 24 & 0xff);
-+ buffer[longInBufferIndex + 5] = (byte) (current >> 16 & 0xff);
-+ buffer[longInBufferIndex + 6] = (byte) (current >> 8 & 0xff);
-+ buffer[longInBufferIndex + 7] = (byte) (current & 0xff);
-+ }
-+ }
-+
-+ public void write(int value) {
-+ if (bitInLongIndex + bits > 64) {
-+ flush();
-+ bitInLongIndex = 0;
-+ longInBufferIndex += 8;
-+ init();
-+ }
-+
-+ current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
-+ dirty = true;
-+ bitInLongIndex += bits;
-+ }
-+
-+ public void skip() {
-+ bitInLongIndex += bits;
-+
-+ if (bitInLongIndex > 64) {
-+ flush();
-+ bitInLongIndex = bits;
-+ longInBufferIndex += 8;
-+ init();
-+ }
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..52d2e2b744f91914802506e52a07161729bbcf3a
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java
-@@ -0,0 +1,45 @@
-+package com.destroystokyo.paper.antixray;
-+
-+import net.minecraft.core.BlockPos;
-+import net.minecraft.core.Direction;
-+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
-+import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
-+import net.minecraft.server.level.ServerPlayer;
-+import net.minecraft.server.level.ServerPlayerGameMode;
-+import net.minecraft.world.level.ChunkPos;
-+import net.minecraft.world.level.Level;
-+import net.minecraft.world.level.block.state.BlockState;
-+import net.minecraft.world.level.chunk.LevelChunk;
-+
-+public class ChunkPacketBlockController {
-+
-+ public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController();
-+
-+ protected ChunkPacketBlockController() {
-+
-+ }
-+
-+ public BlockState[] getPresetBlockStates(Level level, ChunkPos chunkPos, int chunkSectionY) {
-+ return null;
-+ }
-+
-+ public boolean shouldModify(ServerPlayer player, LevelChunk chunk) {
-+ return false;
-+ }
-+
-+ public ChunkPacketInfo<BlockState> getChunkPacketInfo(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk) {
-+ return null;
-+ }
-+
-+ public void modifyBlocks(ClientboundLevelChunkWithLightPacket chunkPacket, ChunkPacketInfo<BlockState> chunkPacketInfo) {
-+ chunkPacket.setReady(true);
-+ }
-+
-+ public void onBlockChange(Level level, BlockPos blockPos, BlockState newBlockState, BlockState oldBlockState, int flags, int maxUpdateDepth) {
-+
-+ }
-+
-+ public void onPlayerLeftClickBlock(ServerPlayerGameMode serverPlayerGameMode, BlockPos blockPos, ServerboundPlayerActionPacket.Action action, Direction direction, int worldHeight, int sequence) {
-+
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..e7fe98ea30ae6d0baea3ec1f9f98a89502a49a12
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
-@@ -0,0 +1,676 @@
-+package com.destroystokyo.paper.antixray;
-+
-+import io.papermc.paper.configuration.WorldConfiguration;
-+import io.papermc.paper.configuration.type.EngineMode;
-+import java.util.ArrayList;
-+import java.util.LinkedHashSet;
-+import java.util.LinkedList;
-+import java.util.List;
-+import java.util.Set;
-+import java.util.concurrent.Executor;
-+import java.util.concurrent.ThreadLocalRandom;
-+import java.util.function.IntSupplier;
-+import net.minecraft.core.BlockPos;
-+import net.minecraft.core.Direction;
-+import net.minecraft.core.registries.Registries;
-+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
-+import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
-+import net.minecraft.server.MinecraftServer;
-+import net.minecraft.server.level.ServerLevel;
-+import net.minecraft.server.level.ServerPlayer;
-+import net.minecraft.server.level.ServerPlayerGameMode;
-+import net.minecraft.world.level.ChunkPos;
-+import net.minecraft.world.level.Level;
-+import net.minecraft.world.level.biome.Biomes;
-+import net.minecraft.world.level.block.Block;
-+import net.minecraft.world.level.block.Blocks;
-+import net.minecraft.world.level.block.EntityBlock;
-+import net.minecraft.world.level.block.state.BlockState;
-+import net.minecraft.world.level.chunk.EmptyLevelChunk;
-+import net.minecraft.world.level.chunk.GlobalPalette;
-+import net.minecraft.world.level.chunk.LevelChunk;
-+import net.minecraft.world.level.chunk.LevelChunkSection;
-+import net.minecraft.world.level.chunk.MissingPaletteEntryException;
-+import net.minecraft.world.level.chunk.Palette;
-+import org.bukkit.Bukkit;
-+
-+public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController {
-+
-+ private static final Palette<BlockState> GLOBAL_BLOCKSTATE_PALETTE = new GlobalPalette<>(Block.BLOCK_STATE_REGISTRY);
-+ private static final LevelChunkSection EMPTY_SECTION = null;
-+ private final Executor executor;
-+ private final EngineMode engineMode;
-+ private final int maxBlockHeight;
-+ private final int updateRadius;
-+ private final boolean usePermission;
-+ private final BlockState[] presetBlockStates;
-+ private final BlockState[] presetBlockStatesFull;
-+ private final BlockState[] presetBlockStatesStone;
-+ private final BlockState[] presetBlockStatesDeepslate;
-+ private final BlockState[] presetBlockStatesNetherrack;
-+ private final BlockState[] presetBlockStatesEndStone;
-+ private final int[] presetBlockStateBitsGlobal;
-+ private final int[] presetBlockStateBitsStoneGlobal;
-+ private final int[] presetBlockStateBitsDeepslateGlobal;
-+ private final int[] presetBlockStateBitsNetherrackGlobal;
-+ private final int[] presetBlockStateBitsEndStoneGlobal;
-+ private final boolean[] solidGlobal = new boolean[Block.BLOCK_STATE_REGISTRY.size()];
-+ private final boolean[] obfuscateGlobal = new boolean[Block.BLOCK_STATE_REGISTRY.size()];
-+ private final LevelChunkSection[] emptyNearbyChunkSections = {EMPTY_SECTION, EMPTY_SECTION, EMPTY_SECTION, EMPTY_SECTION};
-+ private final int maxBlockHeightUpdatePosition;
-+
-+ public ChunkPacketBlockControllerAntiXray(Level level, Executor executor) {
-+ this.executor = executor;
-+ WorldConfiguration.Anticheat.AntiXray paperWorldConfig = level.paperConfig().anticheat.antiXray;
-+ engineMode = paperWorldConfig.engineMode;
-+ maxBlockHeight = paperWorldConfig.maxBlockHeight >> 4 << 4;
-+ updateRadius = paperWorldConfig.updateRadius;
-+ usePermission = paperWorldConfig.usePermission;
-+ List<Block> toObfuscate;
-+
-+ if (engineMode == EngineMode.HIDE) {
-+ toObfuscate = paperWorldConfig.hiddenBlocks;
-+ presetBlockStates = null;
-+ presetBlockStatesFull = null;
-+ presetBlockStatesStone = new BlockState[]{Blocks.STONE.defaultBlockState()};
-+ presetBlockStatesDeepslate = new BlockState[]{Blocks.DEEPSLATE.defaultBlockState()};
-+ presetBlockStatesNetherrack = new BlockState[]{Blocks.NETHERRACK.defaultBlockState()};
-+ presetBlockStatesEndStone = new BlockState[]{Blocks.END_STONE.defaultBlockState()};
-+ presetBlockStateBitsGlobal = null;
-+ presetBlockStateBitsStoneGlobal = new int[]{GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.STONE.defaultBlockState())};
-+ presetBlockStateBitsDeepslateGlobal = new int[]{GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.DEEPSLATE.defaultBlockState())};
-+ presetBlockStateBitsNetherrackGlobal = new int[]{GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.NETHERRACK.defaultBlockState())};
-+ presetBlockStateBitsEndStoneGlobal = new int[]{GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.END_STONE.defaultBlockState())};
-+ } else {
-+ toObfuscate = new ArrayList<>(paperWorldConfig.replacementBlocks);
-+ List<BlockState> presetBlockStateList = new LinkedList<>();
-+
-+ for (Block block : paperWorldConfig.hiddenBlocks) {
-+
-+ if (!(block instanceof EntityBlock)) {
-+ toObfuscate.add(block);
-+ presetBlockStateList.add(block.defaultBlockState());
-+ }
-+ }
-+
-+ // The doc of the LinkedHashSet(Collection<? extends E>) constructor doesn't specify that the insertion order is the predictable iteration order of the specified Collection, although it is in the implementation
-+ Set<BlockState> presetBlockStateSet = new LinkedHashSet<>();
-+ // Therefore addAll(Collection<? extends E>) is used, which guarantees this order in the doc
-+ presetBlockStateSet.addAll(presetBlockStateList);
-+ presetBlockStates = presetBlockStateSet.isEmpty() ? new BlockState[]{Blocks.DIAMOND_ORE.defaultBlockState()} : presetBlockStateSet.toArray(new BlockState[0]);
-+ presetBlockStatesFull = presetBlockStateSet.isEmpty() ? new BlockState[]{Blocks.DIAMOND_ORE.defaultBlockState()} : presetBlockStateList.toArray(new BlockState[0]);
-+ presetBlockStatesStone = null;
-+ presetBlockStatesDeepslate = null;
-+ presetBlockStatesNetherrack = null;
-+ presetBlockStatesEndStone = null;
-+ presetBlockStateBitsGlobal = new int[presetBlockStatesFull.length];
-+
-+ for (int i = 0; i < presetBlockStatesFull.length; i++) {
-+ presetBlockStateBitsGlobal[i] = GLOBAL_BLOCKSTATE_PALETTE.idFor(presetBlockStatesFull[i]);
-+ }
-+
-+ presetBlockStateBitsStoneGlobal = null;
-+ presetBlockStateBitsDeepslateGlobal = null;
-+ presetBlockStateBitsNetherrackGlobal = null;
-+ presetBlockStateBitsEndStoneGlobal = null;
-+ }
-+
-+ for (Block block : toObfuscate) {
-+
-+ // Don't obfuscate air because air causes unnecessary block updates and causes block updates to fail in the void
-+ if (block != null && !block.defaultBlockState().isAir()) {
-+ // Replace all block states of a specified block
-+ for (BlockState blockState : block.getStateDefinition().getPossibleStates()) {
-+ obfuscateGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)] = true;
-+ }
-+ }
-+ }
-+
-+ EmptyLevelChunk emptyChunk = new EmptyLevelChunk(level, new ChunkPos(0, 0), MinecraftServer.getServer().registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.PLAINS));
-+ BlockPos zeroPos = new BlockPos(0, 0, 0);
-+
-+ for (int i = 0; i < solidGlobal.length; i++) {
-+ BlockState blockState = GLOBAL_BLOCKSTATE_PALETTE.valueFor(i);
-+
-+ if (blockState != null) {
-+ solidGlobal[i] = blockState.isRedstoneConductor(emptyChunk, zeroPos)
-+ && blockState.getBlock() != Blocks.SPAWNER && blockState.getBlock() != Blocks.BARRIER && blockState.getBlock() != Blocks.SHULKER_BOX && blockState.getBlock() != Blocks.SLIME_BLOCK && blockState.getBlock() != Blocks.MANGROVE_ROOTS || paperWorldConfig.lavaObscures && blockState == Blocks.LAVA.defaultBlockState();
-+ // Comparing blockState == Blocks.LAVA.defaultBlockState() instead of blockState.getBlock() == Blocks.LAVA ensures that only "stationary lava" is used
-+ // shulker box checks TE.
-+ }
-+ }
-+
-+ maxBlockHeightUpdatePosition = maxBlockHeight + updateRadius - 1;
-+ }
-+
-+ private int getPresetBlockStatesFullLength() {
-+ return engineMode == EngineMode.HIDE ? 1 : presetBlockStatesFull.length;
-+ }
-+
-+ @Override
-+ public BlockState[] getPresetBlockStates(Level level, ChunkPos chunkPos, int chunkSectionY) {
-+ // Return the block states to be added to the paletted containers so that they can be used for obfuscation
-+ int bottomBlockY = chunkSectionY << 4;
-+
-+ if (bottomBlockY < maxBlockHeight) {
-+ if (engineMode == EngineMode.HIDE) {
-+ return switch (level.getWorld().getEnvironment()) {
-+ case NETHER -> presetBlockStatesNetherrack;
-+ case THE_END -> presetBlockStatesEndStone;
-+ default -> bottomBlockY < 0 ? presetBlockStatesDeepslate : presetBlockStatesStone;
-+ };
-+ }
-+
-+ return presetBlockStates;
-+ }
-+
-+ return null;
-+ }
-+
-+ @Override
-+ public boolean shouldModify(ServerPlayer player, LevelChunk chunk) {
-+ return !usePermission || !player.getBukkitEntity().hasPermission("paper.antixray.bypass");
-+ }
-+
-+ @Override
-+ public ChunkPacketInfoAntiXray getChunkPacketInfo(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk) {
-+ // Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later
-+ return new ChunkPacketInfoAntiXray(chunkPacket, chunk, this);
-+ }
-+
-+ @Override
-+ public void modifyBlocks(ClientboundLevelChunkWithLightPacket chunkPacket, ChunkPacketInfo<BlockState> chunkPacketInfo) {
-+ if (!(chunkPacketInfo instanceof ChunkPacketInfoAntiXray)) {
-+ chunkPacket.setReady(true);
-+ return;
-+ }
-+
-+ if (!Bukkit.isPrimaryThread()) {
-+ // Plugins?
-+ MinecraftServer.getServer().scheduleOnMain(() -> modifyBlocks(chunkPacket, chunkPacketInfo));
-+ return;
-+ }
-+
-+ LevelChunk chunk = chunkPacketInfo.getChunk();
-+ int x = chunk.getPos().x;
-+ int z = chunk.getPos().z;
-+ Level level = chunk.getLevel();
-+ ((ChunkPacketInfoAntiXray) chunkPacketInfo).setNearbyChunks(level.getChunkIfLoaded(x - 1, z), level.getChunkIfLoaded(x + 1, z), level.getChunkIfLoaded(x, z - 1), level.getChunkIfLoaded(x, z + 1));
-+ executor.execute((Runnable) chunkPacketInfo);
-+ }
-+
-+ // Actually these fields should be variables inside the obfuscate method but in sync mode or with SingleThreadExecutor in async mode it's okay (even without ThreadLocal)
-+ // If an ExecutorService with multiple threads is used, ThreadLocal must be used here
-+ private final ThreadLocal<int[]> presetBlockStateBits = ThreadLocal.withInitial(() -> new int[getPresetBlockStatesFullLength()]);
-+ private static final ThreadLocal<boolean[]> SOLID = ThreadLocal.withInitial(() -> new boolean[Block.BLOCK_STATE_REGISTRY.size()]);
-+ private static final ThreadLocal<boolean[]> OBFUSCATE = ThreadLocal.withInitial(() -> new boolean[Block.BLOCK_STATE_REGISTRY.size()]);
-+ // These boolean arrays represent chunk layers, true means don't obfuscate, false means obfuscate
-+ private static final ThreadLocal<boolean[][]> CURRENT = ThreadLocal.withInitial(() -> new boolean[16][16]);
-+ private static final ThreadLocal<boolean[][]> NEXT = ThreadLocal.withInitial(() -> new boolean[16][16]);
-+ private static final ThreadLocal<boolean[][]> NEXT_NEXT = ThreadLocal.withInitial(() -> new boolean[16][16]);
-+
-+ public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) {
-+ int[] presetBlockStateBits = this.presetBlockStateBits.get();
-+ boolean[] solid = SOLID.get();
-+ boolean[] obfuscate = OBFUSCATE.get();
-+ boolean[][] current = CURRENT.get();
-+ boolean[][] next = NEXT.get();
-+ boolean[][] nextNext = NEXT_NEXT.get();
-+ // bitStorageReader, bitStorageWriter and nearbyChunkSections could also be reused (with ThreadLocal if necessary) but it's not worth it
-+ BitStorageReader bitStorageReader = new BitStorageReader();
-+ BitStorageWriter bitStorageWriter = new BitStorageWriter();
-+ LevelChunkSection[] nearbyChunkSections = new LevelChunkSection[4];
-+ LevelChunk chunk = chunkPacketInfoAntiXray.getChunk();
-+ Level level = chunk.getLevel();
-+ int maxChunkSectionIndex = Math.min((maxBlockHeight >> 4) - chunk.getMinSection(), chunk.getSectionsCount()) - 1;
-+ boolean[] solidTemp = null;
-+ boolean[] obfuscateTemp = null;
-+ bitStorageReader.setBuffer(chunkPacketInfoAntiXray.getBuffer());
-+ bitStorageWriter.setBuffer(chunkPacketInfoAntiXray.getBuffer());
-+ int numberOfBlocks = presetBlockStateBits.length;
-+ // Keep the lambda expressions as simple as possible. They are used very frequently.
-+ LayeredIntSupplier random = numberOfBlocks == 1 ? (() -> 0) : engineMode == EngineMode.OBFUSCATE_LAYER ? new LayeredIntSupplier() {
-+ // engine-mode: 3
-+ private int state;
-+ private int next;
-+
-+ {
-+ while ((state = ThreadLocalRandom.current().nextInt()) == 0) ;
-+ }
-+
-+ @Override
-+ public void nextLayer() {
-+ // https://en.wikipedia.org/wiki/Xorshift
-+ state ^= state << 13;
-+ state ^= state >>> 17;
-+ state ^= state << 5;
-+ // https://www.pcg-random.org/posts/bounded-rands.html
-+ next = (int) ((Integer.toUnsignedLong(state) * numberOfBlocks) >>> 32);
-+ }
-+
-+ @Override
-+ public int getAsInt() {
-+ return next;
-+ }
-+ } : new LayeredIntSupplier() {
-+ // engine-mode: 2
-+ private int state;
-+
-+ {
-+ while ((state = ThreadLocalRandom.current().nextInt()) == 0) ;
-+ }
-+
-+ @Override
-+ public int getAsInt() {
-+ // https://en.wikipedia.org/wiki/Xorshift
-+ state ^= state << 13;
-+ state ^= state >>> 17;
-+ state ^= state << 5;
-+ // https://www.pcg-random.org/posts/bounded-rands.html
-+ return (int) ((Integer.toUnsignedLong(state) * numberOfBlocks) >>> 32);
-+ }
-+ };
-+
-+ for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) {
-+ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex) != null) {
-+ int[] presetBlockStateBitsTemp;
-+
-+ if (chunkPacketInfoAntiXray.getPalette(chunkSectionIndex) instanceof GlobalPalette) {
-+ if (engineMode == EngineMode.HIDE) {
-+ presetBlockStateBitsTemp = switch (level.getWorld().getEnvironment()) {
-+ case NETHER -> presetBlockStateBitsNetherrackGlobal;
-+ case THE_END -> presetBlockStateBitsEndStoneGlobal;
-+ default -> chunkSectionIndex + chunk.getMinSection() < 0 ? presetBlockStateBitsDeepslateGlobal : presetBlockStateBitsStoneGlobal;
-+ };
-+ } else {
-+ presetBlockStateBitsTemp = presetBlockStateBitsGlobal;
-+ }
-+ } else {
-+ // If it's presetBlockStates, use this.presetBlockStatesFull instead
-+ BlockState[] presetBlockStatesFull = chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex) == presetBlockStates ? this.presetBlockStatesFull : chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex);
-+ presetBlockStateBitsTemp = presetBlockStateBits;
-+
-+ for (int i = 0; i < presetBlockStateBitsTemp.length; i++) {
-+ // This is thread safe because we only request IDs that are guaranteed to be in the palette and are visible
-+ // For more details see the comments in the readPalette method
-+ presetBlockStateBitsTemp[i] = chunkPacketInfoAntiXray.getPalette(chunkSectionIndex).idFor(presetBlockStatesFull[i]);
-+ }
-+ }
-+
-+ bitStorageWriter.setIndex(chunkPacketInfoAntiXray.getIndex(chunkSectionIndex));
-+
-+ // Check if the chunk section below was not obfuscated
-+ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex - 1) == null) {
-+ // If so, initialize some stuff
-+ bitStorageReader.setBits(chunkPacketInfoAntiXray.getBits(chunkSectionIndex));
-+ bitStorageReader.setIndex(chunkPacketInfoAntiXray.getIndex(chunkSectionIndex));
-+ solidTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex), solid, solidGlobal);
-+ obfuscateTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex), obfuscate, obfuscateGlobal);
-+ // Read the blocks of the upper layer of the chunk section below if it exists
-+ LevelChunkSection belowChunkSection = null;
-+ boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunk.getSections()[chunkSectionIndex - 1]) == EMPTY_SECTION;
-+
-+ for (int z = 0; z < 16; z++) {
-+ for (int x = 0; x < 16; x++) {
-+ current[z][x] = true;
-+ next[z][x] = skipFirstLayer || isTransparent(belowChunkSection, x, 15, z);
-+ }
-+ }
-+
-+ // Abuse the obfuscateLayer method to read the blocks of the first layer of the current chunk section
-+ bitStorageWriter.setBits(0);
-+ obfuscateLayer(-1, bitStorageReader, bitStorageWriter, solidTemp, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, emptyNearbyChunkSections, random);
-+ }
-+
-+ bitStorageWriter.setBits(chunkPacketInfoAntiXray.getBits(chunkSectionIndex));
-+ nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex];
-+ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex];
-+ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex];
-+ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex];
-+
-+ // Obfuscate all layers of the current chunk section except the upper one
-+ for (int y = 0; y < 15; y++) {
-+ boolean[][] temp = current;
-+ current = next;
-+ next = nextNext;
-+ nextNext = temp;
-+ random.nextLayer();
-+ obfuscateLayer(y, bitStorageReader, bitStorageWriter, solidTemp, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, nearbyChunkSections, random);
-+ }
-+
-+ // Check if the chunk section above doesn't need obfuscation
-+ if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex + 1) == null) {
-+ // If so, obfuscate the upper layer of the current chunk section by reading blocks of the first layer from the chunk section above if it exists
-+ LevelChunkSection aboveChunkSection;
-+
-+ if (chunkSectionIndex != chunk.getSectionsCount() - 1 && (aboveChunkSection = chunk.getSections()[chunkSectionIndex + 1]) != EMPTY_SECTION) {
-+ boolean[][] temp = current;
-+ current = next;
-+ next = nextNext;
-+ nextNext = temp;
-+
-+ for (int z = 0; z < 16; z++) {
-+ for (int x = 0; x < 16; x++) {
-+ if (isTransparent(aboveChunkSection, x, 0, z)) {
-+ current[z][x] = true;
-+ }
-+ }
-+ }
-+
-+ // There is nothing to read anymore
-+ bitStorageReader.setBits(0);
-+ solid[0] = true;
-+ random.nextLayer();
-+ obfuscateLayer(15, bitStorageReader, bitStorageWriter, solid, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, nearbyChunkSections, random);
-+ }
-+ } else {
-+ // If not, initialize the reader and other stuff for the chunk section above to obfuscate the upper layer of the current chunk section
-+ bitStorageReader.setBits(chunkPacketInfoAntiXray.getBits(chunkSectionIndex + 1));
-+ bitStorageReader.setIndex(chunkPacketInfoAntiXray.getIndex(chunkSectionIndex + 1));
-+ solidTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex + 1), solid, solidGlobal);
-+ obfuscateTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal);
-+ boolean[][] temp = current;
-+ current = next;
-+ next = nextNext;
-+ nextNext = temp;
-+ random.nextLayer();
-+ obfuscateLayer(15, bitStorageReader, bitStorageWriter, solidTemp, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, nearbyChunkSections, random);
-+ }
-+
-+ bitStorageWriter.flush();
-+ }
-+ }
-+
-+ chunkPacketInfoAntiXray.getChunkPacket().setReady(true);
-+ }
-+
-+ private void obfuscateLayer(int y, BitStorageReader bitStorageReader, BitStorageWriter bitStorageWriter, boolean[] solid, boolean[] obfuscate, int[] presetBlockStateBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, LevelChunkSection[] nearbyChunkSections, IntSupplier random) {
-+ // First block of first line
-+ int bits = bitStorageReader.read();
-+
-+ if (nextNext[0][0] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[0][1] = true;
-+ next[1][0] = true;
-+ } else {
-+ if (current[0][0] || isTransparent(nearbyChunkSections[2], 0, y, 15) || isTransparent(nearbyChunkSections[0], 15, y, 0)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[0][0] = true;
-+ }
-+
-+ // First line
-+ for (int x = 1; x < 15; x++) {
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[0][x] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[0][x - 1] = true;
-+ next[0][x + 1] = true;
-+ next[1][x] = true;
-+ } else {
-+ if (current[0][x] || isTransparent(nearbyChunkSections[2], x, y, 15)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[0][x] = true;
-+ }
-+ }
-+
-+ // Last block of first line
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[0][15] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[0][14] = true;
-+ next[1][15] = true;
-+ } else {
-+ if (current[0][15] || isTransparent(nearbyChunkSections[2], 15, y, 15) || isTransparent(nearbyChunkSections[1], 0, y, 0)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[0][15] = true;
-+ }
-+
-+ // All inner lines
-+ for (int z = 1; z < 15; z++) {
-+ // First block
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[z][0] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[z][1] = true;
-+ next[z - 1][0] = true;
-+ next[z + 1][0] = true;
-+ } else {
-+ if (current[z][0] || isTransparent(nearbyChunkSections[0], 15, y, z)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[z][0] = true;
-+ }
-+
-+ // All inner blocks
-+ for (int x = 1; x < 15; x++) {
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[z][x] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[z][x - 1] = true;
-+ next[z][x + 1] = true;
-+ next[z - 1][x] = true;
-+ next[z + 1][x] = true;
-+ } else {
-+ if (current[z][x]) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[z][x] = true;
-+ }
-+ }
-+
-+ // Last block
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[z][15] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[z][14] = true;
-+ next[z - 1][15] = true;
-+ next[z + 1][15] = true;
-+ } else {
-+ if (current[z][15] || isTransparent(nearbyChunkSections[1], 0, y, z)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[z][15] = true;
-+ }
-+ }
-+
-+ // First block of last line
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[15][0] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[15][1] = true;
-+ next[14][0] = true;
-+ } else {
-+ if (current[15][0] || isTransparent(nearbyChunkSections[3], 0, y, 0) || isTransparent(nearbyChunkSections[0], 15, y, 15)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[15][0] = true;
-+ }
-+
-+ // Last line
-+ for (int x = 1; x < 15; x++) {
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[15][x] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[15][x - 1] = true;
-+ next[15][x + 1] = true;
-+ next[14][x] = true;
-+ } else {
-+ if (current[15][x] || isTransparent(nearbyChunkSections[3], x, y, 0)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[15][x] = true;
-+ }
-+ }
-+
-+ // Last block of last line
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[15][15] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[15][14] = true;
-+ next[14][15] = true;
-+ } else {
-+ if (current[15][15] || isTransparent(nearbyChunkSections[3], 15, y, 0) || isTransparent(nearbyChunkSections[1], 0, y, 15)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[15][15] = true;
-+ }
-+ }
-+
-+ private boolean isTransparent(LevelChunkSection chunkSection, int x, int y, int z) {
-+ if (chunkSection == EMPTY_SECTION) {
-+ return true;
-+ }
-+
-+ try {
-+ return !solidGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(chunkSection.getBlockState(x, y, z))];
-+ } catch (MissingPaletteEntryException e) {
-+ // Race condition / visibility issue / no happens-before relationship
-+ // We don't care and treat the block as transparent
-+ // Internal implementation details of PalettedContainer, LinearPalette, HashMapPalette, CrudeIncrementalIntIdentityHashBiMap, ... guarantee us that no (other) exceptions will occur
-+ return true;
-+ }
-+ }
-+
-+ private boolean[] readPalette(Palette<BlockState> palette, boolean[] temp, boolean[] global) {
-+ if (palette instanceof GlobalPalette) {
-+ return global;
-+ }
-+
-+ try {
-+ for (int i = 0; i < palette.getSize(); i++) {
-+ temp[i] = global[GLOBAL_BLOCKSTATE_PALETTE.idFor(palette.valueFor(i))];
-+ }
-+ } catch (MissingPaletteEntryException e) {
-+ // Race condition / visibility issue / no happens-before relationship
-+ // We don't care because we at least see the state as it was when the chunk packet was created
-+ // Internal implementation details of PalettedContainer, LinearPalette, HashMapPalette, CrudeIncrementalIntIdentityHashBiMap, ... guarantee us that no (other) exceptions will occur until we have all the data that we need here
-+ // Since all palettes have a fixed initial maximum size and there is no internal restructuring and no values are removed from palettes, we are also guaranteed to see the data
-+ }
-+
-+ return temp;
-+ }
-+
-+ @Override
-+ public void onBlockChange(Level level, BlockPos blockPos, BlockState newBlockState, BlockState oldBlockState, int flags, int maxUpdateDepth) {
-+ if (oldBlockState != null && solidGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(oldBlockState)] && !solidGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(newBlockState)] && blockPos.getY() <= maxBlockHeightUpdatePosition) {
-+ updateNearbyBlocks(level, blockPos);
-+ }
-+ }
-+
-+ @Override
-+ public void onPlayerLeftClickBlock(ServerPlayerGameMode serverPlayerGameMode, BlockPos blockPos, ServerboundPlayerActionPacket.Action action, Direction direction, int worldHeight, int sequence) {
-+ if (blockPos.getY() <= maxBlockHeightUpdatePosition) {
-+ updateNearbyBlocks(serverPlayerGameMode.level, blockPos);
-+ }
-+ }
-+
-+ private void updateNearbyBlocks(Level level, BlockPos blockPos) {
-+ if (updateRadius >= 2) {
-+ BlockPos temp = blockPos.west();
-+ updateBlock(level, temp);
-+ updateBlock(level, temp.west());
-+ updateBlock(level, temp.below());
-+ updateBlock(level, temp.above());
-+ updateBlock(level, temp.north());
-+ updateBlock(level, temp.south());
-+ updateBlock(level, temp = blockPos.east());
-+ updateBlock(level, temp.east());
-+ updateBlock(level, temp.below());
-+ updateBlock(level, temp.above());
-+ updateBlock(level, temp.north());
-+ updateBlock(level, temp.south());
-+ updateBlock(level, temp = blockPos.below());
-+ updateBlock(level, temp.below());
-+ updateBlock(level, temp.north());
-+ updateBlock(level, temp.south());
-+ updateBlock(level, temp = blockPos.above());
-+ updateBlock(level, temp.above());
-+ updateBlock(level, temp.north());
-+ updateBlock(level, temp.south());
-+ updateBlock(level, temp = blockPos.north());
-+ updateBlock(level, temp.north());
-+ updateBlock(level, temp = blockPos.south());
-+ updateBlock(level, temp.south());
-+ } else if (updateRadius == 1) {
-+ updateBlock(level, blockPos.west());
-+ updateBlock(level, blockPos.east());
-+ updateBlock(level, blockPos.below());
-+ updateBlock(level, blockPos.above());
-+ updateBlock(level, blockPos.north());
-+ updateBlock(level, blockPos.south());
-+ } else {
-+ // Do nothing if updateRadius <= 0 (test mode)
-+ }
-+ }
-+
-+ private void updateBlock(Level level, BlockPos blockPos) {
-+ BlockState blockState = level.getBlockStateIfLoaded(blockPos);
-+
-+ if (blockState != null && obfuscateGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)]) {
-+ ((ServerLevel) level).getChunkSource().blockChanged(blockPos);
-+ }
-+ }
-+
-+ @FunctionalInterface
-+ private interface LayeredIntSupplier extends IntSupplier {
-+ default void nextLayer() {
-+
-+ }
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..d98a3f5c54c67a673eb7dc456dd039cd78f9c34d
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java
-@@ -0,0 +1,80 @@
-+package com.destroystokyo.paper.antixray;
-+
-+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
-+import net.minecraft.world.level.chunk.LevelChunk;
-+import net.minecraft.world.level.chunk.Palette;
-+
-+public class ChunkPacketInfo<T> {
-+
-+ private final ClientboundLevelChunkWithLightPacket chunkPacket;
-+ private final LevelChunk chunk;
-+ private final int[] bits;
-+ private final Object[] palettes;
-+ private final int[] indexes;
-+ private final Object[][] presetValues;
-+ private byte[] buffer;
-+
-+ public ChunkPacketInfo(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk) {
-+ this.chunkPacket = chunkPacket;
-+ this.chunk = chunk;
-+ int sections = chunk.getSectionsCount();
-+ bits = new int[sections];
-+ palettes = new Object[sections];
-+ indexes = new int[sections];
-+ presetValues = new Object[sections][];
-+ }
-+
-+ public ClientboundLevelChunkWithLightPacket getChunkPacket() {
-+ return chunkPacket;
-+ }
-+
-+ public LevelChunk getChunk() {
-+ return chunk;
-+ }
-+
-+ public byte[] getBuffer() {
-+ return buffer;
-+ }
-+
-+ public void setBuffer(byte[] buffer) {
-+ this.buffer = buffer;
-+ }
-+
-+ public int getBits(int chunkSectionIndex) {
-+ return bits[chunkSectionIndex];
-+ }
-+
-+ public void setBits(int chunkSectionIndex, int bits) {
-+ this.bits[chunkSectionIndex] = bits;
-+ }
-+
-+ @SuppressWarnings("unchecked")
-+ public Palette<T> getPalette(int chunkSectionIndex) {
-+ return (Palette<T>) palettes[chunkSectionIndex];
-+ }
-+
-+ public void setPalette(int chunkSectionIndex, Palette<T> palette) {
-+ palettes[chunkSectionIndex] = palette;
-+ }
-+
-+ public int getIndex(int chunkSectionIndex) {
-+ return indexes[chunkSectionIndex];
-+ }
-+
-+ public void setIndex(int chunkSectionIndex, int index) {
-+ indexes[chunkSectionIndex] = index;
-+ }
-+
-+ @SuppressWarnings("unchecked")
-+ public T[] getPresetValues(int chunkSectionIndex) {
-+ return (T[]) presetValues[chunkSectionIndex];
-+ }
-+
-+ public void setPresetValues(int chunkSectionIndex, T[] presetValues) {
-+ this.presetValues[chunkSectionIndex] = presetValues;
-+ }
-+
-+ public boolean isWritten(int chunkSectionIndex) {
-+ return bits[chunkSectionIndex] != 0;
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..80a2dfb266ae1221680a7b24fee2f7e2a8330b7d
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java
-@@ -0,0 +1,29 @@
-+package com.destroystokyo.paper.antixray;
-+
-+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
-+import net.minecraft.world.level.block.state.BlockState;
-+import net.minecraft.world.level.chunk.LevelChunk;
-+
-+public final class ChunkPacketInfoAntiXray extends ChunkPacketInfo<BlockState> implements Runnable {
-+
-+ private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray;
-+ private LevelChunk[] nearbyChunks;
-+
-+ public ChunkPacketInfoAntiXray(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) {
-+ super(chunkPacket, chunk);
-+ this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray;
-+ }
-+
-+ public LevelChunk[] getNearbyChunks() {
-+ return nearbyChunks;
-+ }
-+
-+ public void setNearbyChunks(LevelChunk... nearbyChunks) {
-+ this.nearbyChunks = nearbyChunks;
-+ }
-+
-+ @Override
-+ public void run() {
-+ chunkPacketBlockControllerAntiXray.obfuscate(this);
-+ }
-+}
-diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
-index ccc8c32c27c19cb0f0b581ca6693cfa737cb1de1..3c1cad5c2b34047cec44734ba4e8348cabec6f80 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
-@@ -70,8 +70,10 @@ public record ClientboundChunksBiomesPacket(List<ClientboundChunksBiomesPacket.C
- }
-
- public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) {
-+ int chunkSectionIndex = 0; // Paper - Anti-Xray
- for (LevelChunkSection levelChunkSection : chunk.getSections()) {
-- levelChunkSection.getBiomes().write(buf);
-+ levelChunkSection.getBiomes().write(buf, null, chunkSectionIndex); // Paper - Anti-Xray
-+ chunkSectionIndex++; // Paper - Anti-Xray
- }
- }
-
-diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-index cf8fd671490863e126c059157e1ca234e6509d9f..1e75cd33c32f0e2923681da64b9b73b279933c1b 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-@@ -28,7 +28,10 @@ public class ClientboundLevelChunkPacketData {
- private final byte[] buffer;
- private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
-
-- public ClientboundLevelChunkPacketData(LevelChunk chunk) {
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public ClientboundLevelChunkPacketData(LevelChunk chunk) { this(chunk, null); }
-+ public ClientboundLevelChunkPacketData(LevelChunk chunk, com.destroystokyo.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
-+ // Paper end
- this.heightmaps = new CompoundTag();
-
- for (Entry<Heightmap.Types, Heightmap> entry : chunk.getHeightmaps()) {
-@@ -38,7 +41,14 @@ public class ClientboundLevelChunkPacketData {
- }
-
- this.buffer = new byte[calculateChunkSize(chunk)];
-- extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk);
-+
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ if (chunkPacketInfo != null) {
-+ chunkPacketInfo.setBuffer(this.buffer);
-+ }
-+
-+ extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk, chunkPacketInfo);
-+ // Paper end
- this.blockEntitiesData = Lists.newArrayList();
-
- for (Entry<BlockPos, BlockEntity> entry2 : chunk.getBlockEntities().entrySet()) {
-@@ -85,9 +95,15 @@ public class ClientboundLevelChunkPacketData {
- return byteBuf;
- }
-
-- public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) {
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) { ClientboundLevelChunkPacketData.extractChunkData(buf, chunk, null); }
-+ public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk, com.destroystokyo.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
-+ int chunkSectionIndex = 0;
-+
- for (LevelChunkSection levelChunkSection : chunk.getSections()) {
-- levelChunkSection.write(buf);
-+ levelChunkSection.write(buf, chunkPacketInfo, chunkSectionIndex);
-+ chunkSectionIndex++;
-+ // Paper end
- }
- }
-
-diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
-index 183b2191fa1c1b27adedf39593e1b5a223fb1279..8ead66c134688b11dca15f6509147e726f182e6a 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
-@@ -18,13 +18,30 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
- private final int z;
- private final ClientboundLevelChunkPacketData chunkData;
- private final ClientboundLightUpdatePacketData lightData;
-+ // Paper start - Async-Anti-Xray - Ready flag for the connection
-+ private volatile boolean ready;
-
-- public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightProvider, @Nullable BitSet skyBits, @Nullable BitSet blockBits) {
-+ @Override
-+ public boolean isReady() {
-+ return this.ready;
-+ }
-+
-+ public void setReady(boolean ready) {
-+ this.ready = ready;
-+ }
-+ // Paper end
-+
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightProvider, @Nullable BitSet skyBits, @Nullable BitSet blockBits) { this(chunk, lightProvider, skyBits, blockBits, true); }
-+ public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightProvider, @Nullable BitSet skyBits, @Nullable BitSet blockBits, boolean modifyBlocks) {
- ChunkPos chunkPos = chunk.getPos();
- this.x = chunkPos.x;
- this.z = chunkPos.z;
-- this.chunkData = new ClientboundLevelChunkPacketData(chunk);
-+ com.destroystokyo.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo = modifyBlocks ? chunk.getLevel().chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null;
-+ this.chunkData = new ClientboundLevelChunkPacketData(chunk, chunkPacketInfo);
-+ // Paper end
- this.lightData = new ClientboundLightUpdatePacketData(chunkPos, lightProvider, skyBits, blockBits);
-+ chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
- }
-
- private ClientboundLevelChunkWithLightPacket(RegistryFriendlyByteBuf buf) {
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index b5cfd8994c201b2025597f334b3bc33ff4011409..3968227d413d4acf68292ce9fba7e3a8f5ee0694 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -502,7 +502,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf.
- // Holder holder = worlddimension.type(); // CraftBukkit - decompile error
-
- // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error
-- super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> minecraftserver.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig, minecraftserver.registryAccess(), iworlddataserver.getGameRules()))); // Paper - create paper world configs
-+ super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> minecraftserver.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig, minecraftserver.registryAccess(), iworlddataserver.getGameRules())), executor); // Paper - create paper world configs; Async-Anti-Xray: Pass executor
- this.pvpMode = minecraftserver.isPvpAllowed();
- this.convertable = convertable_conversionsession;
- this.uuid = WorldUUID.getUUID(convertable_conversionsession.levelDirectory.path().toFile());
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-index 415d9802ae4dd75b44055b8faf19672fa50c585f..e9dcdb1e09e84a9b451034ff4bdfa6eae2dd1c04 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-@@ -50,7 +50,7 @@ import org.bukkit.event.player.PlayerInteractEvent;
- public class ServerPlayerGameMode {
-
- private static final Logger LOGGER = LogUtils.getLogger();
-- protected ServerLevel level;
-+ public ServerLevel level; // Paper - Anti-Xray - protected -> public
- protected final ServerPlayer player;
- private GameType gameModeForPlayer;
- @Nullable
-@@ -334,6 +334,8 @@ public class ServerPlayerGameMode {
- }
-
- }
-+
-+ this.level.chunkPacketBlockController.onPlayerLeftClickBlock(this, pos, action, direction, worldHeight, sequence); // Paper - Anti-Xray
- }
-
- public void destroyAndAck(BlockPos pos, int sequence, String reason) {
-diff --git a/src/main/java/net/minecraft/server/network/PlayerChunkSender.java b/src/main/java/net/minecraft/server/network/PlayerChunkSender.java
-index 32634e45ac8433648e49e47e20081e15ad41ff15..dafa2cf7d3c49fc5bdcd68d2a952812774a1dfe4 100644
---- a/src/main/java/net/minecraft/server/network/PlayerChunkSender.java
-+++ b/src/main/java/net/minecraft/server/network/PlayerChunkSender.java
-@@ -79,7 +79,10 @@ public class PlayerChunkSender {
- }
-
- public static void sendChunk(ServerGamePacketListenerImpl handler, ServerLevel world, LevelChunk chunk) { // Paper - public
-- handler.send(new ClientboundLevelChunkWithLightPacket(chunk, world.getLightEngine(), null, null));
-+ // Paper start - Anti-Xray
-+ final boolean shouldModify = world.chunkPacketBlockController.shouldModify(handler.player, chunk);
-+ handler.send(new ClientboundLevelChunkWithLightPacket(chunk, world.getLightEngine(), null, null, shouldModify));
-+ // Paper end - Anti-Xray
- // Paper start - PlayerChunkLoadEvent
- if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) {
- new io.papermc.paper.event.packet.PlayerChunkLoadEvent(new org.bukkit.craftbukkit.CraftChunk(chunk), handler.getPlayer().getBukkitEntity()).callEvent();
-diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 883fa950284da3baf2de7a289dfcd7c0a2dfd3f9..dc4af41c0c26689dffb4bfd299a27f2ee6800cb5 100644
---- a/src/main/java/net/minecraft/server/players/PlayerList.java
-+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
-@@ -414,7 +414,7 @@ public abstract class PlayerList {
- .getHolderOrThrow(net.minecraft.world.level.biome.Biomes.PLAINS);
- player.connection.send(new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket(
- new net.minecraft.world.level.chunk.EmptyLevelChunk(worldserver1, player.chunkPosition(), plains),
-- worldserver1.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null)
-+ worldserver1.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null, true)
- );
- }
- // Paper end - Send empty chunk
-diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 23af11c22713ac3e005fd89b4f3b3873bf74e751..9f2fad2f4b1e4e7eda645d53eb76ed04fc0b3451 100644
---- a/src/main/java/net/minecraft/world/level/Level.java
-+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -171,6 +171,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
- }
- // Paper end - add paper world config
-
-+ public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
- public final co.aikar.timings.WorldTimingsHandler timings; // Paper
- public static BlockPos lastPhysicsProblem; // Spigot
- private org.spigotmc.TickLimiter entityLimiter;
-@@ -257,7 +258,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
- }
- // Paper end - rewrite chunk system
-
-- protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, RegistryAccess iregistrycustom, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function<org.spigotmc.SpigotWorldConfig, io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator) { // Paper - create paper world config
-+ protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, RegistryAccess iregistrycustom, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function<org.spigotmc.SpigotWorldConfig, io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - create paper world config & Anti-Xray
- this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
- this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
- this.generator = gen;
-@@ -340,6 +341,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
- this.entityLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.entityMaxTickTime);
- this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime);
- this.entityLookup = new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEntityLookup(this); // Paper - rewrite chunk system
-+ this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : com.destroystokyo.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray
- }
-
- // Paper start - Cancel hit for vanished players
-@@ -619,6 +621,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
- // CraftBukkit end
-
- BlockState iblockdata1 = chunk.setBlockState(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag
-+ this.chunkPacketBlockController.onBlockChange(this, pos, state, iblockdata1, flags, maxUpdateDepth); // Paper - Anti-Xray
-
- if (iblockdata1 == null) {
- // CraftBukkit start - remove blockstate if failed (or the same)
-diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-index 2822a9b010e6d45f9562950a94f1942784db9784..97f8ef86a0e398b7e4aa3445d5e413addbe3a9e3 100644
---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-@@ -155,7 +155,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
- }
- }
-
-- ChunkAccess.replaceMissingSections(biomeRegistry, this.sections);
-+ this.replaceMissingSections(biomeRegistry, this.sections); // Paper - Anti-Xray - make it a non-static method
- // CraftBukkit start
- this.biomeRegistry = biomeRegistry;
- // Paper start - rewrite chunk system
-@@ -168,10 +168,10 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
- public final Registry<Biome> biomeRegistry;
- // CraftBukkit end
-
-- private static void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sectionArray) {
-+ private void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sectionArray) { // Paper - Anti-Xray - make it a non-static method
- for (int i = 0; i < sectionArray.length; ++i) {
- if (sectionArray[i] == null) {
-- sectionArray[i] = new LevelChunkSection(biomeRegistry);
-+ sectionArray[i] = new LevelChunkSection(biomeRegistry, this.levelHeightAccessor instanceof net.minecraft.world.level.Level ? (net.minecraft.world.level.Level) this.levelHeightAccessor : null, this.chunkPos, this.levelHeightAccessor.getSectionYFromSectionIndex(i)); // Paper - Anti-Xray - Add parameters
- }
- }
-
-diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index 3be8f35ece18d4cffe8b23ecfeeff359e0b36e3e..d869607853ad27032df072c1f1d74a74e4911baf 100644
---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -91,7 +91,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
- }
-
- public LevelChunk(Level world, ChunkPos pos, UpgradeData upgradeData, LevelChunkTicks<Block> blockTickScheduler, LevelChunkTicks<Fluid> fluidTickScheduler, long inhabitedTime, @Nullable LevelChunkSection[] sectionArrayInitializer, @Nullable LevelChunk.PostLoadProcessor entityLoader, @Nullable BlendingData blendingData) {
-- super(pos, upgradeData, world, world.registryAccess().registryOrThrow(Registries.BIOME), inhabitedTime, sectionArrayInitializer, blendingData);
-+ super(pos, upgradeData, world, net.minecraft.server.MinecraftServer.getServer().registryAccess().registryOrThrow(Registries.BIOME), inhabitedTime, sectionArrayInitializer, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry
- this.tickersInLevel = Maps.newHashMap();
- this.level = (ServerLevel) world; // CraftBukkit - type
- this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap();
-diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
-index 90d1c3e23e753c29660f7d993b3c90ac022941c3..f2e11bff414b521295bde721e01ae2166a6a3fd6 100644
---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
-@@ -33,9 +33,12 @@ public class LevelChunkSection {
- 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) {
-@@ -172,10 +175,13 @@ public class LevelChunkSection {
- 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() {
-diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-index fa9df6ebcd90d4e9e5836a37212b1f60665783b1..926c81a25180d508d662eb3fa35f771636164694 100644
---- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-@@ -28,6 +28,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- private static final int MIN_PALETTE_BITS = 0;
- private final PaletteResize<T> dummyPaletteResize = (newSize, added) -> 0;
- public final IdMap<T> registry;
-+ private final T @org.jetbrains.annotations.Nullable [] presetValues; // Paper - Anti-Xray - Add preset values
- private volatile PalettedContainer.Data<T> data;
- private final PalettedContainer.Strategy strategy;
- // private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused
-@@ -40,14 +41,19 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- // this.threadingDetector.checkAndUnlock(); // Paper - disable this
- }
-
-- public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) {
-- PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = PalettedContainer::unpack;
-+ // Paper start - Anti-Xray - Add preset values
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) { return PalettedContainer.codecRW(idList, entryCodec, paletteProvider, defaultValue, null); }
-+ public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues) {
-+ PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = (idListx, paletteProviderx, serialized) -> {
-+ return unpack(idListx, paletteProviderx, serialized, defaultValue, presetValues);
-+ };
-+ // Paper end
- return codec(idList, entryCodec, paletteProvider, defaultValue, unpacker);
- }
-
- public static <T> Codec<PalettedContainerRO<T>> codecRO(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) {
- PalettedContainerRO.Unpacker<T, PalettedContainerRO<T>> unpacker = (idListx, paletteProviderx, serialized) -> unpack(
-- idListx, paletteProviderx, serialized
-+ idListx, paletteProviderx, serialized, defaultValue, null // Paper - Anti-Xray - Add preset values
- )
- .map(result -> (PalettedContainerRO<T>)result);
- return codec(idList, entryCodec, paletteProvider, defaultValue, unpacker);
-@@ -71,25 +77,58 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- );
- }
-
-+ // Paper start - Anti-Xray - Add preset values
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Configuration<T> dataProvider, BitStorage storage, List<T> paletteEntries) { this(idList, paletteProvider, dataProvider, storage, paletteEntries, null, null); }
- public PalettedContainer(
- IdMap<T> idList,
- PalettedContainer.Strategy paletteProvider,
- PalettedContainer.Configuration<T> dataProvider,
- BitStorage storage,
-- List<T> paletteEntries
-+ List<T> paletteEntries, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues
- ) {
-+ this.presetValues = presetValues;
- this.registry = idList;
- this.strategy = paletteProvider;
- this.data = new PalettedContainer.Data<>(dataProvider, storage, dataProvider.factory().create(dataProvider.bits(), idList, this, paletteEntries));
-+
-+ if (presetValues != null && (dataProvider.factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY ? this.data.palette.valueFor(0) != defaultValue : dataProvider.factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY)) {
-+ // In 1.18 Mojang unfortunately removed code that already handled possible resize operations on read from disk for us
-+ // We readd this here but in a smarter way than it was before
-+ int maxSize = 1 << dataProvider.bits();
-+
-+ for (T presetValue : presetValues) {
-+ if (this.data.palette.getSize() >= maxSize) {
-+ java.util.Set<T> allValues = new java.util.HashSet<>(paletteEntries);
-+ allValues.addAll(Arrays.asList(presetValues));
-+ int newBits = Mth.ceillog2(allValues.size());
-+
-+ if (newBits > dataProvider.bits()) {
-+ this.onResize(newBits, null);
-+ }
-+
-+ break;
-+ }
-+
-+ this.data.palette.idFor(presetValue);
-+ }
-+ }
-+ // Paper end
- }
-
-- private PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Data<T> data) {
-+ // Paper start - Anti-Xray - Add preset values
-+ private PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Data<T> data, T @org.jetbrains.annotations.Nullable [] presetValues) {
-+ this.presetValues = presetValues;
-+ // Paper end
- this.registry = idList;
- this.strategy = paletteProvider;
- this.data = data;
- }
-
-- public PalettedContainer(IdMap<T> idList, T object, PalettedContainer.Strategy paletteProvider) {
-+ // Paper start - Anti-Xray - Add preset values
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public PalettedContainer(IdMap<T> idList, T object, PalettedContainer.Strategy paletteProvider) { this(idList, object, paletteProvider, null); }
-+ public PalettedContainer(IdMap<T> idList, T object, PalettedContainer.Strategy paletteProvider, T @org.jetbrains.annotations.Nullable [] presetValues) {
-+ this.presetValues = presetValues;
-+ // Paper end
- this.strategy = paletteProvider;
- this.registry = idList;
- this.data = this.createOrReuseData(null, 0);
-@@ -106,11 +145,33 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- @Override
- public synchronized int onResize(int newBits, T object) { // Paper - synchronize
- PalettedContainer.Data<T> data = this.data;
-+
-+ // Paper start - Anti-Xray - Add preset values
-+ if (this.presetValues != null && object != null && data.configuration().factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY) {
-+ int duplicates = 0;
-+ List<T> presetValues = Arrays.asList(this.presetValues);
-+ duplicates += presetValues.contains(object) ? 1 : 0;
-+ duplicates += presetValues.contains(data.palette.valueFor(0)) ? 1 : 0;
-+ newBits = Mth.ceillog2((1 << this.strategy.calculateBitsForSerialization(this.registry, 1 << newBits)) + presetValues.size() - duplicates);
-+ }
-+
- PalettedContainer.Data<T> data2 = this.createOrReuseData(data, newBits);
- data2.copyFrom(data.palette, data.storage);
- this.data = data2;
-- return data2.palette.idFor(object);
-+ this.addPresetValues();
-+ return object == null ? -1 : data2.palette.idFor(object);
-+ // Paper end
-+ }
-+
-+ // Paper start - Anti-Xray - Add preset values
-+ private void addPresetValues() {
-+ if (this.presetValues != null && this.data.configuration().factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY) {
-+ for (T presetValue : this.presetValues) {
-+ this.data.palette.idFor(presetValue);
-+ }
-+ }
- }
-+ // Paper end
-
- public T getAndSet(int x, int y, int z, T value) {
- this.acquire();
-@@ -177,24 +238,33 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- data.palette.read(buf);
- buf.readLongArray(data.storage.getRaw());
- this.data = data;
-+ this.addPresetValues(); // Paper - Anti-Xray - Add preset values (inefficient, but this isn't used by the server)
- } finally {
- this.release();
- }
- }
-
-+ // Paper start - Anti-Xray; Add chunk packet info
-+ @Override
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public void write(FriendlyByteBuf buf) { this.write(buf, null, 0); }
- @Override
-- public synchronized void write(FriendlyByteBuf buf) { // Paper - synchronize
-+ public synchronized void write(FriendlyByteBuf buf, @Nullable com.destroystokyo.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) { // Paper - Synchronize
- this.acquire();
-
- try {
-- this.data.write(buf);
-+ this.data.write(buf, chunkPacketInfo, chunkSectionIndex);
-+
-+ if (chunkPacketInfo != null) {
-+ chunkPacketInfo.setPresetValues(chunkSectionIndex, this.presetValues);
-+ }
-+ // Paper end
- } finally {
- this.release();
- }
- }
-
- private static <T> DataResult<PalettedContainer<T>> unpack(
-- IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainerRO.PackedData<T> serialized
-+ IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainerRO.PackedData<T> serialized, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues // Paper - Anti-Xray - Add preset values
- ) {
- List<T> list = serialized.paletteEntries();
- int i = paletteProvider.size();
-@@ -227,7 +297,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- }
- }
-
-- return DataResult.success(new PalettedContainer<>(idList, paletteProvider, configuration, bitStorage, list));
-+ return DataResult.success(new PalettedContainer<>(idList, paletteProvider, configuration, bitStorage, list, defaultValue, presetValues)); // Paper - Anti-Xray - Add preset values
- }
-
- @Override
-@@ -284,12 +354,12 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- }
-
- public PalettedContainer<T> copy() {
-- return new PalettedContainer<>(this.registry, this.strategy, this.data.copy());
-+ return new PalettedContainer<>(this.registry, this.strategy, this.data.copy(), this.presetValues); // Paper - Anti-Xray - Add preset values
- }
-
- @Override
- public PalettedContainer<T> recreate() {
-- return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy);
-+ return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy, this.presetValues); // Paper - Anti-Xray - Add preset values
- }
-
- @Override
-@@ -328,9 +398,18 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- return 1 + this.palette.getSerializedSize() + VarInt.getByteSize(this.storage.getRaw().length) + this.storage.getRaw().length * 8;
- }
-
-- public void write(FriendlyByteBuf buf) {
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ public void write(FriendlyByteBuf buf, @Nullable com.destroystokyo.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) {
- buf.writeByte(this.storage.getBits());
- this.palette.write(buf);
-+
-+ if (chunkPacketInfo != null) {
-+ chunkPacketInfo.setBits(chunkSectionIndex, this.configuration.bits());
-+ chunkPacketInfo.setPalette(chunkSectionIndex, this.palette);
-+ chunkPacketInfo.setIndex(chunkSectionIndex, buf.writerIndex() + VarInt.getByteSize(this.storage.getRaw().length));
-+ }
-+ // Paper end
-+
- buf.writeLongArray(this.storage.getRaw());
- }
-
-diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainerRO.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainerRO.java
-index 9a2bf744abd8916d492e901be889223591bac3fd..1dd415c96d17eff8e7555c33d3c52e57f2559fa5 100644
---- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainerRO.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainerRO.java
-@@ -14,7 +14,10 @@ public interface PalettedContainerRO<T> {
-
- void getAll(Consumer<T> action);
-
-- void write(FriendlyByteBuf buf);
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse void write(FriendlyByteBuf buf);
-+ void write(FriendlyByteBuf buf, @javax.annotation.Nullable com.destroystokyo.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex);
-+ // Paper end
-
- int getSerializedSize();
-
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index 977bebe8657abc5cb84ede8276d6781cde20e847..6d461849da76894244e6212a75da0c6e4fb459c3 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-@@ -73,7 +73,7 @@ import org.slf4j.Logger;
-
- public class ChunkSerializer {
-
-- public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
-+ public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null); // Paper - Anti-Xray - Add preset block states
- private static final Logger LOGGER = LogUtils.getLogger();
- private static final String TAG_UPGRADE_DATA = "UpgradeData";
- private static final String BLOCK_TICKS_TAG = "block_ticks";
-@@ -141,13 +141,17 @@ public class ChunkSerializer {
-
- if (k >= 0 && k < achunksection.length) {
- PalettedContainer datapaletteblock;
-+ // Paper start - Anti-Xray - Add preset block states
-+ BlockState[] presetBlockStates = world.chunkPacketBlockController.getPresetBlockStates(world, chunkPos, b0);
-
- if (nbttagcompound1.contains("block_states", 10)) {
-- datapaletteblock = (PalettedContainer) ChunkSerializer.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, nbttagcompound1.getCompound("block_states")).promotePartial((s) -> {
-+ Codec<PalettedContainer<BlockState>> blockStateCodec = presetBlockStates == null ? ChunkSerializer.BLOCK_STATE_CODEC : PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), presetBlockStates);
-+ datapaletteblock = blockStateCodec.parse(NbtOps.INSTANCE, nbttagcompound1.getCompound("block_states")).promotePartial((s) -> {
- ChunkSerializer.logErrors(chunkPos, b0, s);
- }).getOrThrow(ChunkSerializer.ChunkReadException::new);
- } else {
-- datapaletteblock = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
-+ datapaletteblock = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, presetBlockStates);
-+ // Paper end
- }
-
- PalettedContainer object; // CraftBukkit - read/write
-@@ -157,7 +161,7 @@ public class ChunkSerializer {
- ChunkSerializer.logErrors(chunkPos, b0, s);
- }).getOrThrow(ChunkSerializer.ChunkReadException::new);
- } else {
-- object = new PalettedContainer<>(iregistry.asHolderIdMap(), iregistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
-+ object = new PalettedContainer<>(iregistry.asHolderIdMap(), iregistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes
- }
-
- LevelChunkSection chunksection = new LevelChunkSection(datapaletteblock, (PalettedContainer) object); // CraftBukkit - read/write
-@@ -339,7 +343,7 @@ public class ChunkSerializer {
-
- // CraftBukkit start - read/write
- private static Codec<PalettedContainer<Holder<Biome>>> makeBiomeCodecRW(Registry<Biome> iregistry) {
-- return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getHolderOrThrow(Biomes.PLAINS));
-+ return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getHolderOrThrow(Biomes.PLAINS), null); // Paper - Anti-Xray - Add preset biomes
- }
- // CraftBukkit end
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
-index cce2fed2d4e9d6147ea1854321012c6950eb05cc..2d5c88b80c983eb067ef366c3d9344826fbb0938 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
-@@ -56,7 +56,7 @@ public class CraftChunk implements Chunk {
- private final ServerLevel worldServer;
- private final int x;
- private final int z;
-- private static final PalettedContainer<net.minecraft.world.level.block.state.BlockState> emptyBlockIDs = new PalettedContainer<>(net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
-+ private static final PalettedContainer<net.minecraft.world.level.block.state.BlockState> emptyBlockIDs = new PalettedContainer<>(net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); // Paper - Anti-Xray - Add preset block states
- private static final byte[] FULL_LIGHT = new byte[2048];
- private static final byte[] EMPTY_LIGHT = new byte[2048];
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 05e44a1448f30ceb8cecba2bed76f51aac5543f9..caf6ff33b42472d30f28629470e12889f50490cc 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -2677,7 +2677,7 @@ public final class CraftServer implements Server {
- public ChunkGenerator.ChunkData createChunkData(World world) {
- Preconditions.checkArgument(world != null, "World cannot be null");
- ServerLevel handle = ((CraftWorld) world).getHandle();
-- return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().registryOrThrow(Registries.BIOME));
-+ return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().registryOrThrow(Registries.BIOME), world); // Paper - Anti-Xray - Add parameters
- }
-
- @Override
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index cdc704364cf339084537d089e654f6078f8be783..fe9e8d1d4ae1f7a4e8f4cf5688004fc969422b3c 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -465,11 +465,16 @@ public class CraftWorld extends CraftRegionAccessor implements World {
- List<ServerPlayer> playersInRange = playerChunk.playerProvider.getPlayers(playerChunk.getPos(), false);
- if (playersInRange.isEmpty()) return true; // Paper - chunk system
-
-- ClientboundLevelChunkWithLightPacket refreshPacket = new ClientboundLevelChunkWithLightPacket(chunk, this.world.getLightEngine(), null, null);
-+ // Paper start - Anti-Xray bypass
-+ final Map<Object, ClientboundLevelChunkWithLightPacket> refreshPackets = new HashMap<>();
- for (ServerPlayer player : playersInRange) {
- if (player.connection == null) continue;
-
-- player.connection.send(refreshPacket);
-+ Boolean shouldModify = chunk.getLevel().chunkPacketBlockController.shouldModify(player, chunk);
-+ player.connection.send(refreshPackets.computeIfAbsent(shouldModify, s -> { // Use connection to prevent creating firing event
-+ return new ClientboundLevelChunkWithLightPacket(chunk, this.world.getLightEngine(), null, null, (Boolean) s);
-+ }));
-+ // Paper end - Anti-Xray bypass
- }
- // Paper - chunk system
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
-index e7f7a246e9c03e676dadfee59de87b8b2ac55ba3..03eb35d5c67f125c44cf46595c93d124ac7892b8 100644
---- a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
-+++ b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
-@@ -27,8 +27,13 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData {
- private final Registry<net.minecraft.world.level.biome.Biome> biomes;
- private Set<BlockPos> tiles;
- private final Set<BlockPos> lights = new HashSet<>();
-+ // Paper start - Anti-Xray - Add parameters
-+ private final org.bukkit.World world;
-
-- public OldCraftChunkData(int minHeight, int maxHeight, Registry<net.minecraft.world.level.biome.Biome> biomes) {
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public OldCraftChunkData(int minHeight, int maxHeight, Registry<net.minecraft.world.level.biome.Biome> biomes) { this(minHeight, maxHeight, biomes, null); }
-+ public OldCraftChunkData(int minHeight, int maxHeight, Registry<net.minecraft.world.level.biome.Biome> biomes, org.bukkit.World world) {
-+ this.world = world;
-+ // Paper end
- this.minHeight = minHeight;
- this.maxHeight = maxHeight;
- this.biomes = biomes;
-@@ -176,7 +181,7 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData {
- int offset = (y - this.minHeight) >> 4;
- LevelChunkSection section = this.sections[offset];
- if (create && section == null) {
-- this.sections[offset] = section = new LevelChunkSection(this.biomes);
-+ this.sections[offset] = section = new LevelChunkSection(this.biomes, this.world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) this.world).getHandle() : null, null, offset + (this.minHeight >> 4)); // Paper - Anti-Xray - Add parameters
- }
- return section;
- }