diff --git a/feature-patches/1044-Anti-Xray.patch b/feature-patches/1044-Anti-Xray.patch index f002f23549..6b16ff833e 100644 --- a/feature-patches/1044-Anti-Xray.patch +++ b/feature-patches/1044-Anti-Xray.patch @@ -4,1007 +4,11 @@ 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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java -@@ -0,0 +0,0 @@ -+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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java -@@ -0,0 +0,0 @@ -+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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java -@@ -0,0 +0,0 @@ -+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 getChunkPacketInfo(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk) { -+ return null; -+ } -+ -+ public void modifyBlocks(ClientboundLevelChunkWithLightPacket chunkPacket, ChunkPacketInfo 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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -0,0 +0,0 @@ -+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 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 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 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) constructor doesn't specify that the insertion order is the predictable iteration order of the specified Collection, although it is in the implementation -+ Set presetBlockStateSet = new LinkedHashSet<>(); -+ // Therefore addAll(Collection) 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().lookupOrThrow(Registries.BIOME).getOrThrow(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 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 presetBlockStateBits = ThreadLocal.withInitial(() -> new int[getPresetBlockStatesFullLength()]); -+ private static final ThreadLocal SOLID = ThreadLocal.withInitial(() -> new boolean[Block.BLOCK_STATE_REGISTRY.size()]); -+ private static final ThreadLocal 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 CURRENT = ThreadLocal.withInitial(() -> new boolean[16][16]); -+ private static final ThreadLocal NEXT = ThreadLocal.withInitial(() -> new boolean[16][16]); -+ private static final ThreadLocal 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.getMinSectionY(), 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.getMinSectionY() < 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 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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java -@@ -0,0 +0,0 @@ -+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 { -+ -+ 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 getPalette(int chunkSectionIndex) { -+ return (Palette) palettes[chunkSectionIndex]; -+ } -+ -+ public void setPalette(int chunkSectionIndex, Palette 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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java -@@ -0,0 +0,0 @@ -+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 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/io/papermc/paper/FeatureHooks.java b/src/main/java/io/papermc/paper/FeatureHooks.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/io/papermc/paper/FeatureHooks.java -+++ b/src/main/java/io/papermc/paper/FeatureHooks.java -@@ -0,0 +0,0 @@ import it.unimi.dsi.fastutil.longs.LongSets; +diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java +index aad9d9687dffb872a12ba0ba39d674895b7474e7..764daee7cd619c56314bcea9a4c35702afcb262d 100644 +--- a/io/papermc/paper/FeatureHooks.java ++++ b/io/papermc/paper/FeatureHooks.java +@@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.longs.LongSets; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectSet; import it.unimi.dsi.fastutil.objects.ObjectSets; @@ -1012,7 +16,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 import java.util.List; import java.util.Map; import java.util.Set; -@@ -0,0 +0,0 @@ public final class FeatureHooks { +@@ -35,20 +36,25 @@ public final class FeatureHooks { } public static LevelChunkSection createSection(final Registry biomeRegistry, final Level level, final ChunkPos chunkPos, final int chunkSection) { @@ -1042,84 +46,90 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } public static Set getSentChunkKeys(final ServerPlayer player) { -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java -@@ -0,0 +0,0 @@ public record ClientboundChunksBiomesPacket(List 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 chunkPacketInfo) { ++ @Deprecated @io.papermc.paper.annotation.DoNotUse + public ClientboundLevelChunkPacketData(LevelChunk levelChunk) { ++ this(levelChunk, null); ++ } ++ public ClientboundLevelChunkPacketData(LevelChunk levelChunk, io.papermc.paper.antixray.ChunkPacketInfo chunkPacketInfo) { + // Paper end this.heightmaps = new CompoundTag(); - for (Entry entry : chunk.getHeightmaps()) { -@@ -0,0 +0,0 @@ public class ClientboundLevelChunkPacketData { + for (Entry entry : levelChunk.getHeightmaps()) { +@@ -38,7 +44,11 @@ public class ClientboundLevelChunkPacketData { } - this.buffer = new byte[calculateChunkSize(chunk)]; -- extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk); -+ + this.buffer = new byte[calculateChunkSize(levelChunk)]; +- extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk); + // Paper start - Anti-Xray - Add chunk packet info + if (chunkPacketInfo != null) { + chunkPacketInfo.setBuffer(this.buffer); + } -+ -+ extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk, chunkPacketInfo); -+ // Paper end ++ extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk, chunkPacketInfo); this.blockEntitiesData = Lists.newArrayList(); - for (Entry entry2 : chunk.getBlockEntities().entrySet()) { -@@ -0,0 +0,0 @@ public class ClientboundLevelChunkPacketData { + for (Entry entryx : levelChunk.getBlockEntities().entrySet()) { +@@ -85,9 +95,17 @@ 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 chunkPacketInfo) { ++ @Deprecated @io.papermc.paper.annotation.DoNotUse + public static void extractChunkData(FriendlyByteBuf buffer, LevelChunk chunk) { ++ ClientboundLevelChunkPacketData.extractChunkData(buffer, chunk, null); ++ } ++ public static void extractChunkData(FriendlyByteBuf buffer, LevelChunk chunk, io.papermc.paper.antixray.ChunkPacketInfo chunkPacketInfo) { + int chunkSectionIndex = 0; -+ for (LevelChunkSection levelChunkSection : chunk.getSections()) { -- levelChunkSection.write(buf); -+ levelChunkSection.write(buf, chunkPacketInfo, chunkSectionIndex); +- levelChunkSection.write(buffer); ++ levelChunkSection.write(buffer, chunkPacketInfo, chunkSectionIndex); + chunkSectionIndex++; -+ // Paper end ++ // Paper end - Anti-Xray - Add chunk packet info } } -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -@@ -0,0 +0,0 @@ public class ClientboundLevelChunkWithLightPacket implements Packet 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); ++ io.papermc.paper.antixray.ChunkPacketInfo chunkPacketInfo = modifyBlocks ? chunk.getLevel().chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null; // Paper - Ant-Xray ++ this.chunkData = new ClientboundLevelChunkPacketData(chunk, chunkPacketInfo); // Paper - Anti-Xray + this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight); + 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + private ClientboundLevelChunkWithLightPacket(RegistryFriendlyByteBuf buffer) { +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index c2d9f1e6c68f4945e3b9e5b95326e8180fa7a0fb..bb43dff0d55af335ed54f3a8843633b20b7ee63b 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -348,7 +348,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + org.bukkit.generator.BiomeProvider biomeProvider // CraftBukkit + ) { + // CraftBukkit start +- super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules()))); // Paper - create paper world configs ++ super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor + this.pvpMode = server.isPvpAllowed(); + this.levelStorageAccess = levelStorageAccess; + this.uuid = org.bukkit.craftbukkit.util.WorldUUID.getUUID(levelStorageAccess.levelDirectory.path().toFile()); +diff --git a/net/minecraft/server/level/ServerPlayerGameMode.java b/net/minecraft/server/level/ServerPlayerGameMode.java +index d6a493de667bfe97b722efe40d1530bdb666bb94..bb352ad40da33a9d411836d4fab2664e226a6e38 100644 +--- a/net/minecraft/server/level/ServerPlayerGameMode.java ++++ b/net/minecraft/server/level/ServerPlayerGameMode.java +@@ -28,7 +28,7 @@ import org.slf4j.Logger; - // Add env and gen to constructor, IWorldDataServer -> WorldDataServer - public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List list, boolean flag1, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) { -- super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), 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(), 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -@@ -0,0 +0,0 @@ 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; + private GameType gameModeForPlayer = GameType.DEFAULT_MODE; @Nullable -@@ -0,0 +0,0 @@ public class ServerPlayerGameMode { +@@ -312,6 +312,7 @@ public class ServerPlayerGameMode { + org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDamageAbortEvent(this.player, pos, this.player.getInventory().getSelected()); // CraftBukkit } - } -+ -+ this.level.chunkPacketBlockController.onPlayerLeftClickBlock(this, pos, action, direction, worldHeight, sequence); // Paper - Anti-Xray ++ this.level.chunkPacketBlockController.onPlayerLeftClickBlock(this, pos, action, face, maxBuildHeight, 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/network/PlayerChunkSender.java -+++ b/src/main/java/net/minecraft/server/network/PlayerChunkSender.java -@@ -0,0 +0,0 @@ public class PlayerChunkSender { + public void destroyAndAck(BlockPos pos, int sequence, String message) { +diff --git a/net/minecraft/server/network/PlayerChunkSender.java b/net/minecraft/server/network/PlayerChunkSender.java +index 342bc843c384761e883de861044f4f8930ae8763..14878690a88fd4de3e2c127086607e6c819c636c 100644 +--- a/net/minecraft/server/network/PlayerChunkSender.java ++++ b/net/minecraft/server/network/PlayerChunkSender.java +@@ -78,8 +78,11 @@ public class PlayerChunkSender { } } -- private static void sendChunk(ServerGamePacketListenerImpl handler, ServerLevel world, LevelChunk chunk) { -- handler.send(new ClientboundLevelChunkWithLightPacket(chunk, world.getLightEngine(), null, null)); -+ public static void sendChunk(ServerGamePacketListenerImpl handler, ServerLevel world, LevelChunk chunk) { // Paper - public -+ // Paper start - Anti-Xray -+ final boolean shouldModify = world.chunkPacketBlockController.shouldModify(handler.player, chunk); -+ handler.send(new ClientboundLevelChunkWithLightPacket(chunk, world.getLightEngine(), null, null, shouldModify)); +- private static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) { +- packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null)); ++ // Paper start - Anti-Xray ++ public static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) { ++ final boolean shouldModify = level.chunkPacketBlockController.shouldModify(packetListener.player, chunk); ++ packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -0,0 +0,0 @@ public abstract class PlayerList { + new io.papermc.paper.event.packet.PlayerChunkLoadEvent(new org.bukkit.craftbukkit.CraftChunk(chunk), packetListener.getPlayer().getBukkitEntity()).callEvent(); +diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java +index 2580b249d9a024400e295ca5beab551b3571ceb3..d227714de0fe13544779fae6cf0e9ff6af5469c7 100644 +--- a/net/minecraft/server/players/PlayerList.java ++++ b/net/minecraft/server/players/PlayerList.java +@@ -404,7 +404,7 @@ public abstract class PlayerList { .getOrThrow(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 - Anti-Xray + new net.minecraft.world.level.chunk.EmptyLevelChunk(serverLevel, player.chunkPosition(), plains), +- serverLevel.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null) ++ serverLevel.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null, true) // Paper - Anti-Xray ); } // 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java +index 051a86e8a723aeb86ffa06e3cd5fab102a808cde..2c2eae0c8656055940c6e1457fc77a20bb58dd8e 100644 +--- a/net/minecraft/world/level/Level.java ++++ b/net/minecraft/world/level/Level.java +@@ -168,6 +168,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } // Paper end - add paper world config -+ public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray ++ public final io.papermc.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray public static BlockPos lastPhysicsProblem; // Spigot private org.spigotmc.TickLimiter entityLimiter; private org.spigotmc.TickLimiter tileLimiter; -@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - - public abstract ResourceKey getTypeKey(); - -- protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, 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 paperWorldConfigCreator) { // Paper - create paper world config -+ protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, 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 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 +@@ -214,7 +215,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + org.bukkit.generator.BiomeProvider biomeProvider, // CraftBukkit + org.bukkit.World.Environment env, // CraftBukkit + java.util.function.Function paperWorldConfigCreator // Paper - create paper world config ++ io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator, // Paper - create paper world config ++ java.util.concurrent.Executor executor // Paper - Anti-Xray + ) { + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config - this.generator = gen; -@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -295,6 +297,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { // CraftBukkit end this.entityLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.entityMaxTickTime); this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime); -+ 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 ++ this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new io.papermc.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : io.papermc.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray } // Paper start - Cancel hit for vanished players -@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -495,6 +498,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { // 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 + BlockState blockState = chunkAt.setBlockState(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag ++ this.chunkPacketBlockController.onBlockChange(this, pos, state, blockState, flags, recursionLeft); // Paper - Anti-Xray - if (iblockdata1 == null) { + if (blockState == 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -@@ -0,0 +0,0 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh +diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java +index f68f3f5e8ef39a0dc371e75110227a39791c04c8..254ca42e08148915b96ce77310a46e09de84ef53 100644 +--- a/net/minecraft/world/level/chunk/ChunkAccess.java ++++ b/net/minecraft/world/level/chunk/ChunkAccess.java +@@ -114,14 +114,14 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh } } -- ChunkAccess.replaceMissingSections(biomeRegistry, this.sections); +- replaceMissingSections(biomeRegistry, this.sections); + this.replaceMissingSections(biomeRegistry, this.sections); // Paper - Anti-Xray - make it a non-static method - // CraftBukkit start - this.biomeRegistry = biomeRegistry; + this.biomeRegistry = biomeRegistry; // Craftbukkit } - public final Registry biomeRegistry; - // CraftBukkit end -- private static void replaceMissingSections(Registry biomeRegistry, LevelChunkSection[] sectionArray) { -+ private void replaceMissingSections(Registry 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 +- private static void replaceMissingSections(Registry biomeRegistry, LevelChunkSection[] sections) { ++ private void replaceMissingSections(Registry biomeRegistry, LevelChunkSection[] sections) { // Paper - Anti-Xray - make it a non-static method + for (int i = 0; i < sections.length; i++) { + if (sections[i] == null) { +- sections[i] = new LevelChunkSection(biomeRegistry); ++ sections[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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -0,0 +0,0 @@ public class LevelChunk extends ChunkAccess { } +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index b3cc671f33b2c8c5f3131afffc6ee9d7b83dd3bc..d1d0dc13eecb0e0eb3a7839b570a5fe7f62f3fba 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -109,7 +109,7 @@ public class LevelChunk extends ChunkAccess { + @Nullable LevelChunk.PostLoadProcessor postLoad, + @Nullable BlendingData blendingData + ) { +- super(pos, data, level, level.registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData); ++ super(pos, data, level, net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry + this.level = (net.minecraft.server.level.ServerLevel) level; // CraftBukkit - type + this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>(); - public LevelChunk(Level world, ChunkPos pos, UpgradeData upgradeData, LevelChunkTicks blockTickScheduler, LevelChunkTicks fluidTickScheduler, long inhabitedTime, @Nullable LevelChunkSection[] sectionArrayInitializer, @Nullable LevelChunk.PostLoadProcessor entityLoader, @Nullable BlendingData blendingData) { -- super(pos, upgradeData, world, world.registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sectionArrayInitializer, blendingData); -+ super(pos, upgradeData, world, net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sectionArrayInitializer, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry - this.tickersInLevel = Maps.newHashMap(); - this.unsavedListener = (chunkcoordintpair1) -> { - }; -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -0,0 +0,0 @@ public class LevelChunkSection { +diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java +index baa9f3e2e6e45c250930658e82bad70a3a292b05..fc21c3268c4b4fda2933d71f0913db28e3796653 100644 +--- a/net/minecraft/world/level/chunk/LevelChunkSection.java ++++ b/net/minecraft/world/level/chunk/LevelChunkSection.java +@@ -38,9 +38,15 @@ public class LevelChunkSection { this.recalcBlockCounts(); } -- public LevelChunkSection(Registry biomeRegistry) { ++ // Paper start - Anti-Xray - Add parameters ++ @Deprecated @io.papermc.paper.annotation.DoNotUse + public LevelChunkSection(Registry biomeRegistry) { - this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); - this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); -+ // Paper start - Anti-Xray - Add parameters -+ @Deprecated @io.papermc.paper.annotation.DoNotUse public LevelChunkSection(Registry biomeRegistry) { this(biomeRegistry, null, null, 0); } ++ this(biomeRegistry, null, null, 0); ++ } + public LevelChunkSection(Registry biomeRegistry, net.minecraft.world.level.Level level, net.minecraft.world.level.ChunkPos chunkPos, int chunkSectionY) { -+ // Paper end ++ // Paper end - Anti-Xray + 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.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes } public BlockState getBlockState(int x, int y, int z) { -@@ -0,0 +0,0 @@ public class LevelChunkSection { - this.biomes = datapaletteblock; +@@ -168,10 +174,16 @@ public class LevelChunkSection { + this.biomes = palettedContainer; } -- 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 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 ++ @Deprecated @io.papermc.paper.annotation.DoNotUse + public void write(FriendlyByteBuf buffer) { ++ this.write(buffer, null, 0); ++ } ++ public void write(FriendlyByteBuf buffer, io.papermc.paper.antixray.ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { + buffer.writeShort(this.nonEmptyBlockCount); +- this.states.write(buffer); +- this.biomes.write(buffer); ++ this.states.write(buffer, chunkPacketInfo, chunkSectionIndex); ++ this.biomes.write(buffer, null, chunkSectionIndex); ++ // Paper end - Anti-Xray } 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java -+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize, PalettedContainer +diff --git a/net/minecraft/world/level/chunk/PalettedContainer.java b/net/minecraft/world/level/chunk/PalettedContainer.java +index e8ec28ce3fe13561b45c4654e174776d9d2d7b71..a6028a54c75de068515e95913b21160ab4326985 100644 +--- a/net/minecraft/world/level/chunk/PalettedContainer.java ++++ b/net/minecraft/world/level/chunk/PalettedContainer.java +@@ -28,6 +28,7 @@ public class PalettedContainer implements PaletteResize, PalettedContainer private static final int MIN_PALETTE_BITS = 0; - private final PaletteResize dummyPaletteResize = (newSize, added) -> 0; + private final PaletteResize dummyPaletteResize = (bits, objectAdded) -> 0; public final IdMap registry; + private final T @org.jetbrains.annotations.Nullable [] presetValues; // Paper - Anti-Xray - Add preset values private volatile PalettedContainer.Data data; private final PalettedContainer.Strategy strategy; - // private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize, PalettedContainer - // this.threadingDetector.checkAndUnlock(); // Paper - disable this + //private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused +@@ -40,13 +41,21 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + // this.threadingDetector.checkAndUnlock(); // Paper - disable this - use proper synchronization } -- public static Codec> codecRW(IdMap idList, Codec entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) { -- PalettedContainerRO.Unpacker> unpacker = PalettedContainer::unpack; + // Paper start - Anti-Xray - Add preset values -+ @Deprecated @io.papermc.paper.annotation.DoNotUse public static Codec> codecRW(IdMap idList, Codec entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) { return PalettedContainer.codecRW(idList, entryCodec, paletteProvider, defaultValue, null); } -+ public static Codec> codecRW(IdMap idList, Codec entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues) { ++ @Deprecated @io.papermc.paper.annotation.DoNotUse + public static Codec> codecRW(IdMap registry, Codec codec, PalettedContainer.Strategy strategy, T value) { +- PalettedContainerRO.Unpacker> unpacker = PalettedContainer::unpack; ++ return PalettedContainer.codecRW(registry, codec, strategy, value, null); ++ } ++ public static Codec> codecRW(IdMap registry, Codec codec, PalettedContainer.Strategy strategy, T value, T @org.jetbrains.annotations.Nullable [] presetValues) { + PalettedContainerRO.Unpacker> unpacker = (idListx, paletteProviderx, serialized) -> { -+ return unpack(idListx, paletteProviderx, serialized, defaultValue, presetValues); ++ return unpack(idListx, paletteProviderx, serialized, value, presetValues); + }; + // Paper end - return codec(idList, entryCodec, paletteProvider, defaultValue, unpacker); + return codec(registry, codec, strategy, value, unpacker); } - public static Codec> codecRO(IdMap idList, Codec entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) { - PalettedContainerRO.Unpacker> unpacker = (idListx, paletteProviderx, serialized) -> unpack( -- idListx, paletteProviderx, serialized -+ idListx, paletteProviderx, serialized, defaultValue, null // Paper - Anti-Xray - Add preset values - ) - .map(result -> (PalettedContainerRO)result); - return codec(idList, entryCodec, paletteProvider, defaultValue, unpacker); -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + public static Codec> codecRO(IdMap registry, Codec codec, PalettedContainer.Strategy strategy, T value) { +- PalettedContainerRO.Unpacker> unpacker = (registry1, strategy1, packedData) -> unpack(registry1, strategy1, packedData) ++ PalettedContainerRO.Unpacker> unpacker = (registry1, strategy1, packedData) -> unpack(registry1, strategy1, packedData, value, null) // Paper - Anti-Xray - Add preset values + .map(container -> (PalettedContainerRO)container); + return codec(registry, codec, strategy, value, unpacker); + } +@@ -66,27 +75,66 @@ public class PalettedContainer implements PaletteResize, PalettedContainer ); } + // Paper start - Anti-Xray - Add preset values -+ @Deprecated @io.papermc.paper.annotation.DoNotUse public PalettedContainer(IdMap idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Configuration dataProvider, BitStorage storage, List paletteEntries) { this(idList, paletteProvider, dataProvider, storage, paletteEntries, null, null); } ++ @Deprecated @io.papermc.paper.annotation.DoNotUse ++ public PalettedContainer(IdMap registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration configuration, BitStorage storage, List values) { ++ this(registry, strategy, configuration, storage, values, null, null); ++ } public PalettedContainer( - IdMap idList, - PalettedContainer.Strategy paletteProvider, - PalettedContainer.Configuration dataProvider, - BitStorage storage, -- List paletteEntries -+ List paletteEntries, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues +- IdMap registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration configuration, BitStorage storage, List values ++ IdMap registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration configuration, BitStorage storage, List values, 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)) { + this.registry = registry; + this.strategy = strategy; + this.data = new PalettedContainer.Data<>(configuration, storage, configuration.factory().create(configuration.bits(), registry, this, values)); ++ if (presetValues != null && (configuration.factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY ? this.data.palette.valueFor(0) != defaultValue : configuration.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(); ++ int maxSize = 1 << configuration.bits(); + + for (T presetValue : presetValues) { + if (this.data.palette.getSize() >= maxSize) { -+ java.util.Set allValues = new java.util.HashSet<>(paletteEntries); ++ java.util.Set allValues = new java.util.HashSet<>(values); + allValues.addAll(Arrays.asList(presetValues)); + int newBits = Mth.ceillog2(allValues.size()); + -+ if (newBits > dataProvider.bits()) { ++ if (newBits > configuration.bits()) { + this.onResize(newBits, null); + } + @@ -1401,57 +412,56 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper end } -- private PalettedContainer(IdMap idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Data data) { +- private PalettedContainer(IdMap registry, PalettedContainer.Strategy strategy, PalettedContainer.Data data) { + // Paper start - Anti-Xray - Add preset values -+ private PalettedContainer(IdMap idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Data data, T @org.jetbrains.annotations.Nullable [] presetValues) { ++ private PalettedContainer(IdMap registry, PalettedContainer.Strategy strategy, PalettedContainer.Data data, T @org.jetbrains.annotations.Nullable [] presetValues) { + this.presetValues = presetValues; -+ // Paper end - this.registry = idList; - this.strategy = paletteProvider; ++ // Paper end - Anti-Xray + this.registry = registry; + this.strategy = strategy; this.data = data; } -- private PalettedContainer(PalettedContainer container) { -+ private PalettedContainer(PalettedContainer container, T @org.jetbrains.annotations.Nullable [] presetValues) { // Paper - Anti-Xray - Add preset values +- private PalettedContainer(PalettedContainer other) { ++ private PalettedContainer(PalettedContainer other, T @org.jetbrains.annotations.Nullable [] presetValues) { // Paper - Anti-Xray - Add preset values + this.presetValues = presetValues; // Paper - Anti-Xray - Add preset values - this.registry = container.registry; - this.strategy = container.strategy; - this.data = container.data.copy(this); + this.registry = other.registry; + this.strategy = other.strategy; + this.data = other.data.copy(this); } -- public PalettedContainer(IdMap idList, T object, PalettedContainer.Strategy paletteProvider) { + // Paper start - Anti-Xray - Add preset values -+ @Deprecated @io.papermc.paper.annotation.DoNotUse public PalettedContainer(IdMap idList, T object, PalettedContainer.Strategy paletteProvider) { this(idList, object, paletteProvider, null); } -+ public PalettedContainer(IdMap idList, T object, PalettedContainer.Strategy paletteProvider, T @org.jetbrains.annotations.Nullable [] presetValues) { ++ @Deprecated @io.papermc.paper.annotation.DoNotUse + public PalettedContainer(IdMap registry, T palette, PalettedContainer.Strategy strategy) { ++ this(registry, palette, strategy, null); ++ } ++ public PalettedContainer(IdMap registry, T palette, PalettedContainer.Strategy strategy, T @org.jetbrains.annotations.Nullable [] presetValues) { + this.presetValues = presetValues; -+ // Paper end - this.strategy = paletteProvider; - this.registry = idList; ++ // Paper end - Anti-Xray + this.strategy = strategy; + this.registry = registry; this.data = this.createOrReuseData(null, 0); -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize, PalettedContainer +@@ -101,11 +149,30 @@ public class PalettedContainer implements PaletteResize, PalettedContainer @Override - public synchronized int onResize(int newBits, T object) { // Paper - synchronize + public synchronized int onResize(int bits, T objectAdded) { // Paper - synchronize PalettedContainer.Data 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) { ++ if (this.presetValues != null && objectAdded != null && data.configuration().factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY) { + int duplicates = 0; + List presetValues = Arrays.asList(this.presetValues); -+ duplicates += presetValues.contains(object) ? 1 : 0; ++ duplicates += presetValues.contains(objectAdded) ? 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); ++ bits = Mth.ceillog2((1 << this.strategy.calculateBitsForSerialization(this.registry, 1 << bits)) + presetValues.size() - duplicates); + } -+ - PalettedContainer.Data data2 = this.createOrReuseData(data, newBits); - data2.copyFrom(data.palette, data.storage); - this.data = data2; -- return data2.palette.idFor(object); ++ // Paper end - Anti-Xray + PalettedContainer.Data data1 = this.createOrReuseData(data, bits); + data1.copyFrom(data.palette, data.storage); + this.data = data1; +- return data1.palette.idFor(objectAdded); ++ // Paper start - Anti-Xray + this.addPresetValues(); -+ return object == null ? -1 : data2.palette.idFor(object); -+ // Paper end ++ return objectAdded == null ? -1 : data1.palette.idFor(objectAdded); + } -+ -+ // 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) { @@ -1459,13 +469,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + } } -+ // Paper end ++ // Paper end - Anti-Xray - public T getAndSet(int x, int y, int z, T value) { + public synchronized T getAndSet(int x, int y, int z, T state) { // Paper - synchronize this.acquire(); -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize, PalettedContainer - data.palette.read(buf); - buf.readLongArray(data.storage.getRaw()); +@@ -172,24 +239,35 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + data.palette.read(buffer); + buffer.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 { @@ -1474,42 +484,44 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } + // 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 chunkPacketInfo, int chunkSectionIndex) { // Paper - Synchronize +- public synchronized void write(FriendlyByteBuf buffer) { // Paper - synchronize ++ @Deprecated @io.papermc.paper.annotation.DoNotUse ++ public void write(FriendlyByteBuf buffer) { ++ this.write(buffer, null, 0); ++ } ++ @Override ++ public synchronized void write(FriendlyByteBuf buffer, @Nullable io.papermc.paper.antixray.ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { // Paper - Synchronize this.acquire(); try { -- this.data.write(buf); -+ this.data.write(buf, chunkPacketInfo, chunkSectionIndex); -+ +- this.data.write(buffer); ++ this.data.write(buffer, chunkPacketInfo, chunkSectionIndex); + if (chunkPacketInfo != null) { + chunkPacketInfo.setPresetValues(chunkSectionIndex, this.presetValues); + } -+ // Paper end ++ // Paper end - Anti-Xray } finally { this.release(); } } private static DataResult> unpack( -- IdMap idList, PalettedContainer.Strategy paletteProvider, PalettedContainerRO.PackedData serialized -+ IdMap idList, PalettedContainer.Strategy paletteProvider, PalettedContainerRO.PackedData serialized, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues // Paper - Anti-Xray - Add preset values +- IdMap registry, PalettedContainer.Strategy strategy, PalettedContainerRO.PackedData packedData ++ IdMap registry, PalettedContainer.Strategy strategy, PalettedContainerRO.PackedData packedData, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues // Paper - Anti-Xray - Add preset values ) { - List list = serialized.paletteEntries(); - int i = paletteProvider.size(); -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + List list = packedData.paletteEntries(); + int size = strategy.size(); +@@ -222,7 +300,7 @@ public class PalettedContainer implements PaletteResize, 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 +- return DataResult.success(new PalettedContainer<>(registry, strategy, configuration, bitStorage, list)); ++ return DataResult.success(new PalettedContainer<>(registry, strategy, configuration, bitStorage, list, defaultValue, presetValues)); // Paper - Anti-Xray - Add preset values } @Override -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize, PalettedContainer +@@ -280,12 +358,12 @@ public class PalettedContainer implements PaletteResize, PalettedContainer @Override public PalettedContainer copy() { @@ -1524,100 +536,96 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } @Override -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize, PalettedContainer +@@ -324,9 +402,16 @@ public class PalettedContainer implements PaletteResize, PalettedContainer return 1 + this.palette.getSerializedSize() + VarInt.getByteSize(this.storage.getRaw().length) + this.storage.getRaw().length * 8; } -- public void write(FriendlyByteBuf buf) { +- public void write(FriendlyByteBuf buffer) { + // Paper start - Anti-Xray - Add chunk packet info -+ public void write(FriendlyByteBuf buf, @Nullable com.destroystokyo.paper.antixray.ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { - buf.writeByte(this.storage.getBits()); - this.palette.write(buf); -+ ++ public void write(FriendlyByteBuf buffer, @Nullable io.papermc.paper.antixray.ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { + buffer.writeByte(this.storage.getBits()); + this.palette.write(buffer); + 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)); ++ chunkPacketInfo.setIndex(chunkSectionIndex, buffer.writerIndex() + VarInt.getByteSize(this.storage.getRaw().length)); + } + // Paper end -+ - buf.writeLongArray(this.storage.getRaw()); + buffer.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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainerRO.java -+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainerRO.java -@@ -0,0 +0,0 @@ public interface PalettedContainerRO { +diff --git a/net/minecraft/world/level/chunk/PalettedContainerRO.java b/net/minecraft/world/level/chunk/PalettedContainerRO.java +index bfbb1a2bb4abbb369a24f2f01439e9ea3e16794b..8d6ed8be4d93f7d4e6ea80c351020d88ee98aa4d 100644 +--- a/net/minecraft/world/level/chunk/PalettedContainerRO.java ++++ b/net/minecraft/world/level/chunk/PalettedContainerRO.java +@@ -14,7 +14,10 @@ public interface PalettedContainerRO { - void getAll(Consumer action); + void getAll(Consumer consumer); -- void write(FriendlyByteBuf buf); +- void write(FriendlyByteBuf buffer); + // 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 chunkPacketInfo, int chunkSectionIndex); ++ @Deprecated @io.papermc.paper.annotation.DoNotUse void write(FriendlyByteBuf buffer); ++ void write(FriendlyByteBuf buffer, @javax.annotation.Nullable io.papermc.paper.antixray.ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex); + // Paper end int getSerializedSize(); -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -@@ -0,0 +0,0 @@ import org.slf4j.Logger; - // CraftBukkit - persistentDataContainer - public record SerializableChunkData(Registry biomeRegistry, ChunkPos chunkPos, int minSectionY, long lastUpdateTime, long inhabitedTime, ChunkStatus chunkStatus, @Nullable BlendingData.Packed blendingData, @Nullable BelowZeroRetrogen belowZeroRetrogen, UpgradeData upgradeData, @Nullable long[] carvingMask, Map heightmaps, ChunkAccess.PackedTicks packedTicks, ShortList[] postProcessingSections, boolean lightCorrect, List sectionData, List entities, List blockEntities, CompoundTag structureData, @Nullable Tag persistentDataContainer) { - -- public static final Codec> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState()); -+ public static final Codec> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null); // Paper start - Anti-Xray +diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java +index 37437a86d74291fab1de9495008aafb15dfadce0..cf6e2053d81f7b0f8c8e58b9c0fad3285ebc047d 100644 +--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java ++++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java +@@ -94,7 +94,7 @@ public record SerializableChunkData( + , @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer + ) { + public static final Codec> BLOCK_STATE_CODEC = PalettedContainer.codecRW( +- Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState() ++ Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null // Paper - Anti-Xray + ); private static final Logger LOGGER = LogUtils.getLogger(); private static final String TAG_UPGRADE_DATA = "UpgradeData"; - private static final String BLOCK_TICKS_TAG = "block_ticks"; -@@ -0,0 +0,0 @@ public record SerializableChunkData(Registry biomeRegistry, ChunkPos chun +@@ -128,6 +128,7 @@ public record SerializableChunkData( @Nullable - public static SerializableChunkData parse(LevelHeightAccessor world, RegistryAccess registryManager, CompoundTag nbt) { -+ net.minecraft.server.level.ServerLevel serverLevel = (net.minecraft.server.level.ServerLevel) world; // Paper - Anti-Xray This is is seemingly only called from ChunkMap, where, we have a server level. We'll fight this later if needed. - if (!nbt.contains("Status", 8)) { + public static SerializableChunkData parse(LevelHeightAccessor levelHeightAccessor, RegistryAccess registries, CompoundTag tag) { ++ net.minecraft.server.level.ServerLevel serverLevel = (net.minecraft.server.level.ServerLevel) levelHeightAccessor; // Paper - Anti-Xray This is is seemingly only called from ChunkMap, where, we have a server level. We'll fight this later if needed. + if (!tag.contains("Status", 8)) { return null; } else { -@@ -0,0 +0,0 @@ public record SerializableChunkData(Registry biomeRegistry, ChunkPos chun - Codec>> codec = makeBiomeCodecRW(iregistry); // CraftBukkit - read/write +@@ -212,18 +213,21 @@ public record SerializableChunkData( + Codec>> codec = makeBiomeCodecRW(registry); // CraftBukkit - read/write - for (int i1 = 0; i1 < nbttaglist2.size(); ++i1) { -- CompoundTag nbttagcompound3 = nbttaglist2.getCompound(i1); -+ CompoundTag nbttagcompound3 = nbttaglist2.getCompound(i1); final CompoundTag sectionData = nbttagcompound3; // Paper - Anti-Xray - OBFHELPER - byte b0 = nbttagcompound3.getByte("Y"); - LevelChunkSection chunksection; - - if (b0 >= world.getMinSectionY() && b0 <= world.getMaxSectionY()) { - PalettedContainer datapaletteblock; + for (int i2 = 0; i2 < list7.size(); i2++) { +- CompoundTag compound2 = list7.getCompound(i2); ++ CompoundTag compound2 = list7.getCompound(i2); final CompoundTag sectionData = compound2; // Paper - Anti-Xray - OBFHELPER + int _byte = compound2.getByte("Y"); + LevelChunkSection levelChunkSection; + if (_byte >= levelHeightAccessor.getMinSectionY() && _byte <= levelHeightAccessor.getMaxSectionY()) { + // Paper start - Anti-Xray - Add preset block states -+ BlockState[] presetBlockStates = serverLevel.chunkPacketBlockController.getPresetBlockStates(serverLevel, chunkcoordintpair, b0); -+ - - if (nbttagcompound3.contains("block_states", 10)) { -- datapaletteblock = (PalettedContainer) SerializableChunkData.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, nbttagcompound3.getCompound("block_states")).promotePartial((s1) -> { ++ BlockState[] presetBlockStates = serverLevel.chunkPacketBlockController.getPresetBlockStates(serverLevel, chunkPos, _byte); + PalettedContainer palettedContainer; + if (compound2.contains("block_states", 10)) { +- palettedContainer = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, compound2.getCompound("block_states")) + Codec> blockStateCodec = presetBlockStates == null ? BLOCK_STATE_CODEC : PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), presetBlockStates); // Paper - Anti-Xray -+ datapaletteblock = blockStateCodec.parse(NbtOps.INSTANCE, sectionData.getCompound("block_states")).promotePartial((s1) -> { // Paper - Anti-Xray - logErrors(chunkcoordintpair, b0, s1); - }).getOrThrow(SerializableChunkData.ChunkReadException::new); ++ palettedContainer = blockStateCodec.parse(NbtOps.INSTANCE, sectionData.getCompound("block_states")) // Paper - Anti-Xray + .promotePartial(string -> logErrors(chunkPos, _byte, string)) + .getOrThrow(SerializableChunkData.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 - Anti-Xray + palettedContainer = new PalettedContainer<>( +- Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES ++ Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, presetBlockStates // Paper - Anti-Xray + ); } - PalettedContainer object; // CraftBukkit - read/write -@@ -0,0 +0,0 @@ public record SerializableChunkData(Registry biomeRegistry, ChunkPos chun - logErrors(chunkcoordintpair, b0, s1); - }).getOrThrow(SerializableChunkData.ChunkReadException::new); +@@ -234,7 +238,7 @@ public record SerializableChunkData( + .getOrThrow(SerializableChunkData.ChunkReadException::new); } else { -- object = new PalettedContainer<>(iregistry.asHolderIdMap(), iregistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); -+ object = new PalettedContainer<>(iregistry.asHolderIdMap(), iregistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes + palettedContainerRo = new PalettedContainer<>( +- registry.asHolderIdMap(), registry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES ++ registry.asHolderIdMap(), registry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null // Paper - Anti-Xray - Add preset biomes + ); } - chunksection = new LevelChunkSection(datapaletteblock, (PalettedContainer) object); // CraftBukkit - read/write -@@ -0,0 +0,0 @@ public record SerializableChunkData(Registry biomeRegistry, ChunkPos chun +@@ -414,7 +418,7 @@ public record SerializableChunkData( // CraftBukkit start - read/write private static Codec>> makeBiomeCodecRW(Registry iregistry) {