diff --git a/patches/removed/1.18/0361-Anti-Xray.patch b/patches/removed/1.18/0361-Anti-Xray.patch deleted file mode 100644 index 648adb1b2..000000000 --- a/patches/removed/1.18/0361-Anti-Xray.patch +++ /dev/null @@ -1,1395 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: stonar96 -Date: Mon, 20 Aug 2018 03:03:58 +0200 -Subject: [PATCH] Anti-Xray - - -diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 7a97a4a395f20680bc3b028586c9a17b84783d99..75f6915e2a2cc7328cf51b953edcc3ee6f9e7fbd 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -@@ -1,10 +1,12 @@ - package com.destroystokyo.paper; - -+import java.util.Arrays; - import java.util.List; - - import it.unimi.dsi.fastutil.objects.Reference2IntMap; - import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; - import net.minecraft.world.entity.MobCategory; -+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode; - import org.bukkit.Bukkit; - import org.bukkit.configuration.file.YamlConfiguration; - import org.spigotmc.SpigotWorldConfig; -@@ -518,5 +520,40 @@ public class PaperWorldConfig { - private void lightQueueSize() { - lightQueueSize = getInt("light-queue-size", lightQueueSize); - } -+ -+ public boolean antiXray; -+ public EngineMode engineMode; -+ public int maxBlockHeight; -+ public int updateRadius; -+ public boolean lavaObscures; -+ public boolean usePermission; -+ public List hiddenBlocks; -+ public List replacementBlocks; -+ private void antiXray() { -+ antiXray = getBoolean("anti-xray.enabled", false); -+ engineMode = EngineMode.getById(getInt("anti-xray.engine-mode", EngineMode.HIDE.getId())); -+ engineMode = engineMode == null ? EngineMode.HIDE : engineMode; -+ maxBlockHeight = getInt("anti-xray.max-block-height", 64); -+ updateRadius = getInt("anti-xray.update-radius", 2); -+ lavaObscures = getBoolean("anti-xray.lava-obscures", false); -+ usePermission = getBoolean("anti-xray.use-permission", false); -+ hiddenBlocks = getList("anti-xray.hidden-blocks", Arrays.asList("copper_ore", "deepslate_copper_ore", "gold_ore", "deepslate_gold_ore", "iron_ore", "deepslate_iron_ore", -+ "coal_ore", "deepslate_coal_ore", "lapis_ore", "deepslate_lapis_ore", "mossy_cobblestone", "obsidian", "chest", "diamond_ore", "deepslate_diamond_ore", -+ "redstone_ore", "deepslate_redstone_ore", "clay", "emerald_ore", "deepslate_emerald_ore", "ender_chest")); -+ replacementBlocks = getList("anti-xray.replacement-blocks", Arrays.asList("stone", "oak_planks", "deepslate")); -+ if (PaperConfig.version < 19) { -+ hiddenBlocks.remove("lit_redstone_ore"); -+ int index = replacementBlocks.indexOf("planks"); -+ if (index != -1) { -+ replacementBlocks.set(index, "oak_planks"); -+ } -+ set("anti-xray.hidden-blocks", hiddenBlocks); -+ set("anti-xray.replacement-blocks", replacementBlocks); -+ } -+ log("Anti-Xray: " + (antiXray ? "enabled" : "disabled") + " / Engine Mode: " + engineMode.getDescription() + " / Up to " + ((maxBlockHeight >> 4) << 4) + " blocks / Update Radius: " + updateRadius); -+ if (antiXray && usePermission) { -+ Bukkit.getLogger().warning("You have enabled permission-based Anti-Xray checking - depending on your permission plugin, this may cause performance issues"); -+ } -+ } - } - -diff --git a/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java b/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e448c26327b5f6189c3c52e698cff66c8f9ad81a ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java -@@ -0,0 +1,51 @@ -+package com.destroystokyo.paper.antixray; -+ -+public final class BitStorageReader { -+ -+ private byte[] buffer; -+ private int bits; -+ private int mask; -+ private int longInBufferIndex; -+ private int bitInLongIndex; -+ private long current; -+ -+ public void setBuffer(byte[] buffer) { -+ this.buffer = buffer; -+ } -+ -+ public void setBits(int bits) { -+ this.bits = bits; -+ mask = (1 << bits) - 1; -+ } -+ -+ public void setIndex(int index) { -+ longInBufferIndex = index; -+ bitInLongIndex = 0; -+ init(); -+ } -+ -+ private void init() { -+ if (buffer.length > longInBufferIndex + 7) { -+ current = ((((long) buffer[longInBufferIndex]) << 56) -+ | (((long) buffer[longInBufferIndex + 1] & 0xff) << 48) -+ | (((long) buffer[longInBufferIndex + 2] & 0xff) << 40) -+ | (((long) buffer[longInBufferIndex + 3] & 0xff) << 32) -+ | (((long) buffer[longInBufferIndex + 4] & 0xff) << 24) -+ | (((long) buffer[longInBufferIndex + 5] & 0xff) << 16) -+ | (((long) buffer[longInBufferIndex + 6] & 0xff) << 8) -+ | (((long) buffer[longInBufferIndex + 7] & 0xff))); -+ } -+ } -+ -+ public int read() { -+ if (bitInLongIndex + bits > 64) { -+ bitInLongIndex = 0; -+ longInBufferIndex += 8; -+ init(); -+ } -+ -+ int value = (int) (current >>> bitInLongIndex) & mask; -+ bitInLongIndex += bits; -+ return value; -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java b/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e4540ea278f2dc871cb6a3cb8897559bfd65e134 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java -@@ -0,0 +1,79 @@ -+package com.destroystokyo.paper.antixray; -+ -+public final class BitStorageWriter { -+ -+ private byte[] buffer; -+ private int bits; -+ private long mask; -+ private int longInBufferIndex; -+ private int bitInLongIndex; -+ private long current; -+ private boolean dirty; -+ -+ public void setBuffer(byte[] buffer) { -+ this.buffer = buffer; -+ } -+ -+ public void setBits(int bits) { -+ this.bits = bits; -+ mask = (1L << bits) - 1; -+ } -+ -+ public void setIndex(int index) { -+ longInBufferIndex = index; -+ bitInLongIndex = 0; -+ init(); -+ } -+ -+ private void init() { -+ if (buffer.length > longInBufferIndex + 7) { -+ current = ((((long) buffer[longInBufferIndex]) << 56) -+ | (((long) buffer[longInBufferIndex + 1] & 0xff) << 48) -+ | (((long) buffer[longInBufferIndex + 2] & 0xff) << 40) -+ | (((long) buffer[longInBufferIndex + 3] & 0xff) << 32) -+ | (((long) buffer[longInBufferIndex + 4] & 0xff) << 24) -+ | (((long) buffer[longInBufferIndex + 5] & 0xff) << 16) -+ | (((long) buffer[longInBufferIndex + 6] & 0xff) << 8) -+ | (((long) buffer[longInBufferIndex + 7] & 0xff))); -+ } -+ -+ dirty = false; -+ } -+ -+ public void flush() { -+ if (dirty && buffer.length > longInBufferIndex + 7) { -+ buffer[longInBufferIndex] = (byte) (current >> 56 & 0xff); -+ buffer[longInBufferIndex + 1] = (byte) (current >> 48 & 0xff); -+ buffer[longInBufferIndex + 2] = (byte) (current >> 40 & 0xff); -+ buffer[longInBufferIndex + 3] = (byte) (current >> 32 & 0xff); -+ buffer[longInBufferIndex + 4] = (byte) (current >> 24 & 0xff); -+ buffer[longInBufferIndex + 5] = (byte) (current >> 16 & 0xff); -+ buffer[longInBufferIndex + 6] = (byte) (current >> 8 & 0xff); -+ buffer[longInBufferIndex + 7] = (byte) (current & 0xff); -+ } -+ } -+ -+ public void write(int value) { -+ if (bitInLongIndex + bits > 64) { -+ flush(); -+ bitInLongIndex = 0; -+ longInBufferIndex += 8; -+ init(); -+ } -+ -+ current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex; -+ dirty = true; -+ bitInLongIndex += bits; -+ } -+ -+ public void skip() { -+ bitInLongIndex += bits; -+ -+ if (bitInLongIndex > 64) { -+ flush(); -+ bitInLongIndex = bits; -+ longInBufferIndex += 8; -+ init(); -+ } -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java -new file mode 100644 -index 0000000000000000000000000000000000000000..280ece653cdda74e9c8fab4e9e5b3a952901cb01 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java -@@ -0,0 +1,46 @@ -+package com.destroystokyo.paper.antixray; -+ -+import net.minecraft.core.BlockPos; -+import net.minecraft.core.Direction; -+import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket; -+import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; -+import net.minecraft.server.level.ServerPlayer; -+import net.minecraft.server.level.ServerPlayerGameMode; -+import net.minecraft.world.level.Level; -+import net.minecraft.world.level.block.state.BlockState; -+import net.minecraft.world.level.chunk.ChunkAccess; -+import net.minecraft.world.level.chunk.LevelChunk; -+import net.minecraft.world.level.chunk.LevelChunkSection; -+ -+public class ChunkPacketBlockController { -+ -+ public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController(); -+ -+ protected ChunkPacketBlockController() { -+ -+ } -+ -+ public BlockState[] getPresetBlockStates(Level level, ChunkAccess chunk, LevelChunkSection chunkSection, boolean initializeBlocks) { -+ return null; -+ } -+ -+ public boolean shouldModify(ServerPlayer player, LevelChunk chunk) { -+ return false; -+ } -+ -+ public ChunkPacketInfo getChunkPacketInfo(ClientboundLevelChunkPacket chunkPacket, LevelChunk chunk) { -+ return null; -+ } -+ -+ public void modifyBlocks(ClientboundLevelChunkPacket 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) { -+ -+ } -+} -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..f9ed88082f1a58a0572acc22761fad4340185aeb ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -0,0 +1,635 @@ -+package com.destroystokyo.paper.antixray; -+ -+import com.destroystokyo.paper.PaperWorldConfig; -+import net.minecraft.core.BlockPos; -+import net.minecraft.core.Direction; -+import net.minecraft.core.Registry; -+import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; -+import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; -+import net.minecraft.resources.ResourceLocation; -+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.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.*; -+import org.bukkit.Bukkit; -+ -+import java.util.*; -+import java.util.concurrent.Executor; -+import java.util.concurrent.ThreadLocalRandom; -+import java.util.function.IntSupplier; -+ -+public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController { -+ -+ 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[] presetBlockStatesNetherrack; -+ private final BlockState[] presetBlockStatesEndStone; -+ private final int[] presetBlockStateBitsGlobal; -+ private final int[] presetBlockStateBitsStoneGlobal; -+ 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 = {LevelChunk.EMPTY_SECTION, LevelChunk.EMPTY_SECTION, LevelChunk.EMPTY_SECTION, LevelChunk.EMPTY_SECTION}; -+ private final int maxBlockHeightUpdatePosition; -+ -+ public ChunkPacketBlockControllerAntiXray(Level level, Executor executor) { -+ this.executor = executor; -+ PaperWorldConfig paperWorldConfig = level.paperConfig; -+ 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()}; -+ presetBlockStatesNetherrack = new BlockState[]{Blocks.NETHERRACK.defaultBlockState()}; -+ presetBlockStatesEndStone = new BlockState[]{Blocks.END_STONE.defaultBlockState()}; -+ presetBlockStateBitsGlobal = null; -+ presetBlockStateBitsStoneGlobal = new int[]{LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.STONE.defaultBlockState())}; -+ presetBlockStateBitsNetherrackGlobal = new int[]{LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.NETHERRACK.defaultBlockState())}; -+ presetBlockStateBitsEndStoneGlobal = new int[]{LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.END_STONE.defaultBlockState())}; -+ } else { -+ toObfuscate = new ArrayList<>(paperWorldConfig.replacementBlocks); -+ List presetBlockStateList = new LinkedList<>(); -+ -+ for (String id : paperWorldConfig.hiddenBlocks) { -+ Block block = Registry.BLOCK.getOptional(new ResourceLocation(id)).orElse(null); -+ -+ if (block != null && !(block instanceof EntityBlock)) { -+ toObfuscate.add(id); -+ 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; -+ presetBlockStatesNetherrack = null; -+ presetBlockStatesEndStone = null; -+ presetBlockStateBitsGlobal = new int[presetBlockStatesFull.length]; -+ -+ for (int i = 0; i < presetBlockStatesFull.length; i++) { -+ presetBlockStateBitsGlobal[i] = LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(presetBlockStatesFull[i]); -+ } -+ -+ presetBlockStateBitsStoneGlobal = null; -+ presetBlockStateBitsNetherrackGlobal = null; -+ presetBlockStateBitsEndStoneGlobal = null; -+ } -+ -+ for (String id : toObfuscate) { -+ Block block = Registry.BLOCK.getOptional(new ResourceLocation(id)).orElse(null); -+ -+ // 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[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)] = true; -+ } -+ } -+ } -+ -+ EmptyLevelChunk emptyChunk = new EmptyLevelChunk(level, new ChunkPos(0, 0)); -+ BlockPos zeroPos = new BlockPos(0, 0, 0); -+ -+ for (int i = 0; i < solidGlobal.length; i++) { -+ BlockState blockState = LevelChunkSection.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 || 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, ChunkAccess chunk, LevelChunkSection chunkSection, boolean initializeBlocks) { -+ // Return the block states to be added to the paletted containers so that they can be used for obfuscation -+ if (chunkSection.bottomBlockY() < maxBlockHeight) { -+ if (engineMode == EngineMode.HIDE) { -+ return switch (level.getWorld().getEnvironment()) { -+ case NETHER -> presetBlockStatesNetherrack; -+ case THE_END -> presetBlockStatesEndStone; -+ default -> 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(ClientboundLevelChunkPacketData 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(ClientboundLevelChunkPacket 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.level; -+ ((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 nextNext = ThreadLocal.withInitial(() -> new boolean[16][16]); -+ -+ public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) { -+ int[] presetBlockStateBits = this.presetBlockStateBits.get(); -+ boolean[] solid = ChunkPacketBlockControllerAntiXray.solid.get(); -+ boolean[] obfuscate = ChunkPacketBlockControllerAntiXray.obfuscate.get(); -+ boolean[][] current = ChunkPacketBlockControllerAntiXray.current.get(); -+ boolean[][] next = ChunkPacketBlockControllerAntiXray.next.get(); -+ boolean[][] nextNext = ChunkPacketBlockControllerAntiXray.nextNext.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.level; -+ int maxChunkSectionIndex = Math.min((maxBlockHeight >> 4) - chunk.getMinSection(), chunk.getSectionsCount() - 1); -+ boolean[] solidTemp = null; -+ boolean[] obfuscateTemp = null; -+ bitStorageReader.setBuffer(chunkPacketInfoAntiXray.getBuffer()); -+ bitStorageWriter.setBuffer(chunkPacketInfoAntiXray.getBuffer()); -+ int numberOfBlocks = presetBlockStateBits.length; -+ // Keep the lambda expressions as simple as possible. They are used very frequently. -+ IntSupplier random = numberOfBlocks == 1 ? (() -> 0) : new IntSupplier() { -+ 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) == LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE) { -+ if (engineMode == EngineMode.HIDE) { -+ presetBlockStateBitsTemp = switch (level.getWorld().getEnvironment()) { -+ case NETHER -> presetBlockStateBitsNetherrackGlobal; -+ case THE_END -> presetBlockStateBitsEndStoneGlobal; -+ default -> 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++) { -+ 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]) == LevelChunk.EMPTY_SECTION; -+ -+ for (int z = 0; z < 16; z++) { -+ for (int x = 0; x < 16; x++) { -+ current[z][x] = true; -+ next[z][x] = skipFirstLayer || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(belowChunkSection.getBlockState(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 ? LevelChunk.EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? LevelChunk.EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? LevelChunk.EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? LevelChunk.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; -+ 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]) != LevelChunk.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 (!solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(aboveChunkSection.getBlockState(x, 0, z))]) { -+ current[z][x] = true; -+ } -+ } -+ } -+ -+ // There is nothing to read anymore -+ bitStorageReader.setBits(0); -+ solid[0] = true; -+ 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; -+ 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] || nearbyChunkSections[2] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[2].getBlockState(0, y, 15))] || nearbyChunkSections[0] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[0].getBlockState(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] || nearbyChunkSections[2] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[2].getBlockState(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] || nearbyChunkSections[2] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[2].getBlockState(15, y, 15))] || nearbyChunkSections[1] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[1].getBlockState(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] || nearbyChunkSections[0] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[0].getBlockState(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] || nearbyChunkSections[1] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[1].getBlockState(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] || nearbyChunkSections[3] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[3].getBlockState(0, y, 0))] || nearbyChunkSections[0] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[0].getBlockState(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] || nearbyChunkSections[3] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[3].getBlockState(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] || nearbyChunkSections[3] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[3].getBlockState(15, y, 0))] || nearbyChunkSections[1] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[1].getBlockState(0, y, 15))]) { -+ bitStorageWriter.skip(); -+ } else { -+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]); -+ } -+ } -+ -+ if (!obfuscate[bits]) { -+ next[15][15] = true; -+ } -+ } -+ -+ private boolean[] readPalette(Palette palette, boolean[] temp, boolean[] global) { -+ if (palette == LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE) { -+ return global; -+ } -+ -+ BlockState blockState; -+ -+ for (int i = 0; (blockState = palette.valueFor(i)) != null; i++) { -+ temp[i] = global[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)]; -+ } -+ -+ return temp; -+ } -+ -+ @Override -+ public void onBlockChange(Level level, BlockPos blockPos, BlockState newBlockState, BlockState oldBlockState, int flags, int maxUpdateDepth) { -+ if (oldBlockState != null && solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(oldBlockState)] && !solidGlobal[LevelChunkSection.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) { -+ 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.getTypeIfLoaded(blockPos); -+ -+ if (blockState != null && obfuscateGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)]) { -+ ((ServerLevel) level).getChunkSource().blockChanged(blockPos); -+ } -+ } -+ -+ public enum EngineMode { -+ -+ HIDE(1, "hide ores"), -+ OBFUSCATE(2, "obfuscate"); -+ -+ private final int id; -+ private final String description; -+ -+ EngineMode(int id, String description) { -+ this.id = id; -+ this.description = description; -+ } -+ -+ public static EngineMode getById(int id) { -+ for (EngineMode engineMode : values()) { -+ if (engineMode.id == id) { -+ return engineMode; -+ } -+ } -+ -+ return null; -+ } -+ -+ public int getId() { -+ return id; -+ } -+ -+ public String getDescription() { -+ return description; -+ } -+ } -+} -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..351ab20ba506cca55ee198f06346bad8b1593d0a ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java -@@ -0,0 +1,80 @@ -+package com.destroystokyo.paper.antixray; -+ -+import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; -+import net.minecraft.world.level.chunk.LevelChunk; -+import net.minecraft.world.level.chunk.Palette; -+ -+public class ChunkPacketInfo { -+ -+ private final ClientboundLevelChunkPacketData 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(ClientboundLevelChunkPacketData chunkPacket, LevelChunk chunk) { -+ this.chunkPacket = chunkPacket; -+ this.chunk = chunk; -+ int sections = chunk.getSectionsCount(); -+ this.bits = new int[sections]; -+ this.palettes = new Object[sections]; -+ this.indexes = new int[sections]; -+ this.presetValues = new Object[sections][]; -+ } -+ -+ public ClientboundLevelChunkPacketData getChunkPacket() { -+ return this.chunkPacket; -+ } -+ -+ public LevelChunk getChunk() { -+ return this.chunk; -+ } -+ -+ public byte[] getBuffer() { -+ return this.buffer; -+ } -+ -+ public void setBuffer(byte[] buffer) { -+ this.buffer = buffer; -+ } -+ -+ public int getBits(int chunkSectionIndex) { -+ return this.bits[chunkSectionIndex]; -+ } -+ -+ public void setBits(int chunkSectionIndex, int bits) { -+ this.bits[chunkSectionIndex] = bits; -+ } -+ -+ @SuppressWarnings("unchecked") -+ public Palette getPalette(int chunkSectionIndex) { -+ return (Palette) this.palettes[chunkSectionIndex]; -+ } -+ -+ public void setPalette(int chunkSectionIndex, Palette palette) { -+ this.palettes[chunkSectionIndex] = palette; -+ } -+ -+ public int getIndex(int chunkSectionIndex) { -+ return this.indexes[chunkSectionIndex]; -+ } -+ -+ public void setIndex(int chunkSectionIndex, int index) { -+ this.indexes[chunkSectionIndex] = index; -+ } -+ -+ @SuppressWarnings("unchecked") -+ public T[] getPresetValues(int chunkSectionIndex) { -+ return (T[]) this.presetValues[chunkSectionIndex]; -+ } -+ -+ public void setPresetValues(int chunkSectionIndex, T[] presetValues) { -+ this.presetValues[chunkSectionIndex] = presetValues; -+ } -+ -+ public boolean isWritten(int chunkSectionIndex) { -+ return this.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..a3ee0cf08076848806912b983d386caadf762acc ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java -@@ -0,0 +1,29 @@ -+package com.destroystokyo.paper.antixray; -+ -+import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; -+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(ClientboundLevelChunkPacketData chunkPacket, LevelChunk chunk, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) { -+ super(chunkPacket, chunk); -+ this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray; -+ } -+ -+ public LevelChunk[] getNearbyChunks() { -+ return this.nearbyChunks; -+ } -+ -+ public void setNearbyChunks(LevelChunk... nearbyChunks) { -+ this.nearbyChunks = nearbyChunks; -+ } -+ -+ @Override -+ public void run() { -+ this.chunkPacketBlockControllerAntiXray.obfuscate(this); -+ } -+} -diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java -index dba11f277f3703e1ee7f5a62f021d319e4ab18fc..a6f693ee925b03aa591720864d6b493dfe89e4e8 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java -@@ -33,7 +33,24 @@ public class ClientboundLevelChunkPacketData { - } - // Paper end - -- public ClientboundLevelChunkPacketData(LevelChunk chunk) { -+ // Paper start - Async-Anti-Xray - Ready flag for the connection -+ private volatile boolean ready; -+ -+ @Override -+ public boolean isReady() { -+ return this.ready; -+ } -+ -+ public void setReady(boolean ready) { -+ this.ready = ready; -+ } -+ // Paper end -+ -+ // Paper start - Anti-Xray - Add chunk packet info -+ @Deprecated public ClientboundLevelChunkPacketData(LevelChunk chunk) { this(chunk, true); } // Notice for updates: Please make sure this constructor isn't used anywhere -+ public ClientboundLevelChunkPacketData(LevelChunk chunk, boolean modifyBlocks) { -+ com.destroystokyo.paper.antixray.ChunkPacketInfo chunkPacketInfo = modifyBlocks ? chunk.level.chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null; -+ // Paper end - this.heightmaps = new CompoundTag(); - - for(Entry entry : chunk.getHeightmaps()) { -@@ -43,7 +60,13 @@ public class ClientboundLevelChunkPacketData { - } - - this.buffer = new byte[calculateChunkSize(chunk)]; -- extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk); -+ // Paper start - Anti-Xray - Add chunk packet info -+ if (chunkPacketInfo != null) { -+ chunkPacketInfo.setBuffer(this.buffer); -+ } -+ -+ this.extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk, chunkPacketInfo); -+ // Paper end - this.blockEntitiesData = Lists.newArrayList(); - int totalTileEntities = 0; // Paper - -@@ -60,6 +83,7 @@ public class ClientboundLevelChunkPacketData { - this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(entry2.getValue())); - } - -+ chunk.level.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks - } - - public ClientboundLevelChunkPacketData(FriendlyByteBuf buf, int x, int z) { -@@ -103,9 +127,11 @@ public class ClientboundLevelChunkPacketData { - return byteBuf; - } - -- public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) { -+ // Paper start - Anti-Xray - Add chunk packet info -+ @Deprecated public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) { extractChunkData(buf, chunk, null); } // Notice for updates: Please make sure this method isn't used anywhere -+ public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk, com.destroystokyo.paper.antixray.ChunkPacketInfo chunkPacketInfo) { - for(LevelChunkSection levelChunkSection : chunk.getSections()) { -- levelChunkSection.write(buf); -+ levelChunkSection.write(buf, chunkPacketInfo); - } - - } -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 079734654c0fd421561b5f9003cd318bad5787a9..dc8ff27a3851982b0a798274ddb5e13af6917794 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -@@ -14,11 +14,12 @@ public class ClientboundLevelChunkWithLightPacket implements Packet cachedDataPacket, LevelChunk chunk) { - if (cachedDataPacket.getValue() == null) { -- cachedDataPacket.setValue(new ClientboundLevelChunkWithLightPacket(chunk, this.lightEngine, (BitSet) null, (BitSet) null, true)); -+ cachedDataPacket.setValue(new ClientboundLevelChunkWithLightPacket(chunk, this.lightEngine, (BitSet) null, (BitSet) null, true, chunk.level.chunkPacketBlockController.shouldModify(player, chunk))); // Paper - Ani-Xray - Bypass - } - - player.trackChunk(chunk.getPos(), (Packet) cachedDataPacket.getValue()); -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 09deaa9badf53bdc1292796c643751d8d92ac585..6c4f1fa1d96f906f3b4e1e352c919155a1092530 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -391,7 +391,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - // Add env and gen to constructor, WorldData -> WorldDataServer - public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, ServerLevelData iworlddataserver, ResourceKey resourcekey, DimensionType dimensionmanager, ChunkProgressListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) { - // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error -- super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getProfiler, false, flag, i, gen, biomeProvider, env); -+ super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getProfiler, false, flag, i, gen, biomeProvider, env, executor); // Paper - Anti-Xray - Pass executor - this.pvpMode = minecraftserver.isPvpAllowed(); - this.convertable = convertable_conversionsession; - this.uuid = WorldUUID.getUUID(convertable_conversionsession.levelPath.toFile()); -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -index 44e5ab0b545de41b937c7ce304ce643f78a43734..a85b89a0d525e623e154c8b1bea55470d2072dcb 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -@@ -48,7 +48,7 @@ import org.bukkit.event.player.PlayerInteractEvent; - public class ServerPlayerGameMode { - - private static final Logger LOGGER = LogManager.getLogger(); -- protected ServerLevel level; -+ public ServerLevel level; // Paper - Anti-Xray - protected -> public - protected final ServerPlayer player; - private GameType gameModeForPlayer; - @Nullable -@@ -314,6 +314,8 @@ public class ServerPlayerGameMode { - } - - } -+ -+ this.level.chunkPacketBlockController.onPlayerLeftClickBlock(this, pos, action, direction, worldHeight); // Paper - Anti-Xray - } - - public void destroyAndAck(BlockPos pos, ServerboundPlayerActionPacket.Action action, String reason) { -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index de7abae207b0a565a25fa4ed2f66b94c6b0cdcf9..69ac5ec34b6eb95036b679ed299e11766093935c 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -167,6 +167,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot - - public final com.destroystokyo.paper.PaperWorldConfig paperConfig; // Paper -+ public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray - - public final co.aikar.timings.WorldTimingsHandler timings; // Paper - public static BlockPos lastPhysicsProblem; // Spigot -@@ -186,7 +187,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - - public abstract ResourceKey getTypeKey(); - -- protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, final DimensionType dimensionmanager, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env) { -+ protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, final DimensionType dimensionmanager, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper - Anti-Xray - Pass executor - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot - this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), this.spigotConfig); // Paper - this.generator = gen; -@@ -262,6 +263,9 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - this.keepSpawnInMemory = this.paperConfig.keepSpawnInMemory; // Paper - this.entityLimiter = new org.spigotmc.TickLimiter(spigotConfig.entityMaxTickTime); - this.tileLimiter = new org.spigotmc.TickLimiter(spigotConfig.tileMaxTickTime); -+ this.chunkPacketBlockController = this.paperConfig.antiXray ? -+ new com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) -+ : com.destroystokyo.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray - } - - // Paper start -@@ -441,6 +445,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 - - if (iblockdata1 == null) { - // CraftBukkit start - remove blockstate if failed (or the same) -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 92a42aea3f54c49e2055e8000645d91da9471e09..2e651fcf0227dcf0d36fba6bf6e4ba6a5e21dfd7 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -158,9 +158,12 @@ public class LevelChunkSection { - this.biomes.read(buf); - } - -- public void write(FriendlyByteBuf buf) { -+ // Paper start - Anti-Xray - Add chunk packet info -+ @Deprecated public void write(FriendlyByteBuf buf) { write(buf, null); } // Notice for updates: Please make sure this method isn't used anywhere -+ public void write(FriendlyByteBuf buf, com.destroystokyo.paper.antixray.ChunkPacketInfo chunkPacketInfo) { -+ // Paper end - buf.writeShort(this.nonEmptyBlockCount); -- this.states.write(buf); -+ this.states.write(buf, chunkPacketInfo, this.bottomBlockY); // Paper - Anti-Xray - Add chunk packet info - this.biomes.write(buf); - } - -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 b7b81cbdb36b0041177e012cb932d4267638d9c9..803b75e88e96d4bdcde002fde4000d136d6f07e5 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java -+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java -@@ -33,6 +33,7 @@ public class PalettedContainer implements PaletteResize { - return 0; - }; - public final IdMap registry; -+ private final T[] presetValues; // Paper - Anti-Xray - Add preset values - private volatile PalettedContainer.Data data; - private final PalettedContainer.Strategy strategy; - private final Semaphore lock = new Semaphore(1); -@@ -63,14 +64,47 @@ public class PalettedContainer implements PaletteResize { - }); - } - -- public PalettedContainer(IdMap idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Configuration dataProvider, BitStorage storage, List list) { -+ // Paper start - Anti-Xray - Add preset values -+ @Deprecated public PalettedContainer(IdMap idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Configuration dataProvider, BitStorage storage, List list) { // Notice for updates: Please make sure this constructor isn't used anywhere -+ this(idList, paletteProvider, dataProvider, storage, list, null, true); -+ } -+ public PalettedContainer(IdMap idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Configuration dataProvider, BitStorage storage, List list, T[] presetValues, boolean initialize) { -+ // Paper end - this.registry = idList; - this.strategy = paletteProvider; - Palette datapalette = dataProvider.factory().create(dataProvider.bits(), idList, this, list); - - this.data = new PalettedContainer.Data(dataProvider, storage, datapalette); -+ // Paper start - Anti-Xray - Add preset values -+ this.presetValues = presetValues; -+ -+ if(initialize) { -+ if(presetValues == null) { -+ // Default -+ this.setBits(4); -+ } else { -+ // Count the number of required bits -+ // Preset values: presetValues.length - 1 -+ // Air: + 1 -+ // Extra: + 15 -+ // Air and extra correspond to the default behavior this.setBits(4) -+ this.setBits(32 - Integer.numberOfLeadingZeros(presetValues.length + 15)); -+ this.addPresetValues(); -+ } -+ } -+ // Paper end - } - -+ // Paper start - Anti-Xray - Add preset values -+ private void addPresetValues() { -+ if (this.presetValues != null && this.palette != this.globalPalette) { -+ for (T presetValue : this.presetValues) { -+ this.palette.idFor(presetValue); -+ } -+ } -+ } -+ // Paper end -+ - public PalettedContainer(IdMap idList, T object, PalettedContainer.Strategy paletteProvider) { - this.strategy = paletteProvider; - this.registry = idList; -@@ -88,6 +122,7 @@ public class PalettedContainer implements PaletteResize { - public int onResize(int newBits, T object) { - PalettedContainer.Data datapaletteblock_c = this.data; - PalettedContainer.Data datapaletteblock_c1 = this.createOrReuseData(datapaletteblock_c, newBits); -+ this.addPresetValues(); // Paper - Anti-Xray - Add preset values - - datapaletteblock_c1.copyFrom(datapaletteblock_c.palette, datapaletteblock_c.storage); - this.data = datapaletteblock_c1; -@@ -168,17 +203,26 @@ public class PalettedContainer implements PaletteResize { - datapaletteblock_c.palette.read(buf); - buf.readLongArray(datapaletteblock_c.storage.getRaw()); - this.data = datapaletteblock_c; -+ // Paper start - Anti-Xray - Add preset values -+ // If there are many preset values this may require several resize operations -+ // This can be avoided by calculating the required bits in advance, as it is done in #read(ListTag, long[]) -+ // However, this method is only used by the client, so it does not matter -+ this.addPresetValues(); -+ // Paper end - } finally { - this.release(); - } - - } - -- public void write(FriendlyByteBuf buf) { -+ // Paper start - Anti-Xray - Add chunk packet info -+ @Deprecated public void write(FriendlyByteBuf buf) { write(buf, null, 0); } // Notice for updates: Please make sure this method isn't used anywhere -+ public void write(FriendlyByteBuf buf, com.destroystokyo.paper.antixray.ChunkPacketInfo chunkPacketInfo, int bottomBlockY) { -+ // Paper end - this.acquire(); - - try { -- this.data.write(buf); -+ this.data.write(buf, chunkPacketInfo, bottomBlockY); - } finally { - this.release(); - } -@@ -188,6 +232,7 @@ public class PalettedContainer implements PaletteResize { - private static DataResult> read(IdMap idList, PalettedContainer.Strategy provider, PalettedContainer.DiscData serialized) { - List list = serialized.paletteEntries(); - int i = provider.size(); -+ // TODO I think we need to inject the old code into the calculateBitsForSerialization method - int j = provider.calculateBitsForSerialization(idList, list.size()); - PalettedContainer.Configuration datapaletteblock_a = provider.getConfiguration(idList, j); - Object object; -@@ -224,7 +269,7 @@ public class PalettedContainer implements PaletteResize { - } - } - -- return DataResult.success(new PalettedContainer<>(idList, provider, datapaletteblock_a, (BitStorage) object, list)); -+ return DataResult.success(new PalettedContainer<>(idList, provider, datapaletteblock_a, (BitStorage) object, list, this.presetValues, true)); - } - - private PalettedContainer.DiscData write(IdMap idList, PalettedContainer.Strategy provider) { -@@ -450,9 +495,22 @@ public class PalettedContainer implements PaletteResize { - return 1 + this.palette.getSerializedSize() + FriendlyByteBuf.getVarIntSize(this.storage.getSize()) + this.storage.getRaw().length * 8; - } - -- public void write(FriendlyByteBuf buf) { -+ // Paper start - Anti-Xray - Add chunk packet info -+ @Deprecated public void write(FriendlyByteBuf buf) { this.write(buf, null, 0);} // Notice for updates: Please make sure this method isn't used anywhere - it is used for biomes now -+ public void write(FriendlyByteBuf buf, com.destroystokyo.paper.antixray.ChunkPacketInfo chunkPacketInfo, int bottomBlockY) { -+ // Paper end - buf.writeByte(this.storage.getBits()); - this.palette.write(buf); -+ // Paper start - Anti-Xray - Add chunk packet info -+ if (chunkPacketInfo != null) { -+ // Bottom block to 0 based chunk section index -+ int chunkSectionIndex = (bottomBlockY >> 4) - chunkPacketInfo.getChunk().getMinSection(); -+ chunkPacketInfo.setBits(chunkSectionIndex, this.storage.getBits()); -+ chunkPacketInfo.setPalette(chunkSectionIndex, this.palette); -+ chunkPacketInfo.setIndex(chunkSectionIndex, buf.writerIndex() + FriendlyByteBuf.getVarIntSize(this.storage.getRaw().length)); -+ chunkPacketInfo.setPresetValues(chunkSectionIndex, this.presetValues); // TODO where do we get this from? -+ } -+ // Paper end - buf.writeLongArray(this.storage.getRaw()); - } - -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 8d1cbefc82f64a120eeea8c6bdd6361ffa513099..e8cb57d79c73f115679ddc2e6f579b5e1b5e482b 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -2204,7 +2204,7 @@ public final class CraftServer implements Server { - public ChunkGenerator.ChunkData createChunkData(World world) { - Validate.notNull(world, "World cannot be null"); - ServerLevel handle = ((CraftWorld) world).getHandle(); -- return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)); -+ return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY). world); // Paper - anti xray - add parameters - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java -index 960405935e395a31c0300773c41413801cf0d290..7acf5e6e7e038895d49a0d01d319eb7c8ba809bf 100644 ---- a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java -+++ b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java -@@ -27,8 +27,13 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData { - private final Registry biomes; - private Set tiles; - private final Set lights = new HashSet<>(); -+ private World world; // Paper - Anti-Xray - Add parameters - -- public OldCraftChunkData(int minHeight, int maxHeight, Registry biomes) { -+// Paper start - Anti-Xray - Add parameters -+ @Deprecated OldCraftChunkData(int minHeight, int maxHeight, Registry biomes) { this(minHeight, maxHeight, biomes, null); } // Only used in tests -+ public OldCraftChunkData(int minHeight, int maxHeight, Registry biomes, World world) { -+ this.world = world; -+ // Paper end - this.minHeight = minHeight; - this.maxHeight = maxHeight; - this.biomes = biomes; -@@ -176,7 +181,11 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData { - int offset = (y - this.minHeight) >> 4; - LevelChunkSection section = this.sections[offset]; - if (create && section == null) { -+<<<<<<< HEAD - this.sections[offset] = section = new LevelChunkSection(offset + (this.minHeight >> 4), this.biomes); -+======= -+ this.sections[offset] = section = new LevelChunkSection(offset + (this.minHeight >> 4), this.biomes, null, world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) this.world).getHandle() : null, true); // Paper - Anti-Xray - Add parameters -+>>>>>>> Anti-Xray - } - return section; - }