diff --git a/patches/server/0367-Anti-Xray.patch b/patches/server/0367-Anti-Xray.patch index e05eb362ac..45a9fde2e6 100644 --- a/patches/server/0367-Anti-Xray.patch +++ b/patches/server/0367-Anti-Xray.patch @@ -59,17 +59,160 @@ index a91a7d8f56a068b18d50a8b987b71510b0a19d5b..b8ca1f73b2451307c3711076eaa43e2a + } } +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..55e1c448999d79ddd9781d6f8ff2899807802146 +index 0000000000000000000000000000000000000000..280ece653cdda74e9c8fab4e9e5b3a952901cb01 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java -@@ -0,0 +1,45 @@ +@@ -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; @@ -86,11 +229,11 @@ index 0000000000000000000000000000000000000000..55e1c448999d79ddd9781d6f8ff28998 + + } + -+ public BlockState[] getPredefinedBlockData(Level world, ChunkAccess chunk, LevelChunkSection chunkSection, boolean initializeBlocks) { ++ public BlockState[] getPresetBlockStates(Level level, ChunkAccess chunk, LevelChunkSection chunkSection, boolean initializeBlocks) { + return null; + } + -+ public boolean shouldModify(ServerPlayer entityPlayer, LevelChunk chunk) { ++ public boolean shouldModify(ServerPlayer player, LevelChunk chunk) { + return false; + } + @@ -102,34 +245,28 @@ index 0000000000000000000000000000000000000000..55e1c448999d79ddd9781d6f8ff28998 + chunkPacket.setReady(true); + } + -+ public void onBlockChange(Level world, BlockPos blockPosition, BlockState newBlockData, BlockState oldBlockData, int flag) { ++ public void onBlockChange(Level level, BlockPos blockPos, BlockState newBlockState, BlockState oldBlockState, int flags, int maxUpdateDepth) { + + } + -+ public void onPlayerLeftClickBlock(ServerPlayerGameMode playerInteractManager, BlockPos blockPosition, Direction enumDirection) { ++ 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..c69332299015d90345636cf0f4425db62068f978 +index 0000000000000000000000000000000000000000..020dc2351bc47b9c03ccdcc8f94b2d09c22af61c --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -0,0 +1,644 @@ +@@ -0,0 +1,635 @@ +package com.destroystokyo.paper.antixray; + -+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 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.ClientboundLevelChunkPacket; ++import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; @@ -139,94 +276,88 @@ index 0000000000000000000000000000000000000000..c69332299015d90345636cf0f4425db6 +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.ChunkAccess; -+import net.minecraft.world.level.chunk.EmptyLevelChunk; -+import net.minecraft.world.level.chunk.LevelChunk; -+import net.minecraft.world.level.chunk.LevelChunkSection; -+import net.minecraft.world.level.chunk.Palette; ++import net.minecraft.world.level.chunk.*; +import org.bukkit.Bukkit; -+import org.bukkit.World.Environment; + -+import com.destroystokyo.paper.PaperWorldConfig; ++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 worldSectionHeight; -+ private final int maxChunkSectionIndex; ++ private final int maxBlockHeight; + private final int updateRadius; + private final boolean usePermission; -+ private final BlockState[] predefinedBlockData; -+ private final BlockState[] predefinedBlockDataFull; -+ private final BlockState[] predefinedBlockDataStone; -+ private final BlockState[] predefinedBlockDataNetherrack; -+ private final BlockState[] predefinedBlockDataEndStone; -+ private final int[] predefinedBlockDataBitsGlobal; -+ private final int[] predefinedBlockDataBitsStoneGlobal; -+ private final int[] predefinedBlockDataBitsNetherrackGlobal; -+ private final int[] predefinedBlockDataBitsEndStoneGlobal; ++ 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 maxBlockYUpdatePosition; ++ private final int maxBlockHeightUpdatePosition; + -+ public ChunkPacketBlockControllerAntiXray(Level world, Executor executor) { -+ PaperWorldConfig paperWorldConfig = world.paperConfig; ++ public ChunkPacketBlockControllerAntiXray(Level level, Executor executor) { ++ this.executor = executor; ++ PaperWorldConfig paperWorldConfig = level.paperConfig; + engineMode = paperWorldConfig.engineMode; -+ -+ int minSection = world.getMinSection(); -+ worldSectionHeight = world.getSectionsCount(); -+ maxChunkSectionIndex = (paperWorldConfig.maxBlockHeight >> 4) - minSection; ++ maxBlockHeight = paperWorldConfig.maxBlockHeight >> 4 << 4; + updateRadius = paperWorldConfig.updateRadius; + usePermission = paperWorldConfig.usePermission; -+ -+ this.executor = executor; -+ + List toObfuscate; + + if (engineMode == EngineMode.HIDE) { + toObfuscate = paperWorldConfig.hiddenBlocks; -+ predefinedBlockData = null; -+ predefinedBlockDataFull = null; -+ predefinedBlockDataStone = new BlockState[] {Blocks.STONE.defaultBlockState()}; -+ predefinedBlockDataNetherrack = new BlockState[] {Blocks.NETHERRACK.defaultBlockState()}; -+ predefinedBlockDataEndStone = new BlockState[] {Blocks.END_STONE.defaultBlockState()}; -+ predefinedBlockDataBitsGlobal = null; -+ predefinedBlockDataBitsStoneGlobal = new int[] {LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.STONE.defaultBlockState())}; -+ predefinedBlockDataBitsNetherrackGlobal = new int[] {LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.NETHERRACK.defaultBlockState())}; -+ predefinedBlockDataBitsEndStoneGlobal = new int[] {LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.END_STONE.defaultBlockState())}; ++ 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 predefinedBlockDataList = new LinkedList(); ++ List presetBlockStateList = new LinkedList<>(); + + for (String id : paperWorldConfig.hiddenBlocks) { + Block block = Registry.BLOCK.getOptional(new ResourceLocation(id)).orElse(null); + -+ if (block != null && !(block instanceof net.minecraft.world.level.block.EntityBlock)) { ++ if (block != null && !(block instanceof EntityBlock)) { + toObfuscate.add(id); -+ predefinedBlockDataList.add(block.defaultBlockState()); ++ presetBlockStateList.add(block.defaultBlockState()); + } + } + -+ // The doc of the LinkedHashSet(Collection c) constructor doesn't specify that the insertion order is the predictable iteration order of the specified Collection, although it is in the implementation -+ Set predefinedBlockDataSet = new LinkedHashSet<>(predefinedBlockDataList); -+ // Therefore addAll(Collection c) is used, which guarantees this order in the doc -+ predefinedBlockData = predefinedBlockDataSet.isEmpty() ? new BlockState[] {Blocks.DIAMOND_ORE.defaultBlockState()} : predefinedBlockDataSet.toArray(new BlockState[0]); -+ predefinedBlockDataFull = predefinedBlockDataSet.isEmpty() ? new BlockState[] {Blocks.DIAMOND_ORE.defaultBlockState()} : predefinedBlockDataList.toArray(new BlockState[0]); -+ predefinedBlockDataStone = null; -+ predefinedBlockDataNetherrack = null; -+ predefinedBlockDataEndStone = null; -+ predefinedBlockDataBitsGlobal = new int[predefinedBlockDataFull.length]; ++ // 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 < predefinedBlockDataFull.length; i++) { -+ predefinedBlockDataBitsGlobal[i] = LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(predefinedBlockDataFull[i]); ++ for (int i = 0; i < presetBlockStatesFull.length; i++) { ++ presetBlockStateBitsGlobal[i] = LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(presetBlockStatesFull[i]); + } + -+ predefinedBlockDataBitsStoneGlobal = null; -+ predefinedBlockDataBitsNetherrackGlobal = null; -+ predefinedBlockDataBitsEndStoneGlobal = null; ++ presetBlockStateBitsStoneGlobal = null; ++ presetBlockStateBitsNetherrackGlobal = null; ++ presetBlockStateBitsEndStoneGlobal = null; + } + + for (String id : toObfuscate) { @@ -235,93 +366,86 @@ index 0000000000000000000000000000000000000000..c69332299015d90345636cf0f4425db6 + // 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 blockData : block.getStateDefinition().getPossibleStates()) { -+ obfuscateGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(blockData)] = true; ++ for (BlockState blockState : block.getStateDefinition().getPossibleStates()) { ++ obfuscateGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)] = true; + } + } + } + -+ EmptyLevelChunk emptyChunk = new EmptyLevelChunk(world, new ChunkPos(0, 0)); ++ 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 blockData = LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.valueFor(i); ++ BlockState blockState = LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.valueFor(i); + -+ if (blockData != null) { -+ solidGlobal[i] = blockData.isRedstoneConductor(emptyChunk, zeroPos) -+ && blockData.getBlock() != Blocks.SPAWNER && blockData.getBlock() != Blocks.BARRIER && blockData.getBlock() != Blocks.SHULKER_BOX && blockData.getBlock() != Blocks.SLIME_BLOCK || paperWorldConfig.lavaObscures && blockData == Blocks.LAVA.defaultBlockState(); -+ // Comparing blockData == Blocks.LAVA.getBlockData() instead of blockData.getBlock() == Blocks.LAVA ensures that only "stationary lava" is used ++ 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. + } + } + -+ this.maxBlockYUpdatePosition = (maxChunkSectionIndex + 1) * 16 + updateRadius - 1; ++ maxBlockHeightUpdatePosition = maxBlockHeight + updateRadius - 1; + } + -+ private int getPredefinedBlockDataFullLength() { -+ return engineMode == EngineMode.HIDE ? 1 : predefinedBlockDataFull.length; ++ private int getPresetBlockStatesFullLength() { ++ return engineMode == EngineMode.HIDE ? 1 : presetBlockStatesFull.length; + } + + @Override -+ public BlockState[] getPredefinedBlockData(Level world, ChunkAccess chunk, LevelChunkSection chunkSection, boolean initializeBlocks) { -+ // Return the block data which should be added to the data palettes so that they can be used for the obfuscation -+ if (chunkSection.bottomBlockY() >> 4 <= maxChunkSectionIndex) { ++ 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 (world.getWorld().getEnvironment()) { -+ case NETHER -> predefinedBlockDataNetherrack; -+ case THE_END -> predefinedBlockDataEndStone; -+ default -> predefinedBlockDataStone; ++ return switch (level.getWorld().getEnvironment()) { ++ case NETHER -> presetBlockStatesNetherrack; ++ case THE_END -> presetBlockStatesEndStone; ++ default -> presetBlockStatesStone; + }; + } -+ return predefinedBlockData; ++ ++ return presetBlockStates; + } + + return null; + } + + @Override -+ public boolean shouldModify(ServerPlayer entityPlayer, LevelChunk chunk) { -+ return !usePermission || !entityPlayer.getBukkitEntity().hasPermission("paper.antixray.bypass"); ++ public boolean shouldModify(ServerPlayer player, LevelChunk chunk) { ++ return !usePermission || !player.getBukkitEntity().hasPermission("paper.antixray.bypass"); + } + + @Override + public ChunkPacketInfoAntiXray getChunkPacketInfo(ClientboundLevelChunkPacket 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 -+ // Note: As of 1.14 this has to be moved later due to the chunk system. + return new ChunkPacketInfoAntiXray(chunkPacket, chunk, this); + } + + @Override + public void modifyBlocks(ClientboundLevelChunkPacket chunkPacket, ChunkPacketInfo chunkPacketInfo) { -+ if (chunkPacketInfo == null) { ++ if (!(chunkPacketInfo instanceof ChunkPacketInfoAntiXray)) { + chunkPacket.setReady(true); + return; + } + + if (!Bukkit.isPrimaryThread()) { -+ // plugins? -+ MinecraftServer.getServer().scheduleOnMain(() -> { -+ this.modifyBlocks(chunkPacket, chunkPacketInfo); -+ }); ++ // Plugins? ++ MinecraftServer.getServer().scheduleOnMain(() -> modifyBlocks(chunkPacket, chunkPacketInfo)); + return; + } + + LevelChunk chunk = chunkPacketInfo.getChunk(); + int x = chunk.getPos().x; + int z = chunk.getPos().z; -+ ServerLevel world = chunk.level; -+ ((ChunkPacketInfoAntiXray) chunkPacketInfo).setNearbyChunks( -+ (LevelChunk) world.getChunkIfLoadedImmediately(x - 1, z), -+ (LevelChunk) world.getChunkIfLoadedImmediately(x + 1, z), -+ (LevelChunk) world.getChunkIfLoadedImmediately(x, z - 1), -+ (LevelChunk) world.getChunkIfLoadedImmediately(x, z + 1)); -+ -+ executor.execute((ChunkPacketInfoAntiXray) chunkPacketInfo); ++ 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 predefinedBlockDataBits = ThreadLocal.withInitial(() -> new int[getPredefinedBlockDataFullLength()]); ++ 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 @@ -330,27 +454,30 @@ index 0000000000000000000000000000000000000000..c69332299015d90345636cf0f4425db6 + private static final ThreadLocal nextNext = ThreadLocal.withInitial(() -> new boolean[16][16]); + + public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) { -+ int[] predefinedBlockDataBits = this.predefinedBlockDataBits.get(); ++ 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(); -+ // dataBitsReader, dataBitsWriter and nearbyChunkSections could also be reused (with ThreadLocal if necessary) but it's not worth it -+ DataBitsReader dataBitsReader = new DataBitsReader(); -+ DataBitsWriter dataBitsWriter = new DataBitsWriter(); ++ // 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; -+ dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData()); -+ dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData()); -+ int numberOfBlocks = predefinedBlockDataBits.length; ++ 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); ++ while ((state = ThreadLocalRandom.current().nextInt()) == 0) ; + } + + @Override @@ -365,33 +492,41 @@ index 0000000000000000000000000000000000000000..c69332299015d90345636cf0f4425db6 + }; + + for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) { -+ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) { -+ int[] predefinedBlockDataBitsTemp; ++ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex) != null) { ++ int[] presetBlockStateBitsTemp; + -+ if (chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex) == LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE) { -+ predefinedBlockDataBitsTemp = engineMode == EngineMode.HIDE ? chunkPacketInfoAntiXray.getChunk().level.getWorld().getEnvironment() == Environment.NETHER ? predefinedBlockDataBitsNetherrackGlobal : chunkPacketInfoAntiXray.getChunk().level.getWorld().getEnvironment() == Environment.THE_END ? predefinedBlockDataBitsEndStoneGlobal : predefinedBlockDataBitsStoneGlobal : predefinedBlockDataBitsGlobal; ++ 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 this.predefinedBlockData, use this.predefinedBlockDataFull instead -+ BlockState[] predefinedBlockDataFull = chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) == predefinedBlockData ? this.predefinedBlockDataFull : chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex); -+ predefinedBlockDataBitsTemp = predefinedBlockDataBits; ++ // 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 < predefinedBlockDataBitsTemp.length; i++) { -+ predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).idFor(predefinedBlockDataFull[i]); ++ for (int i = 0; i < presetBlockStateBitsTemp.length; i++) { ++ presetBlockStateBitsTemp[i] = chunkPacketInfoAntiXray.getPalette(chunkSectionIndex).idFor(presetBlockStatesFull[i]); + } + } + -+ dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getDataBitsIndex(chunkSectionIndex)); ++ bitStorageWriter.setIndex(chunkPacketInfoAntiXray.getIndex(chunkSectionIndex)); + + // Check if the chunk section below was not obfuscated -+ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex - 1) == null) { ++ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex - 1) == null) { + // If so, initialize some stuff -+ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); -+ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getDataBitsIndex(chunkSectionIndex)); -+ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), solid, solidGlobal); -+ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), obfuscate, obfuscateGlobal); ++ 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 = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex - 1]) == LevelChunk.EMPTY_SECTION; ++ 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++) { @@ -401,11 +536,11 @@ index 0000000000000000000000000000000000000000..c69332299015d90345636cf0f4425db6 + } + + // Abuse the obfuscateLayer method to read the blocks of the first layer of the current chunk section -+ dataBitsWriter.setBitsPerObject(0); -+ obfuscateLayer(-1, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, emptyNearbyChunkSections, random); ++ bitStorageWriter.setBits(0); ++ obfuscateLayer(-1, bitStorageReader, bitStorageWriter, solidTemp, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, emptyNearbyChunkSections, random); + } + -+ dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); ++ 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]; @@ -417,15 +552,15 @@ index 0000000000000000000000000000000000000000..c69332299015d90345636cf0f4425db6 + current = next; + next = nextNext; + nextNext = temp; -+ obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, random); ++ 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.getPredefinedObjects(chunkSectionIndex + 1) == null) { ++ 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 != worldSectionHeight && (aboveChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex + 1]) != LevelChunk.EMPTY_SECTION) { ++ if (chunkSectionIndex != chunk.getSectionsCount() - 1 && (aboveChunkSection = chunk.getSections()[chunkSectionIndex + 1]) != LevelChunk.EMPTY_SECTION) { + boolean[][] temp = current; + current = next; + next = nextNext; @@ -440,291 +575,290 @@ index 0000000000000000000000000000000000000000..c69332299015d90345636cf0f4425db6 + } + + // There is nothing to read anymore -+ dataBitsReader.setBitsPerObject(0); ++ bitStorageReader.setBits(0); + solid[0] = true; -+ obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, random); ++ 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 -+ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex + 1)); -+ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getDataBitsIndex(chunkSectionIndex + 1)); -+ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), solid, solidGlobal); -+ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal); ++ 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, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, random); ++ obfuscateLayer(15, bitStorageReader, bitStorageWriter, solidTemp, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, nearbyChunkSections, random); + } + -+ dataBitsWriter.finish(); ++ bitStorageWriter.flush(); + } + } + + chunkPacketInfoAntiXray.getChunkPacket().setReady(true); + } + -+ private void obfuscateLayer(int y, DataBitsReader dataBitsReader, DataBitsWriter dataBitsWriter, boolean[] solid, boolean[] obfuscate, int[] predefinedBlockDataBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, LevelChunkSection[] nearbyChunkSections, IntSupplier random) { ++ 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 dataBits = dataBitsReader.read(); ++ int bits = bitStorageReader.read(); + -+ if (nextNext[0][0] = !solid[dataBits]) { -+ dataBitsWriter.skip(); ++ if (nextNext[0][0] = !solid[bits]) { ++ bitStorageWriter.skip(); + next[0][1] = true; + next[1][0] = true; + } else { + if (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))] || current[0][0]) { -+ dataBitsWriter.skip(); ++ bitStorageWriter.skip(); + } else { -+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]); ++ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]); + } + } + -+ if (!obfuscate[dataBits]) { ++ if (!obfuscate[bits]) { + next[0][0] = true; + } + + // First line + for (int x = 1; x < 15; x++) { -+ dataBits = dataBitsReader.read(); ++ bits = bitStorageReader.read(); + -+ if (nextNext[0][x] = !solid[dataBits]) { -+ dataBitsWriter.skip(); ++ if (nextNext[0][x] = !solid[bits]) { ++ bitStorageWriter.skip(); + next[0][x - 1] = true; + next[0][x + 1] = true; + next[1][x] = true; + } else { + if (nearbyChunkSections[2] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[2].getBlockState(x, y, 15))] || current[0][x]) { -+ dataBitsWriter.skip(); ++ bitStorageWriter.skip(); + } else { -+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]); ++ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]); + } + } + -+ if (!obfuscate[dataBits]) { ++ if (!obfuscate[bits]) { + next[0][x] = true; + } + } + + // Last block of first line -+ dataBits = dataBitsReader.read(); ++ bits = bitStorageReader.read(); + -+ if (nextNext[0][15] = !solid[dataBits]) { -+ dataBitsWriter.skip(); ++ if (nextNext[0][15] = !solid[bits]) { ++ bitStorageWriter.skip(); + next[0][14] = true; + next[1][15] = true; + } else { + if (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))] || current[0][15]) { -+ dataBitsWriter.skip(); ++ bitStorageWriter.skip(); + } else { -+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]); ++ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]); + } + } + -+ if (!obfuscate[dataBits]) { ++ if (!obfuscate[bits]) { + next[0][15] = true; + } + + // All inner lines + for (int z = 1; z < 15; z++) { + // First block -+ dataBits = dataBitsReader.read(); ++ bits = bitStorageReader.read(); + -+ if (nextNext[z][0] = !solid[dataBits]) { -+ dataBitsWriter.skip(); ++ if (nextNext[z][0] = !solid[bits]) { ++ bitStorageWriter.skip(); + next[z][1] = true; + next[z - 1][0] = true; + next[z + 1][0] = true; + } else { + if (nearbyChunkSections[0] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[0].getBlockState(15, y, z))] || current[z][0]) { -+ dataBitsWriter.skip(); ++ bitStorageWriter.skip(); + } else { -+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]); ++ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]); + } + } + -+ if (!obfuscate[dataBits]) { ++ if (!obfuscate[bits]) { + next[z][0] = true; + } + + // All inner blocks + for (int x = 1; x < 15; x++) { -+ dataBits = dataBitsReader.read(); ++ bits = bitStorageReader.read(); + -+ if (nextNext[z][x] = !solid[dataBits]) { -+ dataBitsWriter.skip(); ++ 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]) { -+ dataBitsWriter.skip(); ++ bitStorageWriter.skip(); + } else { -+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]); ++ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]); + } + } + -+ if (!obfuscate[dataBits]) { ++ if (!obfuscate[bits]) { + next[z][x] = true; + } + } + + // Last block -+ dataBits = dataBitsReader.read(); ++ bits = bitStorageReader.read(); + -+ if (nextNext[z][15] = !solid[dataBits]) { -+ dataBitsWriter.skip(); ++ if (nextNext[z][15] = !solid[bits]) { ++ bitStorageWriter.skip(); + next[z][14] = true; + next[z - 1][15] = true; + next[z + 1][15] = true; + } else { + if (nearbyChunkSections[1] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[1].getBlockState(0, y, z))] || current[z][15]) { -+ dataBitsWriter.skip(); ++ bitStorageWriter.skip(); + } else { -+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]); ++ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]); + } + } + -+ if (!obfuscate[dataBits]) { ++ if (!obfuscate[bits]) { + next[z][15] = true; + } + } + + // First block of last line -+ dataBits = dataBitsReader.read(); ++ bits = bitStorageReader.read(); + -+ if (nextNext[15][0] = !solid[dataBits]) { -+ dataBitsWriter.skip(); ++ if (nextNext[15][0] = !solid[bits]) { ++ bitStorageWriter.skip(); + next[15][1] = true; + next[14][0] = true; + } else { + if (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))] || current[15][0]) { -+ dataBitsWriter.skip(); ++ bitStorageWriter.skip(); + } else { -+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]); ++ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]); + } + } + -+ if (!obfuscate[dataBits]) { ++ if (!obfuscate[bits]) { + next[15][0] = true; + } + + // Last line + for (int x = 1; x < 15; x++) { -+ dataBits = dataBitsReader.read(); ++ bits = bitStorageReader.read(); + -+ if (nextNext[15][x] = !solid[dataBits]) { -+ dataBitsWriter.skip(); ++ if (nextNext[15][x] = !solid[bits]) { ++ bitStorageWriter.skip(); + next[15][x - 1] = true; + next[15][x + 1] = true; + next[14][x] = true; + } else { + if (nearbyChunkSections[3] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[3].getBlockState(x, y, 0))] || current[15][x]) { -+ dataBitsWriter.skip(); ++ bitStorageWriter.skip(); + } else { -+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]); ++ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]); + } + } + -+ if (!obfuscate[dataBits]) { ++ if (!obfuscate[bits]) { + next[15][x] = true; + } + } + + // Last block of last line -+ dataBits = dataBitsReader.read(); ++ bits = bitStorageReader.read(); + -+ if (nextNext[15][15] = !solid[dataBits]) { -+ dataBitsWriter.skip(); ++ if (nextNext[15][15] = !solid[bits]) { ++ bitStorageWriter.skip(); + next[15][14] = true; + next[14][15] = true; + } else { + if (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))] || current[15][15]) { -+ dataBitsWriter.skip(); ++ bitStorageWriter.skip(); + } else { -+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]); ++ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]); + } + } + -+ if (!obfuscate[dataBits]) { ++ if (!obfuscate[bits]) { + next[15][15] = true; + } + } + -+ private boolean[] readDataPalette(Palette dataPalette, boolean[] temp, boolean[] global) { -+ if (dataPalette == LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE) { ++ private boolean[] readPalette(Palette palette, boolean[] temp, boolean[] global) { ++ if (palette == LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE) { + return global; + } + -+ BlockState blockData; ++ BlockState blockState; + -+ for (int i = 0; (blockData = dataPalette.valueFor(i)) != null; i++) { -+ temp[i] = global[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(blockData)]; ++ 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 world, BlockPos blockPosition, BlockState newBlockData, BlockState oldBlockData, int flag) { -+ if (oldBlockData != null && solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(oldBlockData)] && !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(newBlockData)] && blockPosition.getY() <= maxBlockYUpdatePosition) { -+ updateNearbyBlocks(world, blockPosition); ++ 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 playerInteractManager, BlockPos blockPosition, Direction enumDirection) { -+ if (blockPosition.getY() <= maxBlockYUpdatePosition) { -+ updateNearbyBlocks(playerInteractManager.level, blockPosition); ++ 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 world, BlockPos blockPosition) { ++ private void updateNearbyBlocks(Level level, BlockPos blockPos) { + if (updateRadius >= 2) { -+ BlockPos temp = blockPosition.west(); -+ updateBlock(world, temp); -+ updateBlock(world, temp.west()); -+ updateBlock(world, temp.below()); -+ updateBlock(world, temp.above()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp.south()); -+ updateBlock(world, temp = blockPosition.east()); -+ updateBlock(world, temp.east()); -+ updateBlock(world, temp.below()); -+ updateBlock(world, temp.above()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp.south()); -+ updateBlock(world, temp = blockPosition.below()); -+ updateBlock(world, temp.below()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp.south()); -+ updateBlock(world, temp = blockPosition.above()); -+ updateBlock(world, temp.above()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp.south()); -+ updateBlock(world, temp = blockPosition.north()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp = blockPosition.south()); -+ updateBlock(world, temp.south()); ++ 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(world, blockPosition.west()); -+ updateBlock(world, blockPosition.east()); -+ updateBlock(world, blockPosition.below()); -+ updateBlock(world, blockPosition.above()); -+ updateBlock(world, blockPosition.north()); -+ updateBlock(world, blockPosition.south()); ++ 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 world, BlockPos blockPosition) { -+ BlockState blockData = world.getTypeIfLoaded(blockPosition); ++ private void updateBlock(Level level, BlockPos blockPos) { ++ BlockState blockState = level.getTypeIfLoaded(blockPos); + -+ if (blockData != null && obfuscateGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(blockData)]) { -+ // world.notify(blockPosition, blockData, blockData, 3); -+ ((ServerLevel)world).getChunkSource().blockChanged(blockPosition); // We only need to re-send to client ++ if (blockState != null && obfuscateGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)]) { ++ ((ServerLevel) level).getChunkSource().blockChanged(blockPos); + } + } + @@ -762,10 +896,10 @@ index 0000000000000000000000000000000000000000..c69332299015d90345636cf0f4425db6 +} 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..7c3a85d65754c820e71a1db68f0c5a76c7327d9d +index 0000000000000000000000000000000000000000..7bc2d4daffa8e9e71c3bf496d2cf1a2b7f3c6a4b --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java -@@ -0,0 +1,81 @@ +@@ -0,0 +1,80 @@ +package com.destroystokyo.paper.antixray; + +import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket; @@ -776,21 +910,20 @@ index 0000000000000000000000000000000000000000..7c3a85d65754c820e71a1db68f0c5a76 + + private final ClientboundLevelChunkPacket chunkPacket; + private final LevelChunk chunk; -+ private byte[] data; -+ private final int[] bitsPerObject; -+ private final Object[] dataPalettes; -+ private final int[] dataBitsIndexes; -+ private final Object[][] predefinedObjects; ++ private final int[] bits; ++ private final Object[] palettes; ++ private final int[] indexes; ++ private final Object[][] presetValues; ++ private byte[] buffer; + + public ChunkPacketInfo(ClientboundLevelChunkPacket chunkPacket, LevelChunk chunk) { + this.chunkPacket = chunkPacket; + this.chunk = chunk; -+ + int sections = chunk.getSectionsCount(); -+ this.bitsPerObject = new int[sections]; -+ this.dataPalettes = new Object[sections]; -+ this.dataBitsIndexes = new int[sections]; -+ this.predefinedObjects = new Object[sections][]; ++ bits = new int[sections]; ++ palettes = new Object[sections]; ++ indexes = new int[sections]; ++ presetValues = new Object[sections][]; + } + + public ClientboundLevelChunkPacket getChunkPacket() { @@ -801,58 +934,58 @@ index 0000000000000000000000000000000000000000..7c3a85d65754c820e71a1db68f0c5a76 + return chunk; + } + -+ public byte[] getData() { -+ return data; ++ public byte[] getBuffer() { ++ return buffer; + } + -+ public void setData(byte[] data) { -+ this.data = data; ++ public void setBuffer(byte[] buffer) { ++ this.buffer = buffer; + } + -+ public int getBitsPerObject(int chunkSectionIndex) { -+ return bitsPerObject[chunkSectionIndex]; ++ public int getBits(int chunkSectionIndex) { ++ return bits[chunkSectionIndex]; + } + -+ public void setBitsPerObject(int chunkSectionIndex, int bitsPerObject) { -+ this.bitsPerObject[chunkSectionIndex] = bitsPerObject; ++ public void setBits(int chunkSectionIndex, int bits) { ++ this.bits[chunkSectionIndex] = bits; + } + + @SuppressWarnings("unchecked") -+ public Palette getDataPalette(int chunkSectionIndex) { -+ return (Palette) dataPalettes[chunkSectionIndex]; ++ public Palette getPalette(int chunkSectionIndex) { ++ return (Palette) palettes[chunkSectionIndex]; + } + -+ public void setDataPalette(int chunkSectionIndex, Palette dataPalette) { -+ dataPalettes[chunkSectionIndex] = dataPalette; ++ public void setPalette(int chunkSectionIndex, Palette palette) { ++ palettes[chunkSectionIndex] = palette; + } + -+ public int getDataBitsIndex(int chunkSectionIndex) { -+ return dataBitsIndexes[chunkSectionIndex]; ++ public int getIndex(int chunkSectionIndex) { ++ return indexes[chunkSectionIndex]; + } + -+ public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) { -+ dataBitsIndexes[chunkSectionIndex] = dataBitsIndex; ++ public void setIndex(int chunkSectionIndex, int index) { ++ indexes[chunkSectionIndex] = index; + } + + @SuppressWarnings("unchecked") -+ public T[] getPredefinedObjects(int chunkSectionIndex) { -+ return (T[]) predefinedObjects[chunkSectionIndex]; ++ public T[] getPresetValues(int chunkSectionIndex) { ++ return (T[]) presetValues[chunkSectionIndex]; + } + -+ public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) { -+ this.predefinedObjects[chunkSectionIndex] = predefinedObjects; ++ public void setPresetValues(int chunkSectionIndex, T[] presetValues) { ++ this.presetValues[chunkSectionIndex] = presetValues; + } + + public boolean isWritten(int chunkSectionIndex) { -+ return bitsPerObject[chunkSectionIndex] != 0; ++ 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..2339aa92ecaf3af9c7481ec6c21981c39319c76f +index 0000000000000000000000000000000000000000..02324a59ac21db5349fe2a74248b2c6f92fa8233 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java -@@ -0,0 +1,30 @@ +@@ -0,0 +1,29 @@ +package com.destroystokyo.paper.antixray; + +import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket; @@ -861,12 +994,11 @@ index 0000000000000000000000000000000000000000..2339aa92ecaf3af9c7481ec6c21981c3 + +public final class ChunkPacketInfoAntiXray extends ChunkPacketInfo implements Runnable { + -+ private LevelChunk[] nearbyChunks; + private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray; ++ private LevelChunk[] nearbyChunks; + -+ public ChunkPacketInfoAntiXray(ClientboundLevelChunkPacket packetPlayOutMapChunk, LevelChunk chunk, -+ ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) { -+ super(packetPlayOutMapChunk, chunk); ++ public ChunkPacketInfoAntiXray(ClientboundLevelChunkPacket chunkPacket, LevelChunk chunk, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) { ++ super(chunkPacket, chunk); + this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray; + } + @@ -883,160 +1015,29 @@ index 0000000000000000000000000000000000000000..2339aa92ecaf3af9c7481ec6c21981c3 + chunkPacketBlockControllerAntiXray.obfuscate(this); + } +} -diff --git a/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java -new file mode 100644 -index 0000000000000000000000000000000000000000..298ea423084dbcc1b61f991bcd82b8ae51bf0977 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java -@@ -0,0 +1,51 @@ -+package com.destroystokyo.paper.antixray; -+ -+public final class DataBitsReader { -+ -+ private byte[] dataBits; -+ private int bitsPerObject; -+ private int mask; -+ private int longInDataBitsIndex; -+ private int bitInLongIndex; -+ private long current; -+ -+ public void setDataBits(byte[] dataBits) { -+ this.dataBits = dataBits; -+ } -+ -+ public void setBitsPerObject(int bitsPerObject) { -+ this.bitsPerObject = bitsPerObject; -+ mask = (1 << bitsPerObject) - 1; -+ } -+ -+ public void setIndex(int index) { -+ this.longInDataBitsIndex = index; -+ bitInLongIndex = 0; -+ init(); -+ } -+ -+ private void init() { -+ if (dataBits.length > longInDataBitsIndex + 7) { -+ current = ((((long) dataBits[longInDataBitsIndex]) << 56) -+ | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48) -+ | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40) -+ | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32) -+ | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24) -+ | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16) -+ | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8) -+ | (((long) dataBits[longInDataBitsIndex + 7] & 0xff))); -+ } -+ } -+ -+ public int read() { -+ if (bitInLongIndex + bitsPerObject > 64) { -+ bitInLongIndex = 0; -+ longInDataBitsIndex += 8; -+ init(); -+ } -+ -+ int value = (int) (current >>> bitInLongIndex) & mask; -+ bitInLongIndex += bitsPerObject; -+ return value; -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java -new file mode 100644 -index 0000000000000000000000000000000000000000..333763936897befda5bb6c077944d2667f922799 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java -@@ -0,0 +1,79 @@ -+package com.destroystokyo.paper.antixray; -+ -+public final class DataBitsWriter { -+ -+ private byte[] dataBits; -+ private int bitsPerObject; -+ private long mask; -+ private int longInDataBitsIndex; -+ private int bitInLongIndex; -+ private long current; -+ private boolean dirty; -+ -+ public void setDataBits(byte[] dataBits) { -+ this.dataBits = dataBits; -+ } -+ -+ public void setBitsPerObject(int bitsPerObject) { -+ this.bitsPerObject = bitsPerObject; -+ mask = (1 << bitsPerObject) - 1; -+ } -+ -+ public void setIndex(int index) { -+ this.longInDataBitsIndex = index; -+ bitInLongIndex = 0; -+ init(); -+ } -+ -+ private void init() { -+ if (dataBits.length > longInDataBitsIndex + 7) { -+ current = ((((long) dataBits[longInDataBitsIndex]) << 56) -+ | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48) -+ | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40) -+ | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32) -+ | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24) -+ | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16) -+ | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8) -+ | (((long) dataBits[longInDataBitsIndex + 7] & 0xff))); -+ } -+ -+ dirty = false; -+ } -+ -+ public void finish() { -+ if (dirty && dataBits.length > longInDataBitsIndex + 7) { -+ dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff); -+ dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff); -+ dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff); -+ dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff); -+ dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff); -+ dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff); -+ dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff); -+ dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff); -+ } -+ } -+ -+ public void write(int value) { -+ if (bitInLongIndex + bitsPerObject > 64) { -+ finish(); -+ bitInLongIndex = 0; -+ longInDataBitsIndex += 8; -+ init(); -+ } -+ -+ current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex; -+ dirty = true; -+ bitInLongIndex += bitsPerObject; -+ } -+ -+ public void skip() { -+ bitInLongIndex += bitsPerObject; -+ -+ if (bitInLongIndex > 64) { -+ finish(); -+ bitInLongIndex = bitsPerObject; -+ longInDataBitsIndex += 8; -+ init(); -+ } -+ } -+} diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacket.java -index c28879f32b004f36ff746ea2274f91ddd9501e71..7762d8ff94f856d613a6f50311006b698f2aa2b0 100644 +index c28879f32b004f36ff746ea2274f91ddd9501e71..60d72e488bc77cd913328be400ca374a873b4561 100644 --- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacket.java +++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacket.java -@@ -37,7 +37,13 @@ public class ClientboundLevelChunkPacket implements Packet chunkPacketInfo = modifyBlocks ? chunk.level.chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null; @@ -1044,30 +1045,31 @@ index c28879f32b004f36ff746ea2274f91ddd9501e71..7762d8ff94f856d613a6f50311006b69 ChunkPos chunkPos = chunk.getPos(); this.x = chunkPos.x; this.z = chunkPos.z; -@@ -51,7 +57,12 @@ public class ClientboundLevelChunkPacket implements Packet 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) { // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error - super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getProfiler, false, flag, i, gen, env); -+ super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getProfiler, false, flag, i, gen, env, executor); // Paper - pass executor ++ super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getProfiler, false, flag, i, gen, 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 f4a056185990181e486f452960159a5287947382..a695e5a0c2e8846333ccb9aea499b5656af35163 100644 +index f4a056185990181e486f452960159a5287947382..b932580dc10703c8a7dbecd4cf38954872f1cab6 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java @@ -49,7 +49,7 @@ import org.bukkit.event.player.PlayerInteractEvent; @@ -1139,7 +1125,7 @@ index f4a056185990181e486f452960159a5287947382..a695e5a0c2e8846333ccb9aea499b565 private static final Logger LOGGER = LogManager.getLogger(); - protected ServerLevel level; -+ public ServerLevel level; // Paper - protected->public ++ public ServerLevel level; // Paper - Anti-Xray - protected -> public protected final ServerPlayer player; private GameType gameModeForPlayer; @Nullable @@ -1148,12 +1134,12 @@ index f4a056185990181e486f452960159a5287947382..a695e5a0c2e8846333ccb9aea499b565 } + -+ this.level.chunkPacketBlockController.onPlayerLeftClickBlock(this, pos, direction); // Paper - Anti-Xray ++ 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 6d9c74595cd8883167554e8b52008a99d6e113c3..afdbec2365db55ab968b15368ae9ce239b63f373 100644 +index 6d9c74595cd8883167554e8b52008a99d6e113c3..0cd747bd368715c76cd27387ffe513b4a760f8e9 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -164,6 +164,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { @@ -1169,7 +1155,7 @@ index 6d9c74595cd8883167554e8b52008a99d6e113c3..afdbec2365db55ab968b15368ae9ce23 } - protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, final DimensionType dimensionmanager, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, 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.World.Environment env, java.util.concurrent.Executor executor) { // Paper ++ protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, final DimensionType dimensionmanager, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, 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; @@ -1187,36 +1173,32 @@ index 6d9c74595cd8883167554e8b52008a99d6e113c3..afdbec2365db55ab968b15368ae9ce23 // CraftBukkit end BlockState iblockdata1 = chunk.setType(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag -+ this.chunkPacketBlockController.onBlockChange(this, pos, state, iblockdata1, flags); // Paper - Anti-Xray ++ this.chunkPacketBlockController.onBlockChange(this, pos, state, iblockdata1, flags, maxUpdateDepth); // Paper - Anti-Xray if (iblockdata1 == null) { // CraftBukkit start - remove blockstate if failed (or the same) diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -index c0075d226331f32e470dae5bf1ce8d79e8b263dc..a857953f3488e79fd601ac63881bc4d87708afa7 100644 +index c0075d226331f32e470dae5bf1ce8d79e8b263dc..ba409922a5274fbb9a02a897cf7fe490b02fd781 100644 --- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java +++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -@@ -75,12 +75,18 @@ public interface ChunkAccess extends BlockGetter, FeatureAccess { +@@ -75,12 +75,14 @@ public interface ChunkAccess extends BlockGetter, FeatureAccess { default LevelChunkSection getOrCreateSection(int yIndex) { LevelChunkSection[] levelChunkSections = this.getSections(); if (levelChunkSections[yIndex] == LevelChunk.EMPTY_SECTION) { - levelChunkSections[yIndex] = new LevelChunkSection(this.getSectionYFromSectionIndex(yIndex)); -+ levelChunkSections[yIndex] = new LevelChunkSection(this.getSectionYFromSectionIndex(yIndex), this, getServerLevel(), true); ++ levelChunkSections[yIndex] = new LevelChunkSection(this.getSectionYFromSectionIndex(yIndex), this, this.getLevel(), true); // Paper - Anti-Xray - Add parameters } return levelChunkSections[yIndex]; } -+ // Paper start -+ default net.minecraft.server.level.ServerLevel getServerLevel() { -+ return null; -+ } -+ // Paper end ++ net.minecraft.world.level.Level getLevel(); // Paper - Anti-Xray - Add parameters + Collection> getHeightmaps(); default void setHeightmap(Heightmap.Types type, long[] heightmap) { diff --git a/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java -index 69c2454533e6f21c70792b555ec02c6bc6d169b3..2607c7ba5cf1aca5f3e5c22be2e4e8b3007427d4 100644 +index 69c2454533e6f21c70792b555ec02c6bc6d169b3..f9e0e109497d685a9d88d2fa8892287b9fa97443 100644 --- a/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java @@ -86,7 +86,7 @@ public class EmptyLevelChunk extends LevelChunk { @@ -1224,12 +1206,12 @@ index 69c2454533e6f21c70792b555ec02c6bc6d169b3..2607c7ba5cf1aca5f3e5c22be2e4e8b3 public EmptyChunkBiomeContainer(Level world) { - super(world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), world, EMPTY_BIOMES); -+ super(net.minecraft.server.MinecraftServer.getServer().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), world, EMPTY_BIOMES); // Paper - world isnt ready yet for anti xray use here, use server singleton for registry ++ super(net.minecraft.server.MinecraftServer.getServer().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), world, EMPTY_BIOMES); // Paper - Anti-Xray - The world isnt ready yet, use server singleton for registry } @Override 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 0530de6e9167419b180a3fd6e890d27d9d86c04c..2accbda808b485fa75011b7493567138b0945096 100644 +index 0530de6e9167419b180a3fd6e890d27d9d86c04c..7661f956e7c900602ceedbcd030d3049d90b8cca 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java @@ -456,7 +456,7 @@ public class LevelChunk implements ChunkAccess { @@ -1241,20 +1223,8 @@ index 0530de6e9167419b180a3fd6e890d27d9d86c04c..2accbda808b485fa75011b7493567138 this.sections[j] = chunksection; } -@@ -1306,4 +1306,11 @@ public class LevelChunk implements ChunkAccess { - return "Level ticker for " + s + "@" + this.getPos(); - } - } -+ -+ // Paper start -+ @Override -+ public net.minecraft.server.level.ServerLevel getServerLevel() { -+ return level; -+ } -+ // Paper end - } 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 5fd66020a937b641e2a060cf38df731a43f3bf55..ec8b67c1b024df38d5e1ad81acff33537ae25626 100644 +index 5fd66020a937b641e2a060cf38df731a43f3bf55..c9fefeef19bd46ade51b23eadb5eef3a88024ea1 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java @@ -20,16 +20,25 @@ public class LevelChunkSection { @@ -1265,9 +1235,9 @@ index 5fd66020a937b641e2a060cf38df731a43f3bf55..ec8b67c1b024df38d5e1ad81acff3353 - this(yOffset, (short)0, (short)0, (short)0); + // Paper start - Anti-Xray - Add parameters + @Deprecated public LevelChunkSection(int yOffset) { this(yOffset, null, null, true); } // Notice for updates: Please make sure this constructor isn't used anywhere -+ public LevelChunkSection(int yOffset, ChunkAccess chunk, net.minecraft.server.level.ServerLevel world, boolean initializeBlocks) { ++ public LevelChunkSection(int yOffset, ChunkAccess chunk, net.minecraft.world.level.Level level, boolean initializeBlocks) { ++ this(yOffset, (short) 0, (short) 0, (short) 0, chunk, level, initializeBlocks); + // Paper end -+ this(yOffset, (short) 0, (short) 0, (short) 0, chunk, world, initializeBlocks); } - public LevelChunkSection(int yOffset, short nonEmptyBlockCount, short randomTickableBlockCount, short nonEmptyFluidCount) { @@ -1275,7 +1245,7 @@ index 5fd66020a937b641e2a060cf38df731a43f3bf55..ec8b67c1b024df38d5e1ad81acff3353 + @Deprecated public LevelChunkSection(int yOffset, short nonEmptyBlockCount, short randomTickableBlockCount, short nonEmptyFluidCount) { // Notice for updates: Please make sure this constructor isn't used anywhere + this(yOffset, nonEmptyBlockCount, randomTickableBlockCount, nonEmptyFluidCount, null, null, true); + } -+ public LevelChunkSection(int yOffset, short nonEmptyBlockCount, short randomTickableBlockCount, short nonEmptyFluidCount, ChunkAccess chunk, net.minecraft.server.level.ServerLevel world, boolean initializeBlocks) { ++ public LevelChunkSection(int yOffset, short nonEmptyBlockCount, short randomTickableBlockCount, short nonEmptyFluidCount, ChunkAccess chunk, net.minecraft.world.level.Level level, boolean initializeBlocks) { + // Paper end this.bottomBlockY = getBottomBlockY(yOffset); this.nonEmptyBlockCount = nonEmptyBlockCount; @@ -1283,7 +1253,7 @@ index 5fd66020a937b641e2a060cf38df731a43f3bf55..ec8b67c1b024df38d5e1ad81acff3353 this.tickingFluidCount = nonEmptyFluidCount; - this.states = new PalettedContainer<>(GLOBAL_BLOCKSTATE_PALETTE, Block.BLOCK_STATE_REGISTRY, NbtUtils::readBlockState, NbtUtils::writeBlockState, Blocks.AIR.defaultBlockState()); + this.states = new PalettedContainer<>(GLOBAL_BLOCKSTATE_PALETTE, Block.BLOCK_STATE_REGISTRY, NbtUtils::readBlockState, NbtUtils::writeBlockState, Blocks.AIR.defaultBlockState(), -+ world == null ? null : world.chunkPacketBlockController.getPredefinedBlockData(world, chunk, this, initializeBlocks), initializeBlocks); // Paper - Anti-Xray - Add predefined block data ++ level == null ? null : level.chunkPacketBlockController.getPresetBlockStates(level, chunk, this, initializeBlocks), initializeBlocks); // Paper - Anti-Xray - Add preset block states } public static int getBottomBlockY(int chunkPos) { @@ -1292,7 +1262,7 @@ index 5fd66020a937b641e2a060cf38df731a43f3bf55..ec8b67c1b024df38d5e1ad81acff3353 } - public void write(FriendlyByteBuf buf) { -+ // Paper start ++ // 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 @@ -1303,152 +1273,149 @@ index 5fd66020a937b641e2a060cf38df731a43f3bf55..ec8b67c1b024df38d5e1ad81acff3353 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 bb8fd88aebb550edec8c679622a02a595cbc6694..4a6981f8bacdeca1069e1ddfe44ac9c4217ce624 100644 +index bb8fd88aebb550edec8c679622a02a595cbc6694..ac51089aae57a5f1d2411367ff177e058702894c 100644 --- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java @@ -28,6 +28,7 @@ public class PalettedContainer implements PaletteResize { private final Function reader; private final Function writer; private final T defaultValue; -+ private final T[] predefinedObjects; // Paper - Anti-Xray - Add predefined objects ++ private final T[] presetValues; // Paper - Anti-Xray - Add preset values protected BitStorage storage; private Palette palette; private int bits; -@@ -48,15 +49,51 @@ public class PalettedContainer implements PaletteResize { +@@ -48,14 +49,46 @@ public class PalettedContainer implements PaletteResize { this.lock.release(); } - public PalettedContainer(Palette fallbackPalette, IdMapper idList, Function elementDeserializer, Function elementSerializer, T defaultElement) { -+ // Paper start - Anti-Xray - Add predefined objects ++ // Paper start - Anti-Xray - Add preset values + @Deprecated public PalettedContainer(Palette fallbackPalette, IdMapper idList, Function elementDeserializer, Function elementSerializer, T defaultElement) { // Notice for updates: Please make sure this constructor isn't used anywhere + this(fallbackPalette, idList, elementDeserializer, elementSerializer, defaultElement, null, true); + } -+ public PalettedContainer(Palette fallbackPalette, IdMapper idList, Function elementDeserializer, Function elementSerializer, T defaultElement, T[] predefinedObjects, boolean initialize) { ++ public PalettedContainer(Palette fallbackPalette, IdMapper idList, Function elementDeserializer, Function elementSerializer, T defaultElement, T[] presetValues, boolean initialize) { + // Paper end this.globalPalette = fallbackPalette; this.registry = idList; this.reader = elementDeserializer; this.writer = elementSerializer; this.defaultValue = defaultElement; - this.setBits(4); -+ // Paper start - Anti-Xray - Add predefined objects -+ this.predefinedObjects = predefinedObjects; +- this.setBits(4); ++ // Paper start - Anti-Xray - Add preset values ++ this.presetValues = presetValues; + + if (initialize) { -+ if (predefinedObjects == null) { ++ if (presetValues == null) { + // Default + this.setBits(4); + } else { -+ // MathHelper.d() is trailingBits(roundCeilPow2(n)), alternatively; (int)ceil(log2(n)); however it's trash, use numberOfLeadingZeros instead -+ // Count the bits of the maximum array index to initialize a data palette with enough space from the beginning -+ // The length of the array is used because air is also added to the data palette from the beginning -+ // Start with at least 4 -+ int maxIndex = predefinedObjects.length >> 4; -+ int bitCount = (32 - Integer.numberOfLeadingZeros(Math.max(16, maxIndex) - 1)); -+ -+ // Initialize with at least 15 free indixes -+ this.setBits((1 << bitCount) - predefinedObjects.length < 16 ? bitCount + 1 : bitCount); -+ this.addPredefinedObjects(); ++ // 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 predefined objects -+ private void addPredefinedObjects() { -+ if (this.predefinedObjects != null && this.palette != this.globalPalette) { -+ for (T predefinedObject : this.predefinedObjects) { -+ this.palette.idFor(predefinedObject); ++ } ++ ++ // 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 -+ + private static int getIndex(int x, int y, int z) { return y << 8 | z << 4 | x; - } -@@ -85,6 +122,7 @@ public class PalettedContainer implements PaletteResize { +@@ -84,6 +117,7 @@ public class PalettedContainer implements PaletteResize { + BitStorage bitStorage = this.storage; Palette palette = this.palette; this.setBits(newSize); ++ this.addPresetValues(); // Paper - Anti-Xray - Add preset values -+ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects for(int i = 0; i < bitStorage.getSize(); ++i) { T object = palette.valueFor(bitStorage.get(i)); - if (object != null) { -@@ -159,11 +197,26 @@ public class PalettedContainer implements PaletteResize { +@@ -153,17 +187,38 @@ public class PalettedContainer implements PaletteResize { + + this.palette.read(buf); + buf.readLongArray(this.storage.getRaw()); ++ // 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); -+ } ++ @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 try { this.acquire(); buf.writeByte(this.bits); this.palette.write(buf); ++ + // Paper start - Anti-Xray - Add chunk packet info + if (chunkPacketInfo != null) { + // Bottom block to 0 based chunk section index -+ int section = (bottomBlockY >> 4) - chunkPacketInfo.getChunk().getMinSection(); -+ chunkPacketInfo.setBitsPerObject(section, this.bits); -+ chunkPacketInfo.setDataPalette(section, this.palette); -+ chunkPacketInfo.setDataBitsIndex(section, buf.writerIndex() + FriendlyByteBuf.getVarIntSize(this.storage.getRaw().length)); -+ chunkPacketInfo.setPredefinedObjects(section, this.predefinedObjects); ++ int chunkSectionIndex = (bottomBlockY >> 4) - chunkPacketInfo.getChunk().getMinSection(); ++ chunkPacketInfo.setBits(chunkSectionIndex, this.bits); ++ chunkPacketInfo.setPalette(chunkSectionIndex, this.palette); ++ chunkPacketInfo.setIndex(chunkSectionIndex, buf.writerIndex() + FriendlyByteBuf.getVarIntSize(this.storage.getRaw().length)); ++ chunkPacketInfo.setPresetValues(chunkSectionIndex, this.presetValues); + } + // Paper end ++ buf.writeLongArray(this.storage.getRaw()); } finally { this.release(); -@@ -174,12 +227,14 @@ public class PalettedContainer implements PaletteResize { +@@ -174,12 +229,14 @@ public class PalettedContainer implements PaletteResize { public void read(ListTag paletteNbt, long[] data) { try { this.acquire(); - int i = Math.max(4, Mth.ceillog2(paletteNbt.size())); - if (i != this.bits) { -+ // Paper - Anti-Xray - TODO: Should this.predefinedObjects.length just be added here (faster) or should the contents be compared to calculate the size (less RAM)? -+ int i = Math.max(4, Mth.ceillog2(paletteNbt.size() + (this.predefinedObjects == null ? 0 : this.predefinedObjects.length))); // Paper - Anti-Xray - Calculate the size with predefined objects ++ // Paper - Anti-Xray - TODO: Should this.presetValues.length just be added here (faster) or should the contents be compared to calculate the size (less RAM)? ++ int i = Math.max(4, Mth.ceillog2(paletteNbt.size() + (this.presetValues == null ? 0 : this.presetValues.length))); // Paper - Anti-Xray - Calculate the size with preset values + if (true || i != this.bits) { // Paper - Anti-Xray - Not initialized yet this.setBits(i); } this.palette.read(paletteNbt); -+ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects ++ this.addPresetValues(); // Paper - Anti-Xray - Add preset values int j = data.length * 64 / 4096; if (this.palette == this.globalPalette) { Palette palette = new HashMapPalette<>(this.registry, i, this.dummyPaletteResize, this.reader, this.writer); diff --git a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java -index 64cb0658021866c3875d145cc4266896e57c081e..f0c537e1d6b32ecde52b3d456f0f3889ff554824 100644 +index 64cb0658021866c3875d145cc4266896e57c081e..78bd3274866fed3d627a3eda7b96b92716507d38 100644 --- a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java -@@ -63,7 +63,7 @@ public class ProtoChunk implements ChunkAccess { - private long inhabitedTime; - private final Map carvingMasks = new Object2ObjectArrayMap<>(); - private volatile boolean isLightCorrect; -- final net.minecraft.world.level.Level level; // Paper - Add level -+ final net.minecraft.server.level.ServerLevel level; // Paper - Add level - - // Paper start - add level - @Deprecated public ProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor world) { this(pos, upgradeData, world, null); } -@@ -98,6 +98,13 @@ public class ProtoChunk implements ChunkAccess { - this.postProcessing = new ShortList[world.getSectionsCount()]; +@@ -506,4 +506,11 @@ public class ProtoChunk implements ChunkAccess { + public int getHeight() { + return this.levelHeightAccessor.getHeight(); } - -+ // Paper start ++ ++ // Paper start - Anti-Xray - Add parameters + @Override -+ public net.minecraft.server.level.ServerLevel getServerLevel() { ++ public net.minecraft.world.level.Level getLevel() { + return level; + } + // Paper end -+ - // Paper start - If loaded util - @Override - public FluidState getFluidIfLoaded(BlockPos blockposition) { + } diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java -index abd02d88911cbbbe6f770039aa0649cff85d85ee..9db83a8f2eceac41f6b86430e719bad1a12471b5 100644 +index abd02d88911cbbbe6f770039aa0649cff85d85ee..45c571b475b8fac19fd7b5ccbf5fdae5b435c37f 100644 --- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java +++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java @@ -136,7 +136,7 @@ public class ChunkSerializer { @@ -1456,12 +1423,12 @@ index abd02d88911cbbbe6f770039aa0649cff85d85ee..9db83a8f2eceac41f6b86430e719bad1 if (nbttagcompound2.contains("Palette", 9) && nbttagcompound2.contains("BlockStates", 12)) { - LevelChunkSection chunksection = new LevelChunkSection(b0); -+ LevelChunkSection chunksection = new LevelChunkSection(b0, null, world, false); // Paper - Anti-Xray - Add parameters ++ LevelChunkSection chunksection = new LevelChunkSection(b0, null, world, false); // Paper - Anti-Xray - Add parameters and don't initialize because it's done in the line below internally chunksection.getStates().read(nbttagcompound2.getList("Palette", 10), nbttagcompound2.getLongArray("BlockStates")); chunksection.recalcBlockCounts(); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -index 245d764d3dcc549fa8acbd7c9024a3c88d2d2a74..4dd7cea1eec5ec55a3700ce9786da8a513e72a28 100644 +index 245d764d3dcc549fa8acbd7c9024a3c88d2d2a74..1c49512c4b9c1b187e555312fe937f2a37c9e112 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java @@ -46,7 +46,7 @@ public class CraftChunk implements Chunk { @@ -1478,23 +1445,23 @@ index 245d764d3dcc549fa8acbd7c9024a3c88d2d2a74..4dd7cea1eec5ec55a3700ce9786da8a5 cs[i].getStates().write(data, "Palette", "BlockStates"); - PalettedContainer blockids = new PalettedContainer<>(LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE, net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, NbtUtils::readBlockState, NbtUtils::writeBlockState, Blocks.AIR.defaultBlockState()); // TODO: snapshot whole ChunkSection -+ PalettedContainer blockids = new PalettedContainer<>(LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE, net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, NbtUtils::readBlockState, NbtUtils::writeBlockState, Blocks.AIR.defaultBlockState(), null, false); // TODO: snapshot whole ChunkSection // Paper - Anti-Xray - Add no predefined block data and don't initialize because it's done in the line below internally ++ PalettedContainer blockids = new PalettedContainer<>(LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE, net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, NbtUtils::readBlockState, NbtUtils::writeBlockState, Blocks.AIR.defaultBlockState(), null, false); // TODO: snapshot whole ChunkSection // Paper - Anti-Xray - Add no preset block states and don't initialize because it's done in the line below internally blockids.read(data.getList("Palette", CraftMagicNumbers.NBT.TAG_COMPOUND), data.getLongArray("BlockStates")); sectionBlockIDs[i] = blockids; diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java -index 3d905c98704da64cefd009b2c796b24e729396a5..fe7851476636dfed02339d4d9f93824b96086769 100644 +index 3d905c98704da64cefd009b2c796b24e729396a5..c4d5349f515d5c0ffad4db15ecca1431c830b824 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java @@ -22,9 +22,11 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { private final int maxHeight; private final LevelChunkSection[] sections; private Set tiles; -+ private World world; // Paper - Anti-Xray - Add world ++ private World world; // Paper - Anti-Xray - Add parameters public CraftChunkData(World world) { this(world.getMinHeight(), world.getMaxHeight()); -+ this.world = world; // Paper - Anti-Xray - Add world ++ this.world = world; // Paper - Anti-Xray - Add parameters } /* pp for tests */ CraftChunkData(int minHeight, int maxHeight) { @@ -1503,7 +1470,7 @@ index 3d905c98704da64cefd009b2c796b24e729396a5..fe7851476636dfed02339d4d9f93824b LevelChunkSection section = this.sections[offset]; if (create && section == null) { - this.sections[offset] = section = new LevelChunkSection(offset + (this.minHeight >> 4)); -+ this.sections[offset] = section = new LevelChunkSection(offset + (this.minHeight >> 4), null, world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) world).getHandle() : null, true); // Paper - Anti-Xray - Add parameters ++ this.sections[offset] = section = new LevelChunkSection(offset + (this.minHeight >> 4), null, this.world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) this.world).getHandle() : null, true); // Paper - Anti-Xray - Add parameters } return section; } diff --git a/patches/server/0486-Allow-delegation-to-vanilla-chunk-gen.patch b/patches/server/0486-Allow-delegation-to-vanilla-chunk-gen.patch index 04470a2ed8..5663c8881f 100644 --- a/patches/server/0486-Allow-delegation-to-vanilla-chunk-gen.patch +++ b/patches/server/0486-Allow-delegation-to-vanilla-chunk-gen.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Allow delegation to vanilla chunk gen diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 434a46dca55453815772eeb50ef412b02af2c0a1..ac7034a922facb772a67580100627a7c85510693 100644 +index 30552c2dcb2b8e648ee6519478e830f3e86a10b9..17468c082e9d56193b0c7e5ae1713a1e8da8ad23 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -2028,6 +2028,32 @@ public final class CraftServer implements Server { @@ -42,7 +42,7 @@ index 434a46dca55453815772eeb50ef412b02af2c0a1..ac7034a922facb772a67580100627a7c public BossBar createBossBar(String title, BarColor color, BarStyle style, BarFlag... flags) { return new CraftBossBar(title, color, style, flags); diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java -index fe7851476636dfed02339d4d9f93824b96086769..24a2e88d083f90375c46cf948c7c89dccc6e4aa0 100644 +index c4d5349f515d5c0ffad4db15ecca1431c830b824..4645303efa716442b14e5c1e767b0d94dbb50170 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java @@ -20,7 +20,7 @@ import org.bukkit.material.MaterialData; @@ -52,7 +52,7 @@ index fe7851476636dfed02339d4d9f93824b96086769..24a2e88d083f90375c46cf948c7c89dc - private final LevelChunkSection[] sections; + private LevelChunkSection[] sections; // Paper - remove final private Set tiles; - private World world; // Paper - Anti-Xray - Add world + private World world; // Paper - Anti-Xray - Add parameters @@ -173,6 +173,12 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { return this.sections; diff --git a/patches/server/0700-Synchronize-PalettedContainer-instead-of-ReentrantLo.patch b/patches/server/0700-Synchronize-PalettedContainer-instead-of-ReentrantLo.patch index 117a3dfebe..2584559a0d 100644 --- a/patches/server/0700-Synchronize-PalettedContainer-instead-of-ReentrantLo.patch +++ b/patches/server/0700-Synchronize-PalettedContainer-instead-of-ReentrantLo.patch @@ -13,7 +13,7 @@ contention situations. And this is extremely a low contention situation. 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 4a6981f8bacdeca1069e1ddfe44ac9c4217ce624..f2307f81c399867585ffdefc0db835c6f5e2f42a 100644 +index ac51089aae57a5f1d2411367ff177e058702894c..554474d4b2e57d8a005b3c3b9b23f32a62243058 100644 --- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java @@ -37,16 +37,18 @@ public class PalettedContainer implements PaletteResize { @@ -35,8 +35,8 @@ index 4a6981f8bacdeca1069e1ddfe44ac9c4217ce624..f2307f81c399867585ffdefc0db835c6 + //this.lock.release(); // Paper - disable this } - // Paper start - Anti-Xray - Add predefined objects -@@ -133,7 +135,7 @@ public class PalettedContainer implements PaletteResize { + // Paper start - Anti-Xray - Add preset values +@@ -129,7 +131,7 @@ public class PalettedContainer implements PaletteResize { return this.palette.idFor(objectAdded); } @@ -45,7 +45,7 @@ index 4a6981f8bacdeca1069e1ddfe44ac9c4217ce624..f2307f81c399867585ffdefc0db835c6 Object var6; try { this.acquire(); -@@ -157,7 +159,7 @@ public class PalettedContainer implements PaletteResize { +@@ -153,7 +155,7 @@ public class PalettedContainer implements PaletteResize { return (T)(object == null ? this.defaultValue : object); } @@ -54,7 +54,7 @@ index 4a6981f8bacdeca1069e1ddfe44ac9c4217ce624..f2307f81c399867585ffdefc0db835c6 try { this.acquire(); this.set(getIndex(i, j, k), object); -@@ -181,7 +183,7 @@ public class PalettedContainer implements PaletteResize { +@@ -177,7 +179,7 @@ public class PalettedContainer implements PaletteResize { return (T)(object == null ? this.defaultValue : object); } @@ -64,15 +64,15 @@ index 4a6981f8bacdeca1069e1ddfe44ac9c4217ce624..f2307f81c399867585ffdefc0db835c6 this.acquire(); int i = buf.readByte(); @@ -201,7 +203,7 @@ public class PalettedContainer implements PaletteResize { - @Deprecated public void write(FriendlyByteBuf buf) { - write(buf, null, 0); - } + + // 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) { + public synchronized void write(FriendlyByteBuf buf, com.destroystokyo.paper.antixray.ChunkPacketInfo chunkPacketInfo, int bottomBlockY) { // Paper - synchronize // Paper end try { this.acquire(); -@@ -224,7 +226,7 @@ public class PalettedContainer implements PaletteResize { +@@ -226,7 +228,7 @@ public class PalettedContainer implements PaletteResize { } @@ -80,8 +80,8 @@ index 4a6981f8bacdeca1069e1ddfe44ac9c4217ce624..f2307f81c399867585ffdefc0db835c6 + public synchronized void read(ListTag paletteNbt, long[] data) { // Paper - synchronize try { this.acquire(); - // Paper - Anti-Xray - TODO: Should this.predefinedObjects.length just be added here (faster) or should the contents be compared to calculate the size (less RAM)? -@@ -259,7 +261,7 @@ public class PalettedContainer implements PaletteResize { + // Paper - Anti-Xray - TODO: Should this.presetValues.length just be added here (faster) or should the contents be compared to calculate the size (less RAM)? +@@ -261,7 +263,7 @@ public class PalettedContainer implements PaletteResize { }