From 46a6f1b2c5e7ee1c9d7098851054a258c02c2951 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Tue, 25 Jun 2019 21:50:57 -0700 Subject: [PATCH 1/8] Re-Add Anti-Xray --- Spigot-Server-Patches/0408-Anti-Xray.patch | 1580 ++++++++++++++++++++ 1 file changed, 1580 insertions(+) create mode 100644 Spigot-Server-Patches/0408-Anti-Xray.patch diff --git a/Spigot-Server-Patches/0408-Anti-Xray.patch b/Spigot-Server-Patches/0408-Anti-Xray.patch new file mode 100644 index 0000000000..ad50f76897 --- /dev/null +++ b/Spigot-Server-Patches/0408-Anti-Xray.patch @@ -0,0 +1,1580 @@ +From 9f304c5c1ecc2202f221f1e80b67d2cc1b4494e7 Mon Sep 17 00:00:00 2001 +From: stonar96 +Date: Mon, 20 Aug 2018 03:03:58 +0200 +Subject: [PATCH] Anti-Xray + + +diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +index 58109e1308..93d397d317 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +@@ -1,7 +1,11 @@ + package com.destroystokyo.paper; + ++import java.util.Arrays; + import java.util.List; + ++import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.ChunkEdgeMode; ++import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode; ++import net.minecraft.server.MinecraftServer; + import org.bukkit.Bukkit; + import org.bukkit.configuration.file.YamlConfiguration; + import org.spigotmc.SpigotWorldConfig; +@@ -509,4 +513,34 @@ public class PaperWorldConfig { + private void maxAutoSaveChunksPerTick() { + maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24); + } ++ ++ public boolean antiXray; ++ public boolean asynchronous; ++ public EngineMode engineMode; ++ public ChunkEdgeMode chunkEdgeMode; ++ public int maxChunkSectionIndex; ++ public int updateRadius; ++ public List hiddenBlocks; ++ public List replacementBlocks; ++ private void antiXray() { ++ antiXray = getBoolean("anti-xray.enabled", false); ++ asynchronous = true; ++ engineMode = EngineMode.getById(getInt("anti-xray.engine-mode", EngineMode.HIDE.getId())); ++ engineMode = engineMode == null ? EngineMode.HIDE : engineMode; ++ chunkEdgeMode = ChunkEdgeMode.getById(getInt("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId())); ++ chunkEdgeMode = chunkEdgeMode == null ? ChunkEdgeMode.DEFAULT : chunkEdgeMode; ++ ++ if (chunkEdgeMode != ChunkEdgeMode.WAIT) { ++ log("Migrating anti-xray chunk edge mode to " + ChunkEdgeMode.WAIT + " (" + ChunkEdgeMode.WAIT.getId() + ")"); ++ chunkEdgeMode = ChunkEdgeMode.WAIT; ++ set("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId()); ++ } ++ ++ maxChunkSectionIndex = getInt("anti-xray.max-chunk-section-index", 3); ++ maxChunkSectionIndex = maxChunkSectionIndex > 15 ? 15 : maxChunkSectionIndex; ++ updateRadius = getInt("anti-xray.update-radius", 2); ++ hiddenBlocks = getList("anti-xray.hidden-blocks", Arrays.asList("gold_ore", "iron_ore", "coal_ore", "lapis_ore", "mossy_cobblestone", "obsidian", "chest", "diamond_ore", "redstone_ore", "clay", "emerald_ore", "ender_chest")); ++ replacementBlocks = getList("anti-xray.replacement-blocks", Arrays.asList("stone", "oak_planks")); ++ log("Anti-Xray: " + (antiXray ? "enabled" : "disabled") + " / Engine Mode: " + engineMode.getDescription() + " / Chunk Edge Mode: " + chunkEdgeMode.getDescription() + " / Up to " + ((maxChunkSectionIndex + 1) * 16) + " blocks / Update Radius: " + updateRadius); ++ } + } +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 0000000000..dc534d239e +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java +@@ -0,0 +1,45 @@ ++package com.destroystokyo.paper.antixray; ++ ++import net.minecraft.server.BlockPosition; ++import net.minecraft.server.Chunk; ++import net.minecraft.server.ChunkSection; ++import net.minecraft.server.EnumDirection; ++import net.minecraft.server.IBlockData; ++import net.minecraft.server.IChunkAccess; ++import net.minecraft.server.IWorldReader; ++import net.minecraft.server.PacketPlayOutMapChunk; ++import net.minecraft.server.PlayerInteractManager; ++import net.minecraft.server.World; ++ ++public class ChunkPacketBlockController { ++ ++ public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController(); ++ ++ protected ChunkPacketBlockController() { ++ ++ } ++ ++ public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean initializeBlocks) { ++ return null; ++ } ++ ++ public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { ++ return true; ++ } ++ ++ public ChunkPacketInfo getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { ++ return null; ++ } ++ ++ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { ++ packetPlayOutMapChunk.setReady(true); ++ } ++ ++ public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) { ++ ++ } ++ ++ public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) { ++ ++ } ++} +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 0000000000..3aa614833d +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java +@@ -0,0 +1,691 @@ ++package com.destroystokyo.paper.antixray; ++ ++import java.util.ArrayList; ++import java.util.HashSet; ++import java.util.List; ++import java.util.Set; ++import java.util.concurrent.ExecutorService; ++import java.util.concurrent.Executors; ++ ++import net.minecraft.server.*; ++import org.bukkit.World.Environment; ++ ++import com.destroystokyo.paper.PaperWorldConfig; ++ ++public class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController { ++ ++ private static ExecutorService executorServiceInstance = null; ++ private final ExecutorService executorService; ++ private final boolean asynchronous; ++ private final EngineMode engineMode; ++ private final ChunkEdgeMode chunkEdgeMode; ++ private final int maxChunkSectionIndex; ++ private final int updateRadius; ++ private final IBlockData[] predefinedBlockData; ++ private final IBlockData[] predefinedBlockDataStone; ++ private final IBlockData[] predefinedBlockDataNetherrack; ++ private final IBlockData[] predefinedBlockDataEndStone; ++ private final int[] predefinedBlockDataBitsGlobal; ++ private final int[] predefinedBlockDataBitsStoneGlobal; ++ private final int[] predefinedBlockDataBitsNetherrackGlobal; ++ private final int[] predefinedBlockDataBitsEndStoneGlobal; ++ private final boolean[] solidGlobal = new boolean[Block.REGISTRY_ID.size()]; ++ private final boolean[] obfuscateGlobal = new boolean[Block.REGISTRY_ID.size()]; ++ private final ChunkSection[] emptyNearbyChunkSections = {Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION}; ++ private final int maxBlockYUpdatePosition; ++ ++ public ChunkPacketBlockControllerAntiXray(PaperWorldConfig paperWorldConfig) { ++ asynchronous = paperWorldConfig.asynchronous; ++ engineMode = paperWorldConfig.engineMode; ++ chunkEdgeMode = paperWorldConfig.chunkEdgeMode; ++ maxChunkSectionIndex = paperWorldConfig.maxChunkSectionIndex; ++ updateRadius = paperWorldConfig.updateRadius; ++ ++ if (asynchronous) { ++ executorService = getExecutorServiceInstance(); ++ } else { ++ executorService = null; ++ } ++ ++ List toObfuscate; ++ ++ if (engineMode == EngineMode.HIDE) { ++ toObfuscate = paperWorldConfig.hiddenBlocks; ++ predefinedBlockData = null; ++ predefinedBlockDataStone = new IBlockData[] {Blocks.STONE.getBlockData()}; ++ predefinedBlockDataNetherrack = new IBlockData[] {Blocks.NETHERRACK.getBlockData()}; ++ predefinedBlockDataEndStone = new IBlockData[] {Blocks.END_STONE.getBlockData()}; ++ predefinedBlockDataBitsGlobal = null; ++ predefinedBlockDataBitsStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.STONE.getBlockData())}; ++ predefinedBlockDataBitsNetherrackGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.NETHERRACK.getBlockData())}; ++ predefinedBlockDataBitsEndStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.END_STONE.getBlockData())}; ++ } else { ++ toObfuscate = new ArrayList<>(paperWorldConfig.replacementBlocks); ++ Set predefinedBlockDataSet = new HashSet(); ++ ++ for (String id : paperWorldConfig.hiddenBlocks) { ++ Block block = IRegistry.BLOCK.getOptional(new MinecraftKey(id)).orElse(null); ++ ++ if (block != null && !block.isTileEntity()) { ++ toObfuscate.add(id); ++ predefinedBlockDataSet.add(block.getBlockData()); ++ } ++ } ++ ++ predefinedBlockData = predefinedBlockDataSet.size() == 0 ? new IBlockData[] {Blocks.DIAMOND_ORE.getBlockData()} : predefinedBlockDataSet.toArray(new IBlockData[0]); ++ predefinedBlockDataStone = null; ++ predefinedBlockDataNetherrack = null; ++ predefinedBlockDataEndStone = null; ++ predefinedBlockDataBitsGlobal = new int[predefinedBlockData.length]; ++ ++ for (int i = 0; i < predefinedBlockData.length; i++) { ++ predefinedBlockDataBitsGlobal[i] = ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(predefinedBlockData[i]); ++ } ++ ++ predefinedBlockDataBitsStoneGlobal = null; ++ predefinedBlockDataBitsNetherrackGlobal = null; ++ predefinedBlockDataBitsEndStoneGlobal = null; ++ } ++ ++ for (String id : toObfuscate) { ++ Block block = IRegistry.BLOCK.getOptional(new MinecraftKey(id)).orElse(null); ++ ++ if (block != null) { ++ obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(block.getBlockData())] = true; ++ } ++ } ++ ++ ChunkEmpty emptyChunk = new ChunkEmpty(null, new ChunkCoordIntPair(0, 0)); ++ BlockPosition zeroPos = new BlockPosition(0, 0, 0); ++ ++ for (int i = 0; i < solidGlobal.length; i++) { ++ IBlockData blockData = ChunkSection.GLOBAL_PALETTE.getObject(i); ++ ++ if (blockData != null) { ++ solidGlobal[i] = blockData.getBlock().isOccluding(blockData, emptyChunk, zeroPos) ++ && blockData.getBlock() != Blocks.SPAWNER && blockData.getBlock() != Blocks.BARRIER && blockData.getBlock() != Blocks.SHULKER_BOX; ++ // shulker box checks TE. ++ } ++ } ++ ++ this.maxBlockYUpdatePosition = (maxChunkSectionIndex + 1) * 16 + updateRadius - 1; ++ } ++ ++ private static ExecutorService getExecutorServiceInstance() { ++ if (executorServiceInstance == null) { ++ executorServiceInstance = Executors.newSingleThreadExecutor(); ++ } ++ ++ return executorServiceInstance; ++ } ++ ++ @Override ++ public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection 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.getYPosition() >> 4 <= maxChunkSectionIndex) { ++ switch (engineMode) { ++ case HIDE: ++ if (world instanceof GeneratorAccess) { ++ switch (((GeneratorAccess) world).getMinecraftWorld().getWorld().getEnvironment()) { ++ case NETHER: ++ return predefinedBlockDataNetherrack; ++ case THE_END: ++ return predefinedBlockDataEndStone; ++ default: ++ return predefinedBlockDataStone; ++ } ++ } ++ ++ return null; ++ default: ++ return predefinedBlockData; ++ } ++ } ++ ++ return null; ++ } ++ ++ @Override ++ public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { ++ int locX = chunk.getPos().x; ++ int locZ = chunk.getPos().z; ++ WorldServer world = (WorldServer)chunk.world; ++ ChunkProviderServer chunkProvider = world.getChunkProvider(); ++ ++ //Load nearby chunks if necessary ++ if (force || chunkEdgeMode == ChunkEdgeMode.LOAD) { // TODO temporary ++ // if forced, load NOW; ++ chunk.world.getChunkAt(locX - 1, locZ); ++ chunk.world.getChunkAt(locX + 1, locZ); ++ chunk.world.getChunkAt(locX, locZ - 1); ++ chunk.world.getChunkAt(locX, locZ + 1); ++ } else if (chunkEdgeMode == ChunkEdgeMode.WAIT) { ++ if (chunkProvider.getChunkAtIfCachedImmediately(locX - 1, locZ) == null || chunkProvider.getChunkAtIfCachedImmediately(locX + 1, locZ) == null || chunkProvider.getChunkAtIfCachedImmediately(locX, locZ - 1) == null || chunkProvider.getChunkAtIfCachedImmediately(locX, locZ + 1) == null) { ++ //Don't create the chunk packet now, wait until nearby chunks are loaded and create it later ++ return false; ++ } ++ } else if (false && chunkEdgeMode == ChunkEdgeMode.LOAD) { ++ // TODO Note: These should be asynchronous loads; however we have no such thing in 1.14. ++ boolean missingChunk = false; ++ //noinspection ConstantConditions ++ /* ++ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX - 1, chunk.locZ, true, true, c -> {}) == null; ++ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX + 1, chunk.locZ, true, true, c -> {}) == null; ++ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ - 1, true, true, c -> {}) == null; ++ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ + 1, true, true, c -> {}) == null; ++ */ ++ if (missingChunk) { ++ return false; ++ } ++ } ++ ++ //Create the chunk packet now ++ return true; ++ } ++ ++ @Override ++ public ChunkPacketInfoAntiXray getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { ++ //Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later ++ int locX = chunk.getPos().x; ++ int locZ = chunk.getPos().z; ++ ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this); ++ chunkPacketInfoAntiXray.setNearbyChunks((Chunk)chunk.world.getChunkIfLoadedImmediately(locX - 1, locZ), ++ (Chunk)chunk.world.getChunkIfLoadedImmediately(locX + 1, locZ), ++ (Chunk)chunk.world.getChunkIfLoadedImmediately(locX, locZ - 1), ++ (Chunk)chunk.world.getChunkIfLoadedImmediately(locX, locZ + 1)); ++ return chunkPacketInfoAntiXray; ++ } ++ ++ @Override ++ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { ++ if (asynchronous) { ++ executorService.submit((ChunkPacketInfoAntiXray) chunkPacketInfo); ++ } else { ++ obfuscate((ChunkPacketInfoAntiXray) chunkPacketInfo); ++ } ++ } ++ ++ //Actually these fields should be variables inside the obfuscate method but in sync mode or with SingleThreadExecutor in async mode it's okay ++ private int[] predefinedBlockDataBits; ++ private final boolean[] solid = new boolean[Block.REGISTRY_ID.size()]; ++ private final boolean[] obfuscate = new boolean[Block.REGISTRY_ID.size()]; ++ //These boolean arrays represent chunk layers, true means don't obfuscate, false means obfuscate ++ private boolean[][] current = new boolean[16][16]; ++ private boolean[][] next = new boolean[16][16]; ++ private boolean[][] nextNext = new boolean[16][16]; ++ private final DataBitsReader dataBitsReader = new DataBitsReader(); ++ private final DataBitsWriter dataBitsWriter = new DataBitsWriter(); ++ private final ChunkSection[] nearbyChunkSections = new ChunkSection[4]; ++ ++ public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) { ++ boolean[] solidTemp = null; ++ boolean[] obfuscateTemp = null; ++ dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData()); ++ dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData()); ++ int counter = 0; ++ ++ for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) { ++ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) { ++ int[] predefinedBlockDataBitsTemp; ++ ++ if (chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex) == ChunkSection.GLOBAL_PALETTE) { ++ predefinedBlockDataBitsTemp = engineMode == EngineMode.HIDE ? chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.NETHER ? predefinedBlockDataBitsNetherrackGlobal : chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.THE_END ? predefinedBlockDataBitsEndStoneGlobal : predefinedBlockDataBitsStoneGlobal : predefinedBlockDataBitsGlobal; ++ } else { ++ predefinedBlockDataBitsTemp = predefinedBlockDataBits == null ? predefinedBlockDataBits = engineMode == EngineMode.HIDE ? new int[1] : new int[predefinedBlockData.length] : predefinedBlockDataBits; ++ ++ for (int i = 0; i < predefinedBlockDataBitsTemp.length; i++) { ++ predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).getOrCreateIdFor(chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex)[i]); ++ } ++ } ++ ++ dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); ++ ++ //Check if the chunk section below was not obfuscated ++ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex - 1) == null) { ++ //If so, initialize some stuff ++ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); ++ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); ++ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), solid, solidGlobal); ++ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), obfuscate, obfuscateGlobal); ++ //Read the blocks of the upper layer of the chunk section below if it exists ++ ChunkSection belowChunkSection = null; ++ boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex - 1]) == Chunk.EMPTY_CHUNK_SECTION; ++ ++ for (int z = 0; z < 16; z++) { ++ for (int x = 0; x < 16; x++) { ++ current[z][x] = true; ++ next[z][x] = skipFirstLayer || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(belowChunkSection.getType(x, 15, z))]; ++ } ++ } ++ ++ //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, counter); ++ } ++ ++ dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); ++ nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex]; ++ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex]; ++ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex]; ++ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex]; ++ ++ //Obfuscate all layers of the current chunk section except the upper one ++ for (int y = 0; y < 15; y++) { ++ boolean[][] temp = current; ++ current = next; ++ next = nextNext; ++ nextNext = temp; ++ counter = obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); ++ } ++ ++ //Check if the chunk section above doesn't need obfuscation ++ if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPredefinedObjects(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 ++ ChunkSection aboveChunkSection; ++ ++ if (chunkSectionIndex != 15 && (aboveChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex + 1]) != Chunk.EMPTY_CHUNK_SECTION) { ++ boolean[][] temp = current; ++ current = next; ++ next = nextNext; ++ nextNext = temp; ++ ++ for (int z = 0; z < 16; z++) { ++ for (int x = 0; x < 16; x++) { ++ if (!solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(aboveChunkSection.getType(x, 0, z))]) { ++ current[z][x] = true; ++ } ++ } ++ } ++ ++ //There is nothing to read anymore ++ dataBitsReader.setBitsPerObject(0); ++ solid[0] = true; ++ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); ++ } ++ } 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.getOrCreateIdForIndex(chunkSectionIndex + 1)); ++ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), solid, solidGlobal); ++ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal); ++ boolean[][] temp = current; ++ current = next; ++ next = nextNext; ++ nextNext = temp; ++ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); ++ } ++ ++ dataBitsWriter.finish(); ++ } ++ } ++ ++ chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true); ++ } ++ ++ private int obfuscateLayer(int y, DataBitsReader dataBitsReader, DataBitsWriter dataBitsWriter, boolean[] solid, boolean[] obfuscate, int[] predefinedBlockDataBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, ChunkSection[] nearbyChunkSections, int counter) { ++ //First block of first line ++ int dataBits = dataBitsReader.read(); ++ ++ if (nextNext[0][0] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[0][1] = true; ++ next[1][0] = true; ++ } else { ++ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(0, y, 15))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 0))] || current[0][0]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[0][0] = true; ++ } ++ ++ //First line ++ for (int x = 1; x < 15; x++) { ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[0][x] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[0][x - 1] = true; ++ next[0][x + 1] = true; ++ next[1][x] = true; ++ } else { ++ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(x, y, 15))] || current[0][x]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[0][x] = true; ++ } ++ } ++ ++ //Last block of first line ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[0][15] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[0][14] = true; ++ next[1][15] = true; ++ } else { ++ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(15, y, 15))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 0))] || current[0][15]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[0][15] = true; ++ } ++ ++ //All inner lines ++ for (int z = 1; z < 15; z++) { ++ //First block ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[z][0] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[z][1] = true; ++ next[z - 1][0] = true; ++ next[z + 1][0] = true; ++ } else { ++ if (nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, z))] || current[z][0]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[z][0] = true; ++ } ++ ++ //All inner blocks ++ for (int x = 1; x < 15; x++) { ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[z][x] = !solid[dataBits]) { ++ dataBitsWriter.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(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[z][x] = true; ++ } ++ } ++ ++ //Last block ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[z][15] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[z][14] = true; ++ next[z - 1][15] = true; ++ next[z + 1][15] = true; ++ } else { ++ if (nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, z))] || current[z][15]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[z][15] = true; ++ } ++ } ++ ++ //First block of last line ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[15][0] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[15][1] = true; ++ next[14][0] = true; ++ } else { ++ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(0, y, 0))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 15))] || current[15][0]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[15][0] = true; ++ } ++ ++ //Last line ++ for (int x = 1; x < 15; x++) { ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[15][x] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[15][x - 1] = true; ++ next[15][x + 1] = true; ++ next[14][x] = true; ++ } else { ++ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(x, y, 0))] || current[15][x]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[15][x] = true; ++ } ++ } ++ ++ //Last block of last line ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[15][15] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[15][14] = true; ++ next[14][15] = true; ++ } else { ++ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(15, y, 0))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 15))] || current[15][15]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[15][15] = true; ++ } ++ ++ return counter; ++ } ++ ++ private boolean[] readDataPalette(DataPalette dataPalette, boolean[] temp, boolean[] global) { ++ if (dataPalette == ChunkSection.GLOBAL_PALETTE) { ++ return global; ++ } ++ ++ IBlockData blockData; ++ ++ for (int i = 0; (blockData = dataPalette.getObject(i)) != null; i++) { ++ temp[i] = global[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)]; ++ } ++ ++ return temp; ++ } ++ ++ @Override ++ public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) { ++ if (oldBlockData != null && solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(oldBlockData)] && !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(newBlockData)] && blockPosition.getY() <= maxBlockYUpdatePosition) { ++ updateNearbyBlocks(world, blockPosition); ++ } ++ } ++ ++ @Override ++ public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) { ++ if (blockPosition.getY() <= maxBlockYUpdatePosition) { ++ updateNearbyBlocks(playerInteractManager.world, blockPosition); ++ } ++ } ++ ++ private void updateNearbyBlocks(World world, BlockPosition blockPosition) { ++ if (updateRadius >= 2) { ++ BlockPosition temp = blockPosition.west(); ++ updateBlock(world, temp); ++ updateBlock(world, temp.west()); ++ updateBlock(world, temp.down()); ++ updateBlock(world, temp.up()); ++ updateBlock(world, temp.north()); ++ updateBlock(world, temp.south()); ++ updateBlock(world, temp = blockPosition.east()); ++ updateBlock(world, temp.east()); ++ updateBlock(world, temp.down()); ++ updateBlock(world, temp.up()); ++ updateBlock(world, temp.north()); ++ updateBlock(world, temp.south()); ++ updateBlock(world, temp = blockPosition.down()); ++ updateBlock(world, temp.down()); ++ updateBlock(world, temp.north()); ++ updateBlock(world, temp.south()); ++ updateBlock(world, temp = blockPosition.up()); ++ updateBlock(world, temp.up()); ++ 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()); ++ } else if (updateRadius == 1) { ++ updateBlock(world, blockPosition.west()); ++ updateBlock(world, blockPosition.east()); ++ updateBlock(world, blockPosition.down()); ++ updateBlock(world, blockPosition.up()); ++ updateBlock(world, blockPosition.north()); ++ updateBlock(world, blockPosition.south()); ++ } else { ++ //Do nothing if updateRadius <= 0 (test mode) ++ } ++ } ++ ++ private void updateBlock(World world, BlockPosition blockPosition) { ++ IBlockData blockData = world.getTypeIfLoaded(blockPosition); ++ ++ if (blockData != null && obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)]) { ++ //world.notify(blockPosition, blockData, blockData, 3); ++ ((WorldServer)world).getChunkProvider().flagDirty(blockPosition); // We only need to re-send to client ++ } ++ } ++ ++ public enum EngineMode { ++ ++ HIDE(1, "hide ores"), ++ OBFUSCATE(2, "obfuscate"); ++ ++ private final int id; ++ private final String description; ++ ++ EngineMode(int id, String description) { ++ this.id = id; ++ this.description = description; ++ } ++ ++ public static EngineMode getById(int id) { ++ for (EngineMode engineMode : values()) { ++ if (engineMode.id == id) { ++ return engineMode; ++ } ++ } ++ ++ return null; ++ } ++ ++ public int getId() { ++ return id; ++ } ++ ++ public String getDescription() { ++ return description; ++ } ++ } ++ ++ public enum ChunkEdgeMode { ++ ++ DEFAULT(1, "default"), ++ WAIT(2, "wait until nearby chunks are loaded"), ++ LOAD(3, "load nearby chunks"); ++ ++ private final int id; ++ private final String description; ++ ++ ChunkEdgeMode(int id, String description) { ++ this.id = id; ++ this.description = description; ++ } ++ ++ public static ChunkEdgeMode getById(int id) { ++ for (ChunkEdgeMode chunkEdgeMode : values()) { ++ if (chunkEdgeMode.id == id) { ++ return chunkEdgeMode; ++ } ++ } ++ ++ return null; ++ } ++ ++ public int getId() { ++ return id; ++ } ++ ++ public String getDescription() { ++ return description; ++ } ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java +new file mode 100644 +index 0000000000..a68bace353 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java +@@ -0,0 +1,81 @@ ++package com.destroystokyo.paper.antixray; ++ ++import net.minecraft.server.Chunk; ++import net.minecraft.server.DataPalette; ++import net.minecraft.server.PacketPlayOutMapChunk; ++ ++public class ChunkPacketInfo { ++ ++ private final PacketPlayOutMapChunk packetPlayOutMapChunk; ++ private final Chunk chunk; ++ private final int chunkSectionSelector; ++ private byte[] data; ++ private final int[] bitsPerObject = new int[16]; ++ private final Object[] dataPalettes = new Object[16]; ++ private final int[] dataBitsIndexes = new int[16]; ++ private final Object[][] predefinedObjects = new Object[16][]; ++ ++ public ChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { ++ this.packetPlayOutMapChunk = packetPlayOutMapChunk; ++ this.chunk = chunk; ++ this.chunkSectionSelector = chunkSectionSelector; ++ } ++ ++ public PacketPlayOutMapChunk getPacketPlayOutMapChunk() { ++ return packetPlayOutMapChunk; ++ } ++ ++ public Chunk getChunk() { ++ return chunk; ++ } ++ ++ public int getChunkSectionSelector() { ++ return chunkSectionSelector; ++ } ++ ++ public byte[] getData() { ++ return data; ++ } ++ ++ public void setData(byte[] data) { ++ this.data = data; ++ } ++ ++ public int getBitsPerObject(int chunkSectionIndex) { ++ return bitsPerObject[chunkSectionIndex]; ++ } ++ ++ public void setBitsPerObject(int chunkSectionIndex, int bitsPerObject) { ++ this.bitsPerObject[chunkSectionIndex] = bitsPerObject; ++ } ++ ++ @SuppressWarnings("unchecked") ++ public DataPalette getDataPalette(int chunkSectionIndex) { ++ return (DataPalette) dataPalettes[chunkSectionIndex]; ++ } ++ ++ public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) { ++ dataPalettes[chunkSectionIndex] = dataPalette; ++ } ++ ++ public int getOrCreateIdForIndex(int chunkSectionIndex) { ++ return dataBitsIndexes[chunkSectionIndex]; ++ } ++ ++ public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) { ++ dataBitsIndexes[chunkSectionIndex] = dataBitsIndex; ++ } ++ ++ @SuppressWarnings("unchecked") ++ public T[] getPredefinedObjects(int chunkSectionIndex) { ++ return (T[]) predefinedObjects[chunkSectionIndex]; ++ } ++ ++ public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) { ++ this.predefinedObjects[chunkSectionIndex] = predefinedObjects; ++ } ++ ++ public boolean isWritten(int chunkSectionIndex) { ++ return bitsPerObject[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 0000000000..e255a45fa3 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java +@@ -0,0 +1,29 @@ ++package com.destroystokyo.paper.antixray; ++ ++import net.minecraft.server.Chunk; ++import net.minecraft.server.IBlockData; ++import net.minecraft.server.PacketPlayOutMapChunk; ++ ++public class ChunkPacketInfoAntiXray extends ChunkPacketInfo implements Runnable { ++ ++ private Chunk[] nearbyChunks; ++ private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray; ++ ++ public ChunkPacketInfoAntiXray(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) { ++ super(packetPlayOutMapChunk, chunk, chunkSectionSelector); ++ this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray; ++ } ++ ++ public Chunk[] getNearbyChunks() { ++ return nearbyChunks; ++ } ++ ++ public void setNearbyChunks(Chunk... nearbyChunks) { ++ this.nearbyChunks = nearbyChunks; ++ } ++ ++ @Override ++ public void run() { ++ 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 0000000000..cc586827aa +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java +@@ -0,0 +1,56 @@ ++package com.destroystokyo.paper.antixray; ++ ++public 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() { ++ int value = (int) (current >>> bitInLongIndex) & mask; ++ bitInLongIndex += bitsPerObject; ++ ++ if (bitInLongIndex > 63) { ++ bitInLongIndex -= 64; ++ longInDataBitsIndex += 8; ++ init(); ++ ++ if (bitInLongIndex > 0) { ++ value |= current << bitsPerObject - bitInLongIndex & mask; ++ } ++ } ++ ++ 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 0000000000..37093419cf +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java +@@ -0,0 +1,84 @@ ++package com.destroystokyo.paper.antixray; ++ ++public 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) { ++ current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex; ++ dirty = true; ++ bitInLongIndex += bitsPerObject; ++ ++ if (bitInLongIndex > 63) { ++ finish(); ++ bitInLongIndex -= 64; ++ longInDataBitsIndex += 8; ++ init(); ++ ++ if (bitInLongIndex > 0) { ++ current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex; ++ dirty = true; ++ } ++ } ++ } ++ ++ public void skip() { ++ bitInLongIndex += bitsPerObject; ++ ++ if (bitInLongIndex > 63) { ++ finish(); ++ bitInLongIndex -= 64; ++ longInDataBitsIndex += 8; ++ init(); ++ } ++ } ++} +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index e2a48695df..d19412f186 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -315,7 +315,7 @@ public class Chunk implements IChunkAccess { + return null; + } + +- chunksection = new ChunkSection(j >> 4 << 4); ++ chunksection = new ChunkSection(j >> 4 << 4, this, this.world, true); // Paper - Anti-Xray + this.sections[j >> 4] = chunksection; + } + +diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +index 287f113581..f88e3d957f 100644 +--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java ++++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +@@ -74,7 +74,7 @@ public class ChunkRegionLoader { + byte b0 = nbttagcompound2.getByte("Y"); + + if (nbttagcompound2.hasKeyOfType("Palette", 9) && nbttagcompound2.hasKeyOfType("BlockStates", 12)) { +- ChunkSection chunksection = new ChunkSection(b0 << 4); ++ ChunkSection chunksection = new ChunkSection(b0 << 4, null, worldserver, false); // Paper - Anti-Xray + + chunksection.getBlocks().a(nbttagcompound2.getList("Palette", 10), nbttagcompound2.getLongArray("BlockStates")); + chunksection.recalcBlockCounts(); +@@ -132,7 +132,7 @@ public class ChunkRegionLoader { + loadEntities(nbttagcompound1, chunk); + }); + } else { +- ProtoChunk protochunk = new ProtoChunk(chunkcoordintpair, chunkconverter, achunksection, protochunkticklist, protochunkticklist1); ++ ProtoChunk protochunk = new ProtoChunk(chunkcoordintpair, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, worldserver); // Paper - Anti-Xray + + object = protochunk; + protochunk.a(abiomebase); +diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java +index c4c181c1d5..d6b327eff2 100644 +--- a/src/main/java/net/minecraft/server/ChunkSection.java ++++ b/src/main/java/net/minecraft/server/ChunkSection.java +@@ -6,21 +6,31 @@ public class ChunkSection { + + public static final DataPalette GLOBAL_PALETTE = new DataPaletteGlobal<>(Block.REGISTRY_ID, Blocks.AIR.getBlockData()); + private final int yPos; +- private short nonEmptyBlockCount; ++ short nonEmptyBlockCount; // Paper - private -> package-private + private short tickingBlockCount; + private short e; + final DataPaletteBlock blockIds; // Paper - package + + public ChunkSection(int i) { +- this(i, (short) 0, (short) 0, (short) 0); ++ // Paper start - add parameters ++ this(i, (IChunkAccess)null, (IWorldReader)null, true); ++ } ++ public ChunkSection(int i, IChunkAccess chunk, IWorldReader world, boolean initializeBlocks) { ++ this(i, (short) 0, (short) 0, (short) 0, chunk, world, initializeBlocks); ++ // Paper end + } + + public ChunkSection(int i, short short0, short short1, short short2) { ++ // Paper start - add parameters ++ this(i, short0, short1, short2, (IChunkAccess)null, (IWorldReader)null, true); ++ } ++ public ChunkSection(int i, short short0, short short1, short short2, IChunkAccess chunk, IWorldReader world, boolean initializeBlocks) { ++ // Paper end + this.yPos = i; + this.nonEmptyBlockCount = short0; + this.tickingBlockCount = short1; + this.e = short2; +- this.blockIds = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData()); ++ this.blockIds = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData(), world instanceof GeneratorAccess ? ((GeneratorAccess) world).getMinecraftWorld().chunkPacketBlockController.getPredefinedBlockData(world, chunk, this, initializeBlocks) : null, initializeBlocks); // Paper - Anti-Xray - Add predefined block data + } + + public IBlockData getType(int i, int j, int k) { +diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java +index e05b9d606a..cbc9dc902e 100644 +--- a/src/main/java/net/minecraft/server/DataPaletteBlock.java ++++ b/src/main/java/net/minecraft/server/DataPaletteBlock.java +@@ -1,5 +1,6 @@ + package net.minecraft.server; + ++import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray + import java.util.Arrays; + import java.util.Objects; + import java.util.concurrent.locks.ReentrantLock; +@@ -16,6 +17,7 @@ public class DataPaletteBlock implements DataPaletteExpandable { + private final Function e; + private final Function f; + private final T g; ++ private final T[] predefinedObjects; // Paper - Anti-Xray - Add predefined objects + protected DataBits a; protected DataBits getDataBits() { return this.a; } // Paper - OBFHELPER + private DataPalette h; private DataPalette getDataPalette() { return this.h; } // Paper - OBFHELPER + private int i; private int getBitsPerObject() { return this.i; } // Paper - OBFHELPER +@@ -44,14 +46,50 @@ public class DataPaletteBlock implements DataPaletteExpandable { + } + + public DataPaletteBlock(DataPalette datapalette, RegistryBlockID registryblockid, Function function, Function function1, T t0) { ++ // Paper start - Anti-Xray - Support default constructor ++ this(datapalette, registryblockid, function, function1, t0, null, true); ++ } ++ ++ public DataPaletteBlock(DataPalette datapalette, RegistryBlockID registryblockid, Function function, Function function1, T t0, T[] predefinedObjects, boolean initialize) { ++ // Paper end - Anti-Xray - Add predefined objects + this.b = datapalette; + this.d = registryblockid; + this.e = function; + this.f = function1; + this.g = t0; +- this.b(4); ++ // Paper start - Anti-Xray - Add predefined objects ++ this.predefinedObjects = predefinedObjects; ++ ++ if (initialize) { ++ if (predefinedObjects == null) { ++ // Default ++ this.initialize(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.initialize((1 << bitCount) - predefinedObjects.length < 16 ? bitCount + 1 : bitCount); ++ this.addPredefinedObjects(); ++ } ++ } ++ // Paper end + } + ++ // Paper start - Anti-Xray - Add predefined objects ++ private void addPredefinedObjects() { ++ if (this.predefinedObjects != null && this.getDataPalette() != this.getDataPaletteGlobal()) { ++ for (int i = 0; i < this.predefinedObjects.length; i++) { ++ this.getDataPalette().getOrCreateIdFor(this.predefinedObjects[i]); ++ } ++ } ++ } ++ // Paper end ++ + private static int b(int i, int j, int k) { + return j << 8 | k << 4 | i; + } +@@ -85,6 +123,7 @@ public class DataPaletteBlock implements DataPaletteExpandable { + + int j; + ++ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects + for (j = 0; j < databits.b(); ++j) { + T t1 = datapalette.a(databits.a(j)); + +@@ -136,22 +175,39 @@ public class DataPaletteBlock implements DataPaletteExpandable { + + public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } // Paper - OBFHELPER + public void b(PacketDataSerializer packetdataserializer) { ++ // Paper start - add parameters ++ this.writeDataPaletteBlock(packetdataserializer, null, 0); ++ } ++ public void writeDataPaletteBlock(PacketDataSerializer packetdataserializer, ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { ++ // Paper end + this.a(); + packetdataserializer.writeByte(this.i); + this.h.b(packetdataserializer); ++ ++ // Paper start - Anti-Xray - Add chunk packet info ++ if (chunkPacketInfo != null) { ++ chunkPacketInfo.setBitsPerObject(chunkSectionIndex, this.getBitsPerObject()); ++ chunkPacketInfo.setDataPalette(chunkSectionIndex, this.getDataPalette()); ++ chunkPacketInfo.setDataBitsIndex(chunkSectionIndex, packetdataserializer.writerIndex() + PacketDataSerializer.countBytes(this.getDataBits().getDataBits().length)); ++ chunkPacketInfo.setPredefinedObjects(chunkSectionIndex, this.predefinedObjects); ++ } ++ // Paper end ++ + packetdataserializer.a(this.a.a()); + this.b(); + } + + public void a(NBTTagList nbttaglist, long[] along) { + this.a(); +- int i = Math.max(4, MathHelper.d(nbttaglist.size())); ++ // 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, MathHelper.d(nbttaglist.size() + (this.predefinedObjects == null ? 0 : this.predefinedObjects.length))); // Paper - Anti-Xray - Calculate the size with predefined objects + +- if (i != this.i) { ++ if (true || i != this.i) { // Paper - Anti-Xray - Not initialized yet + this.b(i); + } + + this.h.a(nbttaglist); ++ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects + int j = along.length * 64 / 4096; + + if (this.h == this.b) { +diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java +index ba23d28335..8dcaaf8676 100644 +--- a/src/main/java/net/minecraft/server/NetworkManager.java ++++ b/src/main/java/net/minecraft/server/NetworkManager.java +@@ -166,8 +166,8 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + } + + public void sendPacket(Packet packet, @Nullable GenericFutureListener> genericfuturelistener) { +- if (this.isConnected()) { +- this.o(); ++ if (this.isConnected() && this.sendPacketQueue() && !(packet instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) packet).isReady())) { // Paper - Async-Anti-Xray - Add chunk packets which are not ready or all packets if the packet queue contains chunk packets which are not ready to the packet queue and send the packets later in the right order ++ //this.o(); // Paper - Async-Anti-Xray - Move to if statement (this.sendPacketQueue()) + this.b(packet, genericfuturelistener); + } else { + this.j.writeLock().lock(); +@@ -231,23 +231,38 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + + } + +- private void sendPacketQueue() { this.o(); } // Paper - OBFHELPER +- private void o() { ++ // Paper start - Async-Anti-Xray - Stop dispatching further packets and return false if the peeked packet is a chunk packet which is not ready ++ private boolean sendPacketQueue() { return this.o(); } // OBFHELPER // void -> boolean ++ private boolean o() { // void -> boolean + if (this.channel != null && this.channel.isOpen()) { +- this.j.readLock().lock(); ++ if (this.packetQueue.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all ++ return true; ++ } ++ ++ this.j.writeLock().lock(); // readLock -> writeLock (because of race condition between peek and poll) + + try { + while (!this.packetQueue.isEmpty()) { +- NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.packetQueue.poll(); +- +- this.b(networkmanager_queuedpacket.a, networkmanager_queuedpacket.b); ++ NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.getPacketQueue().peek(); // poll -> peek ++ ++ if (networkmanager_queuedpacket != null) { // Fix NPE (Spigot bug caused by handleDisconnection()) ++ if (networkmanager_queuedpacket.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) networkmanager_queuedpacket.getPacket()).isReady()) { // Check if the peeked packet is a chunk packet which is not ready ++ return false; // Return false if the peeked packet is a chunk packet which is not ready ++ } else { ++ this.getPacketQueue().poll(); // poll here ++ this.dispatchPacket(networkmanager_queuedpacket.getPacket(), networkmanager_queuedpacket.getGenericFutureListener()); // dispatch the packet ++ } ++ } + } + } finally { +- this.j.readLock().unlock(); ++ this.j.writeLock().unlock(); // readLock -> writeLock (because of race condition between peek and poll) + } + + } ++ ++ return true; // Return true if all packets were dispatched + } ++ // Paper end + + public void a() { + this.o(); +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java +index ef71a1feb3..316fb7cc3c 100644 +--- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java ++++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java +@@ -1,5 +1,6 @@ + package net.minecraft.server; + ++import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray + import com.google.common.collect.Lists; + import io.netty.buffer.ByteBuf; + import io.netty.buffer.Unpooled; +@@ -17,8 +18,11 @@ public class PacketPlayOutMapChunk implements Packet { + private byte[] e; private byte[] getData() { return this.e; } // Paper - OBFHELPER + private List f; + private boolean g; ++ private volatile boolean ready; // Paper - Async-Anti-Xray - Ready flag for the network manager + +- public PacketPlayOutMapChunk() {} ++ public PacketPlayOutMapChunk() { ++ this.ready = true; // Paper - Async-Anti-Xray - Set the ready flag to true ++ } + + // Paper start + private final java.util.List extraPackets = new java.util.ArrayList<>(); +@@ -30,6 +34,7 @@ public class PacketPlayOutMapChunk implements Packet { + } + // Paper end + public PacketPlayOutMapChunk(Chunk chunk, int i) { ++ ChunkPacketInfo chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info + ChunkCoordIntPair chunkcoordintpair = chunk.getPos(); + + this.a = chunkcoordintpair.x; +@@ -48,7 +53,12 @@ public class PacketPlayOutMapChunk implements Packet { + } + + this.e = new byte[this.a(chunk, i)]; +- this.c = this.a(new PacketDataSerializer(this.i()), chunk, i); ++ // Paper start - Anti-Xray - Add chunk packet info ++ if (chunkPacketInfo != null) { ++ chunkPacketInfo.setData(this.getData()); ++ } ++ // Paper end ++ this.c = this.writeChunk(new PacketDataSerializer(this.i()), chunk, i, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info + this.f = Lists.newArrayList(); + iterator = chunk.getTileEntities().entrySet().iterator(); + int totalSigns = 0; // Paper +@@ -74,9 +84,19 @@ public class PacketPlayOutMapChunk implements Packet { + this.f.add(nbttagcompound); + } + } ++ chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks ++ } + ++ // Paper start - Async-Anti-Xray - Getter and Setter for the ready flag ++ public boolean isReady() { ++ return this.ready; + } + ++ public void setReady(boolean ready) { ++ this.ready = ready; ++ } ++ // Paper end ++ + @Override + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.readInt(); +@@ -135,6 +155,11 @@ public class PacketPlayOutMapChunk implements Packet { + + public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, chunkSectionSelector); } // Paper - OBFHELPER + public int a(PacketDataSerializer packetdataserializer, Chunk chunk, int i) { ++ // Paper start - Add parameter ++ return this.writeChunk(packetdataserializer, chunk, i, null); ++ } ++ public int writeChunk(PacketDataSerializer packetdataserializer, Chunk chunk, int i, ChunkPacketInfo chunkPacketInfo) { ++ // Paper end + int j = 0; + ChunkSection[] achunksection = chunk.getSections(); + int k = 0; +@@ -146,7 +171,8 @@ public class PacketPlayOutMapChunk implements Packet { + + if (chunksection != Chunk.a && (!this.f() || !chunksection.c()) && (i & 1 << k) != 0) { + j |= 1 << k; +- chunksection.b(packetdataserializer); ++ packetdataserializer.writeShort(chunksection.nonEmptyBlockCount); // Paper - Anti-Xray - Add chunk packet info ++ chunksection.getBlocks().writeDataPaletteBlock(packetdataserializer, chunkPacketInfo, k); // Paper - Anti-Xray - Add chunk packet info + } + } + +diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java +index 761cd1355b..cb3363a01e 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunk.java ++++ b/src/main/java/net/minecraft/server/PlayerChunk.java +@@ -150,6 +150,9 @@ public class PlayerChunk { + } + + public void a(Chunk chunk) { ++ if (!chunk.world.chunkPacketBlockController.onChunkPacketCreate(chunk, '\uffff', false)) { // Paper - Anti-Xray - Load nearby chunks if necessary ++ return; ++ } + if (this.dirtyCount != 0 || this.u != 0 || this.t != 0) { + World world = chunk.getWorld(); + +diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java +index 8e16d6ac87..5e0c73ece6 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java ++++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java +@@ -491,7 +491,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + PlayerChunkMap.LOGGER.error("Couldn't load chunk {}", chunkcoordintpair, exception); + } + +- return Either.left(new ProtoChunk(chunkcoordintpair, ChunkConverter.a)); ++ return Either.left(new ProtoChunk(chunkcoordintpair, ChunkConverter.a, this.world)); // Paper - Anti-Xray + }, this.executor); + } + +@@ -1111,6 +1111,8 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + + private void a(EntityPlayer entityplayer, Packet[] apacket, Chunk chunk) { + if (apacket[0] == null) { ++ // Note that this is ALWAYS the case as of 1.14 (at least once). re-check on update ++ chunk.world.chunkPacketBlockController.onChunkPacketCreate(chunk, '\uffff', true); // Paper - Anti-Xray - Load nearby chunks if necessary + apacket[0] = new PacketPlayOutMapChunk(chunk, 65535); + apacket[1] = new PacketPlayOutLightUpdate(chunk.getPos(), this.lightEngine); + } +diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java +index 83b36b3e7f..8fef6008d1 100644 +--- a/src/main/java/net/minecraft/server/PlayerInteractManager.java ++++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java +@@ -207,6 +207,8 @@ public class PlayerInteractManager { + } + + } ++ ++ this.world.chunkPacketBlockController.onPlayerLeftClickBlock(this, blockposition, enumdirection); // Paper - Anti-Xray + } + + public void a(BlockPosition blockposition) { +diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java +index 6bdd7dda04..7bad12eb00 100644 +--- a/src/main/java/net/minecraft/server/ProtoChunk.java ++++ b/src/main/java/net/minecraft/server/ProtoChunk.java +@@ -44,16 +44,28 @@ public class ProtoChunk implements IChunkAccess { + private long s; + private final Map t; + private volatile boolean u; ++ private final GeneratorAccess world; // Paper - Anti-Xray + + public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter) { ++ // Paper start - add world parameter ++ this(chunkcoordintpair, chunkconverter, (GeneratorAccess)null); ++ } ++ public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, GeneratorAccess world) { ++ // Paper end + this(chunkcoordintpair, chunkconverter, (ChunkSection[]) null, new ProtoChunkTickList<>((block) -> { + return block == null || block.getBlockData().isAir(); + }, chunkcoordintpair), new ProtoChunkTickList<>((fluidtype) -> { + return fluidtype == null || fluidtype == FluidTypes.EMPTY; +- }, chunkcoordintpair)); ++ }, chunkcoordintpair), world); // Paper - add world parameter + } + + public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, @Nullable ChunkSection[] achunksection, ProtoChunkTickList protochunkticklist, ProtoChunkTickList protochunkticklist1) { ++ // Paper start - add world parameter ++ this(chunkcoordintpair, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, (GeneratorAccess)null); ++ } ++ public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, @Nullable ChunkSection[] achunksection, ProtoChunkTickList protochunkticklist, ProtoChunkTickList protochunkticklist1, GeneratorAccess world) { ++ this.world = world; ++ // Paper end + this.f = Maps.newEnumMap(HeightMap.Type.class); + this.g = ChunkStatus.EMPTY; + this.h = Maps.newHashMap(); +@@ -206,7 +218,7 @@ public class ProtoChunk implements IChunkAccess { + + public ChunkSection a(int i) { + if (this.j[i] == Chunk.a) { +- this.j[i] = new ChunkSection(i << 4); ++ this.j[i] = new ChunkSection(i << 4, this, this.world, true); // Paper - Anti-Xray + } + + return this.j[i]; +diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java +index a7a35d6a6f..ff64089857 100644 +--- a/src/main/java/net/minecraft/server/World.java ++++ b/src/main/java/net/minecraft/server/World.java +@@ -2,6 +2,8 @@ package net.minecraft.server; + + import co.aikar.timings.Timing; + import co.aikar.timings.Timings; ++import com.destroystokyo.paper.antixray.ChunkPacketBlockController; // Paper - Anti-Xray ++import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray; // Paper - Anti-Xray + import com.destroystokyo.paper.event.server.ServerExceptionEvent; + import com.destroystokyo.paper.exception.ServerInternalException; + import com.google.common.base.MoreObjects; +@@ -93,6 +95,7 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose + public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot + + public final com.destroystokyo.paper.PaperWorldConfig paperConfig; // Paper ++ public final ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray + + public final co.aikar.timings.WorldTimingsHandler timings; // Paper + public static BlockPosition lastPhysicsProblem; // Spigot +@@ -134,6 +137,7 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose + protected World(WorldData worlddata, DimensionManager dimensionmanager, BiFunction bifunction, GameProfilerFiller gameprofilerfiller, boolean flag, org.bukkit.generator.ChunkGenerator gen, org.bukkit.World.Environment env) { + this.spigotConfig = new org.spigotmc.SpigotWorldConfig( worlddata.getName() ); // Spigot + this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(worlddata.getName(), this.spigotConfig); // Paper ++ this.chunkPacketBlockController = this.paperConfig.antiXray ? new ChunkPacketBlockControllerAntiXray(this.paperConfig) : ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray + this.generator = gen; + this.world = new CraftWorld((WorldServer) this, gen, env); + this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit +@@ -414,6 +418,7 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose + // CraftBukkit end + + IBlockData iblockdata1 = chunk.setType(blockposition, iblockdata, (i & 64) != 0, (i & 1024) == 0); // CraftBukkit custom NO_PLACE flag ++ this.chunkPacketBlockController.onBlockChange(this, blockposition, iblockdata, iblockdata1, i); // Paper - Anti-Xray + + if (iblockdata1 == null) { + // CraftBukkit start - remove blockstate if failed +diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java +index 7772d59005..4570ed9991 100644 +--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java ++++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java +@@ -21,9 +21,11 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { + private final int maxHeight; + private final ChunkSection[] sections; + private Set tiles; ++ private World world; // Paper - Anti-Xray + + public CraftChunkData(World world) { + this(world.getMaxHeight()); ++ this.world = world; // Paper - Anti-Xray + } + + /* pp for tests */ CraftChunkData(int maxHeight) { +@@ -157,7 +159,7 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { + private ChunkSection getChunkSection(int y, boolean create) { + ChunkSection section = sections[y >> 4]; + if (create && section == null) { +- sections[y >> 4] = section = new ChunkSection(y); ++ sections[y >> 4] = section = new ChunkSection(y, null, world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) world).getHandle() : null, true); // Paper - Anti-Xray + } + return section; + } +-- +2.21.0 + From ad757f967d110a57b71d6b7207e766b50ca85e0d Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Tue, 25 Jun 2019 22:14:23 -0700 Subject: [PATCH 2/8] Delete 0335-Anti-Xray.patch --- removed/1.14/0335-Anti-Xray.patch | 1600 ----------------------------- 1 file changed, 1600 deletions(-) delete mode 100644 removed/1.14/0335-Anti-Xray.patch diff --git a/removed/1.14/0335-Anti-Xray.patch b/removed/1.14/0335-Anti-Xray.patch deleted file mode 100644 index ec56316cbf..0000000000 --- a/removed/1.14/0335-Anti-Xray.patch +++ /dev/null @@ -1,1600 +0,0 @@ -From b6acf5d97d05f6b9d8c544a1760eafd5643cc027 Mon Sep 17 00:00:00 2001 -From: stonar96 -Date: Mon, 20 Aug 2018 03:03:58 +0200 -Subject: [PATCH] Anti-Xray - - -diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index e87fb94c06..8f49262226 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -@@ -1,7 +1,10 @@ - package com.destroystokyo.paper; - -+import java.util.Arrays; - import java.util.List; - -+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.ChunkEdgeMode; -+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode; - import net.minecraft.server.MinecraftServer; - import org.bukkit.Bukkit; - import org.bukkit.configuration.file.YamlConfiguration; -@@ -522,4 +525,34 @@ public class PaperWorldConfig { - this.armorStandTick = this.getBoolean("armor-stands-tick", this.armorStandTick); - log("ArmorStand ticking is " + (this.armorStandTick ? "enabled" : "disabled") + " by default"); - } -+ -+ public boolean antiXray; -+ public boolean asynchronous; -+ public EngineMode engineMode; -+ public ChunkEdgeMode chunkEdgeMode; -+ public int maxChunkSectionIndex; -+ public int updateRadius; -+ public List hiddenBlocks; -+ public List replacementBlocks; -+ private void antiXray() { -+ antiXray = getBoolean("anti-xray.enabled", false); -+ asynchronous = true; -+ engineMode = EngineMode.getById(getInt("anti-xray.engine-mode", EngineMode.HIDE.getId())); -+ engineMode = engineMode == null ? EngineMode.HIDE : engineMode; -+ chunkEdgeMode = ChunkEdgeMode.getById(getInt("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId())); -+ chunkEdgeMode = chunkEdgeMode == null ? ChunkEdgeMode.DEFAULT : chunkEdgeMode; -+ -+ if (chunkEdgeMode != ChunkEdgeMode.WAIT) { -+ log("Migrating anti-xray chunk edge mode to " + ChunkEdgeMode.WAIT + " (" + ChunkEdgeMode.WAIT.getId() + ")"); -+ chunkEdgeMode = ChunkEdgeMode.WAIT; -+ set("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId()); -+ } -+ -+ maxChunkSectionIndex = getInt("anti-xray.max-chunk-section-index", 3); -+ maxChunkSectionIndex = maxChunkSectionIndex > 15 ? 15 : maxChunkSectionIndex; -+ updateRadius = getInt("anti-xray.update-radius", 2); -+ hiddenBlocks = getList("anti-xray.hidden-blocks", Arrays.asList("gold_ore", "iron_ore", "coal_ore", "lapis_ore", "mossy_cobblestone", "obsidian", "chest", "diamond_ore", "redstone_ore", "lit_redstone_ore", "clay", "emerald_ore", "ender_chest")); -+ replacementBlocks = getList("anti-xray.replacement-blocks", Arrays.asList("stone", "planks")); -+ log("Anti-Xray: " + (antiXray ? "enabled" : "disabled") + " / Engine Mode: " + engineMode.getDescription() + " / Chunk Edge Mode: " + chunkEdgeMode.getDescription() + " / Up to " + ((maxChunkSectionIndex + 1) * 16) + " blocks / Update Radius: " + updateRadius); -+ } - } -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 0000000000..1ba8477bf9 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java -@@ -0,0 +1,45 @@ -+package com.destroystokyo.paper.antixray; -+ -+import net.minecraft.server.BlockPosition; -+import net.minecraft.server.Chunk; -+import net.minecraft.server.ChunkSection; -+import net.minecraft.server.EnumDirection; -+import net.minecraft.server.IBlockData; -+import net.minecraft.server.IChunkAccess; -+import net.minecraft.server.IWorldReader; -+import net.minecraft.server.PacketPlayOutMapChunk; -+import net.minecraft.server.PlayerInteractManager; -+import net.minecraft.server.World; -+ -+public class ChunkPacketBlockController { -+ -+ public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController(); -+ -+ protected ChunkPacketBlockController() { -+ -+ } -+ -+ public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, boolean initializeBlocks) { -+ return null; -+ } -+ -+ public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { -+ return true; -+ } -+ -+ public ChunkPacketInfo getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { -+ return null; -+ } -+ -+ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { -+ packetPlayOutMapChunk.setReady(true); -+ } -+ -+ public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) { -+ -+ } -+ -+ public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) { -+ -+ } -+} -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 0000000000..e3da35b6ba ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -0,0 +1,684 @@ -+package com.destroystokyo.paper.antixray; -+ -+import java.util.HashSet; -+import java.util.Set; -+import java.util.concurrent.ExecutorService; -+import java.util.concurrent.Executors; -+ -+import net.minecraft.server.IRegistry; -+import net.minecraft.server.MinecraftKey; -+import org.bukkit.World.Environment; -+ -+import com.destroystokyo.paper.PaperWorldConfig; -+ -+import net.minecraft.server.Block; -+import net.minecraft.server.BlockPosition; -+import net.minecraft.server.Blocks; -+import net.minecraft.server.Chunk; -+import net.minecraft.server.ChunkSection; -+import net.minecraft.server.DataPalette; -+import net.minecraft.server.EnumDirection; -+import net.minecraft.server.GeneratorAccess; -+import net.minecraft.server.IBlockData; -+import net.minecraft.server.IChunkAccess; -+import net.minecraft.server.IWorldReader; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.PacketPlayOutMapChunk; -+import net.minecraft.server.PlayerInteractManager; -+import net.minecraft.server.World; -+import net.minecraft.server.WorldServer; -+ -+public class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController { -+ -+ private static ExecutorService executorServiceInstance = null; -+ private final ExecutorService executorService; -+ private final boolean asynchronous; -+ private final EngineMode engineMode; -+ private final ChunkEdgeMode chunkEdgeMode; -+ private final int maxChunkSectionIndex; -+ private final int updateRadius; -+ private final IBlockData[] predefinedBlockData; -+ private final IBlockData[] predefinedBlockDataStone; -+ private final IBlockData[] predefinedBlockDataNetherrack; -+ private final IBlockData[] predefinedBlockDataEndStone; -+ private final int[] predefinedBlockDataBitsGlobal; -+ private final int[] predefinedBlockDataBitsStoneGlobal; -+ private final int[] predefinedBlockDataBitsNetherrackGlobal; -+ private final int[] predefinedBlockDataBitsEndStoneGlobal; -+ private final boolean[] solidGlobal = new boolean[Block.REGISTRY_ID.size()]; -+ private final boolean[] obfuscateGlobal = new boolean[Block.REGISTRY_ID.size()]; -+ private final ChunkSection[] emptyNearbyChunkSections = {Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION}; -+ private final int maxBlockYUpdatePosition; -+ -+ public ChunkPacketBlockControllerAntiXray(PaperWorldConfig paperWorldConfig) { -+ asynchronous = paperWorldConfig.asynchronous; -+ engineMode = paperWorldConfig.engineMode; -+ chunkEdgeMode = paperWorldConfig.chunkEdgeMode; -+ maxChunkSectionIndex = paperWorldConfig.maxChunkSectionIndex; -+ updateRadius = paperWorldConfig.updateRadius; -+ -+ if (asynchronous) { -+ executorService = getExecutorServiceInstance(); -+ } else { -+ executorService = null; -+ } -+ -+ if (engineMode == EngineMode.HIDE) { -+ predefinedBlockData = null; -+ predefinedBlockDataStone = new IBlockData[] {Blocks.STONE.getBlockData()}; -+ predefinedBlockDataNetherrack = new IBlockData[] {Blocks.NETHERRACK.getBlockData()}; -+ predefinedBlockDataEndStone = new IBlockData[] {Blocks.END_STONE.getBlockData()}; -+ predefinedBlockDataBitsGlobal = null; -+ predefinedBlockDataBitsStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.STONE.getBlockData())}; -+ predefinedBlockDataBitsNetherrackGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.NETHERRACK.getBlockData())}; -+ predefinedBlockDataBitsEndStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.END_STONE.getBlockData())}; -+ } else { -+ Set predefinedBlockDataSet = new HashSet(); -+ -+ for (String id : paperWorldConfig.hiddenBlocks) { -+ Block block = IRegistry.BLOCK.get(new MinecraftKey(id)); -+ -+ if (block != null && !block.isTileEntity()) { -+ predefinedBlockDataSet.add(block.getBlockData()); -+ } -+ } -+ -+ predefinedBlockData = predefinedBlockDataSet.size() == 0 ? new IBlockData[] {Blocks.DIAMOND_ORE.getBlockData()} : predefinedBlockDataSet.toArray(new IBlockData[predefinedBlockDataSet.size()]); -+ predefinedBlockDataStone = null; -+ predefinedBlockDataNetherrack = null; -+ predefinedBlockDataEndStone = null; -+ predefinedBlockDataBitsGlobal = new int[predefinedBlockData.length]; -+ -+ for (int i = 0; i < predefinedBlockData.length; i++) { -+ predefinedBlockDataBitsGlobal[i] = ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(predefinedBlockData[i]); -+ } -+ -+ predefinedBlockDataBitsStoneGlobal = null; -+ predefinedBlockDataBitsNetherrackGlobal = null; -+ predefinedBlockDataBitsEndStoneGlobal = null; -+ } -+ -+ for (String id : (engineMode == EngineMode.HIDE) ? paperWorldConfig.hiddenBlocks : paperWorldConfig.replacementBlocks) { -+ Block block = IRegistry.BLOCK.get(new MinecraftKey(id)); -+ -+ if (block != null) { -+ obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(block.getBlockData())] = true; -+ } -+ } -+ -+ for (int i = 0; i < solidGlobal.length; i++) { -+ IBlockData blockData = ChunkSection.GLOBAL_PALETTE.getObject(i); -+ -+ if (blockData != null) { -+ solidGlobal[i] = blockData.getBlock().isOccluding(blockData) && blockData.getBlock() != Blocks.SPAWNER && blockData.getBlock() != Blocks.BARRIER; -+ } -+ } -+ -+ this.maxBlockYUpdatePosition = (maxChunkSectionIndex + 1) * 16 + updateRadius - 1; -+ } -+ -+ private static ExecutorService getExecutorServiceInstance() { -+ if (executorServiceInstance == null) { -+ executorServiceInstance = Executors.newSingleThreadExecutor(); -+ } -+ -+ return executorServiceInstance; -+ } -+ -+ @Override -+ public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, 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.getYPosition() >> 4 <= maxChunkSectionIndex) { -+ switch (engineMode) { -+ case HIDE: -+ if (world instanceof GeneratorAccess) { -+ switch (((GeneratorAccess) world).getMinecraftWorld().getWorld().getEnvironment()) { -+ case NETHER: -+ return predefinedBlockDataNetherrack; -+ case THE_END: -+ return predefinedBlockDataEndStone; -+ default: -+ return predefinedBlockDataStone; -+ } -+ } -+ -+ return null; -+ default: -+ return predefinedBlockData; -+ } -+ } -+ -+ return null; -+ } -+ -+ @Override -+ public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { -+ //Load nearby chunks if necessary -+ if (force) { -+ // if forced, load NOW; -+ chunk.world.getChunkAt(chunk.locX - 1, chunk.locZ); -+ chunk.world.getChunkAt(chunk.locX + 1, chunk.locZ); -+ chunk.world.getChunkAt(chunk.locX, chunk.locZ - 1); -+ chunk.world.getChunkAt(chunk.locX, chunk.locZ + 1); -+ } else if (chunkEdgeMode == ChunkEdgeMode.WAIT && !force) { -+ if (chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1) == null) { -+ //Don't create the chunk packet now, wait until nearby chunks are loaded and create it later -+ return false; -+ } -+ } else if (chunkEdgeMode == ChunkEdgeMode.LOAD) { -+ boolean missingChunk = false; -+ //noinspection ConstantConditions -+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX - 1, chunk.locZ, true, true, c -> {}) == null; -+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX + 1, chunk.locZ, true, true, c -> {}) == null; -+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ - 1, true, true, c -> {}) == null; -+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ + 1, true, true, c -> {}) == null; -+ -+ if (missingChunk) { -+ return false; -+ } -+ } -+ -+ //Create the chunk packet now -+ return true; -+ } -+ -+ @Override -+ public ChunkPacketInfoAntiXray getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { -+ //Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later -+ ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this); -+ chunkPacketInfoAntiXray.setNearbyChunks(chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1)); -+ return chunkPacketInfoAntiXray; -+ } -+ -+ @Override -+ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { -+ if (asynchronous) { -+ executorService.submit((ChunkPacketInfoAntiXray) chunkPacketInfo); -+ } else { -+ obfuscate((ChunkPacketInfoAntiXray) chunkPacketInfo); -+ } -+ } -+ -+ //Actually these fields should be variables inside the obfuscate method but in sync mode or with SingleThreadExecutor in async mode it's okay -+ private int[] predefinedBlockDataBits; -+ private final boolean[] solid = new boolean[Block.REGISTRY_ID.size()]; -+ private final boolean[] obfuscate = new boolean[Block.REGISTRY_ID.size()]; -+ //These boolean arrays represent chunk layers, true means don't obfuscate, false means obfuscate -+ private boolean[][] current = new boolean[16][16]; -+ private boolean[][] next = new boolean[16][16]; -+ private boolean[][] nextNext = new boolean[16][16]; -+ private final DataBitsReader dataBitsReader = new DataBitsReader(); -+ private final DataBitsWriter dataBitsWriter = new DataBitsWriter(); -+ private final ChunkSection[] nearbyChunkSections = new ChunkSection[4]; -+ -+ public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) { -+ boolean[] solidTemp = null; -+ boolean[] obfuscateTemp = null; -+ dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData()); -+ dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData()); -+ int counter = 0; -+ -+ for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) { -+ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) { -+ int[] predefinedBlockDataBitsTemp; -+ -+ if (chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex) == ChunkSection.GLOBAL_PALETTE) { -+ predefinedBlockDataBitsTemp = engineMode == EngineMode.HIDE ? chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.NETHER ? predefinedBlockDataBitsNetherrackGlobal : chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.THE_END ? predefinedBlockDataBitsEndStoneGlobal : predefinedBlockDataBitsStoneGlobal : predefinedBlockDataBitsGlobal; -+ } else { -+ predefinedBlockDataBitsTemp = predefinedBlockDataBits == null ? predefinedBlockDataBits = engineMode == EngineMode.HIDE ? new int[1] : new int[predefinedBlockData.length] : predefinedBlockDataBits; -+ -+ for (int i = 0; i < predefinedBlockDataBitsTemp.length; i++) { -+ predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).getOrCreateIdFor(chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex)[i]); -+ } -+ } -+ -+ dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); -+ -+ //Check if the chunk section below was not obfuscated -+ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex - 1) == null) { -+ //If so, initialize some stuff -+ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); -+ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); -+ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), solid, solidGlobal); -+ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), obfuscate, obfuscateGlobal); -+ //Read the blocks of the upper layer of the chunk section below if it exists -+ ChunkSection belowChunkSection = null; -+ boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex - 1]) == Chunk.EMPTY_CHUNK_SECTION; -+ -+ for (int z = 0; z < 16; z++) { -+ for (int x = 0; x < 16; x++) { -+ current[z][x] = true; -+ next[z][x] = skipFirstLayer || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(belowChunkSection.getType(x, 15, z))]; -+ } -+ } -+ -+ //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, counter); -+ } -+ -+ dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); -+ nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex]; -+ -+ //Obfuscate all layers of the current chunk section except the upper one -+ for (int y = 0; y < 15; y++) { -+ boolean[][] temp = current; -+ current = next; -+ next = nextNext; -+ nextNext = temp; -+ counter = obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); -+ } -+ -+ //Check if the chunk section above doesn't need obfuscation -+ if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPredefinedObjects(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 -+ ChunkSection aboveChunkSection; -+ -+ if (chunkSectionIndex != 15 && (aboveChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex + 1]) != Chunk.EMPTY_CHUNK_SECTION) { -+ boolean[][] temp = current; -+ current = next; -+ next = nextNext; -+ nextNext = temp; -+ -+ for (int z = 0; z < 16; z++) { -+ for (int x = 0; x < 16; x++) { -+ if (!solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(aboveChunkSection.getType(x, 0, z))]) { -+ current[z][x] = true; -+ } -+ } -+ } -+ -+ //There is nothing to read anymore -+ dataBitsReader.setBitsPerObject(0); -+ solid[0] = true; -+ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); -+ } -+ } 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.getOrCreateIdForIndex(chunkSectionIndex + 1)); -+ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), solid, solidGlobal); -+ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal); -+ boolean[][] temp = current; -+ current = next; -+ next = nextNext; -+ nextNext = temp; -+ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); -+ } -+ -+ dataBitsWriter.finish(); -+ } -+ } -+ -+ chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true); -+ } -+ -+ private int obfuscateLayer(int y, DataBitsReader dataBitsReader, DataBitsWriter dataBitsWriter, boolean[] solid, boolean[] obfuscate, int[] predefinedBlockDataBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, ChunkSection[] nearbyChunkSections, int counter) { -+ //First block of first line -+ int dataBits = dataBitsReader.read(); -+ -+ if (nextNext[0][0] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[0][1] = true; -+ next[1][0] = true; -+ } else { -+ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(0, y, 15))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 0))] || current[0][0]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[0][0] = true; -+ } -+ -+ //First line -+ for (int x = 1; x < 15; x++) { -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[0][x] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[0][x - 1] = true; -+ next[0][x + 1] = true; -+ next[1][x] = true; -+ } else { -+ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(x, y, 15))] || current[0][x]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[0][x] = true; -+ } -+ } -+ -+ //Last block of first line -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[0][15] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[0][14] = true; -+ next[1][15] = true; -+ } else { -+ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(15, y, 15))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 0))] || current[0][15]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[0][15] = true; -+ } -+ -+ //All inner lines -+ for (int z = 1; z < 15; z++) { -+ //First block -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[z][0] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[z][1] = true; -+ next[z - 1][0] = true; -+ next[z + 1][0] = true; -+ } else { -+ if (nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, z))] || current[z][0]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[z][0] = true; -+ } -+ -+ //All inner blocks -+ for (int x = 1; x < 15; x++) { -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[z][x] = !solid[dataBits]) { -+ dataBitsWriter.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(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[z][x] = true; -+ } -+ } -+ -+ //Last block -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[z][15] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[z][14] = true; -+ next[z - 1][15] = true; -+ next[z + 1][15] = true; -+ } else { -+ if (nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, z))] || current[z][15]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[z][15] = true; -+ } -+ } -+ -+ //First block of last line -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[15][0] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[15][1] = true; -+ next[14][0] = true; -+ } else { -+ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(0, y, 0))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 15))] || current[15][0]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[15][0] = true; -+ } -+ -+ //Last line -+ for (int x = 1; x < 15; x++) { -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[15][x] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[15][x - 1] = true; -+ next[15][x + 1] = true; -+ next[14][x] = true; -+ } else { -+ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(x, y, 0))] || current[15][x]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[15][x] = true; -+ } -+ } -+ -+ //Last block of last line -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[15][15] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[15][14] = true; -+ next[14][15] = true; -+ } else { -+ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(15, y, 0))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 15))] || current[15][15]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[15][15] = true; -+ } -+ -+ return counter; -+ } -+ -+ private boolean[] readDataPalette(DataPalette dataPalette, boolean[] temp, boolean[] global) { -+ if (dataPalette == ChunkSection.GLOBAL_PALETTE) { -+ return global; -+ } -+ -+ IBlockData blockData; -+ -+ for (int i = 0; (blockData = dataPalette.getObject(i)) != null; i++) { -+ temp[i] = global[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)]; -+ } -+ -+ return temp; -+ } -+ -+ @Override -+ public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) { -+ if (oldBlockData != null && solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(oldBlockData)] && !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(newBlockData)] && blockPosition.getY() <= maxBlockYUpdatePosition) { -+ updateNearbyBlocks(world, blockPosition); -+ } -+ } -+ -+ @Override -+ public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) { -+ if (blockPosition.getY() <= maxBlockYUpdatePosition) { -+ updateNearbyBlocks(playerInteractManager.world, blockPosition); -+ } -+ } -+ -+ private void updateNearbyBlocks(World world, BlockPosition blockPosition) { -+ if (updateRadius >= 2) { -+ BlockPosition temp = blockPosition.west(); -+ updateBlock(world, temp); -+ updateBlock(world, temp.west()); -+ updateBlock(world, temp.down()); -+ updateBlock(world, temp.up()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp.south()); -+ updateBlock(world, temp = blockPosition.east()); -+ updateBlock(world, temp.east()); -+ updateBlock(world, temp.down()); -+ updateBlock(world, temp.up()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp.south()); -+ updateBlock(world, temp = blockPosition.down()); -+ updateBlock(world, temp.down()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp.south()); -+ updateBlock(world, temp = blockPosition.up()); -+ updateBlock(world, temp.up()); -+ 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()); -+ } else if (updateRadius == 1) { -+ updateBlock(world, blockPosition.west()); -+ updateBlock(world, blockPosition.east()); -+ updateBlock(world, blockPosition.down()); -+ updateBlock(world, blockPosition.up()); -+ updateBlock(world, blockPosition.north()); -+ updateBlock(world, blockPosition.south()); -+ } else { -+ //Do nothing if updateRadius <= 0 (test mode) -+ } -+ } -+ -+ private void updateBlock(World world, BlockPosition blockPosition) { -+ IBlockData blockData = world.getTypeIfLoaded(blockPosition); -+ -+ if (blockData != null && obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)]) { -+ world.notify(blockPosition, blockData, blockData, 3); -+ } -+ } -+ -+ public enum EngineMode { -+ -+ HIDE(1, "hide ores"), -+ OBFUSCATE(2, "obfuscate"); -+ -+ private final int id; -+ private final String description; -+ -+ EngineMode(int id, String description) { -+ this.id = id; -+ this.description = description; -+ } -+ -+ public static EngineMode getById(int id) { -+ for (EngineMode engineMode : values()) { -+ if (engineMode.id == id) { -+ return engineMode; -+ } -+ } -+ -+ return null; -+ } -+ -+ public int getId() { -+ return id; -+ } -+ -+ public String getDescription() { -+ return description; -+ } -+ } -+ -+ public enum ChunkEdgeMode { -+ -+ DEFAULT(1, "default"), -+ WAIT(2, "wait until nearby chunks are loaded"), -+ LOAD(3, "load nearby chunks"); -+ -+ private final int id; -+ private final String description; -+ -+ ChunkEdgeMode(int id, String description) { -+ this.id = id; -+ this.description = description; -+ } -+ -+ public static ChunkEdgeMode getById(int id) { -+ for (ChunkEdgeMode chunkEdgeMode : values()) { -+ if (chunkEdgeMode.id == id) { -+ return chunkEdgeMode; -+ } -+ } -+ -+ return null; -+ } -+ -+ public int getId() { -+ return id; -+ } -+ -+ public String getDescription() { -+ return description; -+ } -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java -new file mode 100644 -index 0000000000..a68bace353 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java -@@ -0,0 +1,81 @@ -+package com.destroystokyo.paper.antixray; -+ -+import net.minecraft.server.Chunk; -+import net.minecraft.server.DataPalette; -+import net.minecraft.server.PacketPlayOutMapChunk; -+ -+public class ChunkPacketInfo { -+ -+ private final PacketPlayOutMapChunk packetPlayOutMapChunk; -+ private final Chunk chunk; -+ private final int chunkSectionSelector; -+ private byte[] data; -+ private final int[] bitsPerObject = new int[16]; -+ private final Object[] dataPalettes = new Object[16]; -+ private final int[] dataBitsIndexes = new int[16]; -+ private final Object[][] predefinedObjects = new Object[16][]; -+ -+ public ChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { -+ this.packetPlayOutMapChunk = packetPlayOutMapChunk; -+ this.chunk = chunk; -+ this.chunkSectionSelector = chunkSectionSelector; -+ } -+ -+ public PacketPlayOutMapChunk getPacketPlayOutMapChunk() { -+ return packetPlayOutMapChunk; -+ } -+ -+ public Chunk getChunk() { -+ return chunk; -+ } -+ -+ public int getChunkSectionSelector() { -+ return chunkSectionSelector; -+ } -+ -+ public byte[] getData() { -+ return data; -+ } -+ -+ public void setData(byte[] data) { -+ this.data = data; -+ } -+ -+ public int getBitsPerObject(int chunkSectionIndex) { -+ return bitsPerObject[chunkSectionIndex]; -+ } -+ -+ public void setBitsPerObject(int chunkSectionIndex, int bitsPerObject) { -+ this.bitsPerObject[chunkSectionIndex] = bitsPerObject; -+ } -+ -+ @SuppressWarnings("unchecked") -+ public DataPalette getDataPalette(int chunkSectionIndex) { -+ return (DataPalette) dataPalettes[chunkSectionIndex]; -+ } -+ -+ public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) { -+ dataPalettes[chunkSectionIndex] = dataPalette; -+ } -+ -+ public int getOrCreateIdForIndex(int chunkSectionIndex) { -+ return dataBitsIndexes[chunkSectionIndex]; -+ } -+ -+ public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) { -+ dataBitsIndexes[chunkSectionIndex] = dataBitsIndex; -+ } -+ -+ @SuppressWarnings("unchecked") -+ public T[] getPredefinedObjects(int chunkSectionIndex) { -+ return (T[]) predefinedObjects[chunkSectionIndex]; -+ } -+ -+ public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) { -+ this.predefinedObjects[chunkSectionIndex] = predefinedObjects; -+ } -+ -+ public boolean isWritten(int chunkSectionIndex) { -+ return bitsPerObject[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 0000000000..e255a45fa3 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java -@@ -0,0 +1,29 @@ -+package com.destroystokyo.paper.antixray; -+ -+import net.minecraft.server.Chunk; -+import net.minecraft.server.IBlockData; -+import net.minecraft.server.PacketPlayOutMapChunk; -+ -+public class ChunkPacketInfoAntiXray extends ChunkPacketInfo implements Runnable { -+ -+ private Chunk[] nearbyChunks; -+ private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray; -+ -+ public ChunkPacketInfoAntiXray(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) { -+ super(packetPlayOutMapChunk, chunk, chunkSectionSelector); -+ this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray; -+ } -+ -+ public Chunk[] getNearbyChunks() { -+ return nearbyChunks; -+ } -+ -+ public void setNearbyChunks(Chunk... nearbyChunks) { -+ this.nearbyChunks = nearbyChunks; -+ } -+ -+ @Override -+ public void run() { -+ 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 0000000000..cc586827aa ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java -@@ -0,0 +1,56 @@ -+package com.destroystokyo.paper.antixray; -+ -+public 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() { -+ int value = (int) (current >>> bitInLongIndex) & mask; -+ bitInLongIndex += bitsPerObject; -+ -+ if (bitInLongIndex > 63) { -+ bitInLongIndex -= 64; -+ longInDataBitsIndex += 8; -+ init(); -+ -+ if (bitInLongIndex > 0) { -+ value |= current << bitsPerObject - bitInLongIndex & mask; -+ } -+ } -+ -+ 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 0000000000..37093419cf ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java -@@ -0,0 +1,84 @@ -+package com.destroystokyo.paper.antixray; -+ -+public 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) { -+ current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex; -+ dirty = true; -+ bitInLongIndex += bitsPerObject; -+ -+ if (bitInLongIndex > 63) { -+ finish(); -+ bitInLongIndex -= 64; -+ longInDataBitsIndex += 8; -+ init(); -+ -+ if (bitInLongIndex > 0) { -+ current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex; -+ dirty = true; -+ } -+ } -+ } -+ -+ public void skip() { -+ bitInLongIndex += bitsPerObject; -+ -+ if (bitInLongIndex > 63) { -+ finish(); -+ bitInLongIndex -= 64; -+ longInDataBitsIndex += 8; -+ init(); -+ } -+ } -+} -diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index 4502ece4dd..4ae0acbf13 100644 ---- a/src/main/java/net/minecraft/server/Chunk.java -+++ b/src/main/java/net/minecraft/server/Chunk.java -@@ -541,7 +541,7 @@ public class Chunk implements IChunkAccess { - return null; - } - -- chunksection = new ChunkSection(j >> 4 << 4, this.world.worldProvider.g()); -+ chunksection = new ChunkSection(j >> 4 << 4, this.world.worldProvider.g(), this, this.world, true); // Paper - Anti-Xray - this.sections[j >> 4] = chunksection; - flag1 = j >= l; - } -@@ -641,7 +641,7 @@ public class Chunk implements IChunkAccess { - return; - } - -- chunksection = new ChunkSection(i1 << 4, flag); -+ chunksection = new ChunkSection(i1 << 4, flag, this, this.world, true); // Paper - Anti-Xray - this.sections[i1] = chunksection; - this.initLighting(); - } -diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 60143ff63f..d938eb3749 100644 ---- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java -+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -@@ -849,7 +849,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { - } - - ChunkConverter chunkconverter = nbttagcompound.hasKeyOfType("UpgradeData", 10) ? new ChunkConverter(nbttagcompound.getCompound("UpgradeData")) : ChunkConverter.a; -- ProtoChunk protochunk = new ProtoChunk(i, j, chunkconverter); -+ ProtoChunk protochunk = new ProtoChunk(i, j, chunkconverter, generatoraccess); // Paper - Anti-Xray - - protochunk.a(abiomebase); - protochunk.b(nbttagcompound.getLong("InhabitedTime")); -@@ -955,7 +955,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { - for (int i = 0; i < nbttaglist.size(); ++i) { - NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); - byte b0 = nbttagcompound.getByte("Y"); -- ChunkSection chunksection = new ChunkSection(b0 << 4, flag1); -+ ChunkSection chunksection = new ChunkSection(b0 << 4, flag1, null, iworldreader, false); // Paper - Anti-Xray - - chunksection.getBlocks().a(nbttagcompound, "Palette", "BlockStates"); - chunksection.a(new NibbleArray(nbttagcompound.getByteArray("BlockLight"))); -diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java -index 621ed1fc53..2af07ae592 100644 ---- a/src/main/java/net/minecraft/server/ChunkSection.java -+++ b/src/main/java/net/minecraft/server/ChunkSection.java -@@ -11,9 +11,15 @@ public class ChunkSection { - private NibbleArray emittedLight; - private NibbleArray skyLight; - -+ // Paper start - Anti-Xray - Support default constructor - public ChunkSection(int i, boolean flag) { -+ this(i, flag, null, null, true); -+ } -+ // Paper end -+ -+ public ChunkSection(int i, boolean flag, IChunkAccess chunk, IWorldReader world, boolean initializeBlocks) { // Paper - Anti-Xray - this.yPos = i; -- this.blockIds = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData()); -+ this.blockIds = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData(), world instanceof GeneratorAccess ? ((GeneratorAccess) world).getMinecraftWorld().chunkPacketBlockController.getPredefinedBlockData(world, chunk, this, flag, initializeBlocks) : null, initializeBlocks); // Paper - Anti-Xray - Add predefined block data - this.emittedLight = new NibbleArray(); - if (flag) { - this.skyLight = new NibbleArray(); -diff --git a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java -index 56958a5ce3..d3898599f8 100644 ---- a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java -+++ b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java -@@ -39,7 +39,7 @@ public class ChunkTaskScheduler extends Scheduler implements DataPaletteExpandable { - private final Function e; - private final Function f; - private final T g; -+ private final T[] predefinedObjects; // Paper - Anti-Xray - Add predefined objects - protected DataBits a; protected DataBits getDataBits() { return this.a; } // Paper - OBFHELPER - private DataPalette h; private DataPalette getDataPalette() { return this.h; } // Paper - OBFHELPER - private int i; private int getBitsPerObject() { return this.i; } // Paper - OBFHELPER -@@ -41,12 +43,43 @@ public class DataPaletteBlock implements DataPaletteExpandable { - } - - public DataPaletteBlock(DataPalette datapalette, RegistryBlockID registryblockid, Function function, Function function1, T t0) { -+ // Paper start - Anti-Xray - Support default constructor -+ this(datapalette, registryblockid, function, function1, t0, null, true); -+ } -+ -+ public DataPaletteBlock(DataPalette datapalette, RegistryBlockID registryblockid, Function function, Function function1, T t0, T[] predefinedObjects, boolean initialize) { -+ // Paper end - Anti-Xray - Add predefined objects - this.b = datapalette; - this.d = registryblockid; - this.e = function; - this.f = function1; - this.g = t0; -- this.b(4); -+ // Paper start - Anti-Xray - Add predefined objects -+ this.predefinedObjects = predefinedObjects; -+ -+ if (initialize) { -+ if (predefinedObjects == null) { -+ // Default -+ this.initialize(4); -+ } else { -+ // TODO: MathHelper.d(int i) can be used here instead (see DataPaletteBlock#a(NBTTagCompound nbttagcompound, String s, String s1)) but I don't understand the implementation -+ // 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 = 4; -+ -+ while (maxIndex != 0) { -+ maxIndex >>= 1; -+ bitCount++; -+ } -+ -+ // Initialize with at least 15 free indixes -+ this.initialize((1 << bitCount) - predefinedObjects.length < 16 ? bitCount + 1 : bitCount); -+ this.addPredefinedObjects(); -+ } -+ } -+ // Paper end - } - - private static int b(int i, int j, int k) { -@@ -72,6 +105,16 @@ public class DataPaletteBlock implements DataPaletteExpandable { - } - } - -+ // Paper start - Anti-Xray - Add predefined objects -+ private void addPredefinedObjects() { -+ if (this.predefinedObjects != null && this.getDataPalette() != this.getDataPaletteGlobal()) { -+ for (int i = 0; i < this.predefinedObjects.length; i++) { -+ this.getDataPalette().getOrCreateIdFor(this.predefinedObjects[i]); -+ } -+ } -+ } -+ // Paper end -+ - public int onResize(int i, T t0) { - this.b(); - DataBits databits = this.a; -@@ -81,6 +124,7 @@ public class DataPaletteBlock implements DataPaletteExpandable { - - int j; - -+ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects - for (j = 0; j < databits.b(); ++j) { - T t1 = datapalette.a(databits.a(j)); - -@@ -116,11 +160,28 @@ public class DataPaletteBlock implements DataPaletteExpandable { - return t0 == null ? this.g : t0; - } - -- public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } // Paper - OBFHELPER -+ // Paper start - Anti-Xray - Support default methods -+ public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } - public void b(PacketDataSerializer packetdataserializer) { -+ this.b(packetdataserializer, null, 0); -+ } -+ // Paper end -+ -+ public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer, ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { this.b(packetDataSerializer, chunkPacketInfo, chunkSectionIndex); } // Paper - OBFHELPER // Paper - Anti-Xray - Add chunk packet info -+ public void b(PacketDataSerializer packetdataserializer, ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { // Paper - Anti-Xray - Add chunk packet info - this.b(); - packetdataserializer.writeByte(this.i); - this.h.b(packetdataserializer); -+ -+ // Paper start - Anti-Xray - Add chunk packet info -+ if (chunkPacketInfo != null) { -+ chunkPacketInfo.setBitsPerObject(chunkSectionIndex, this.getBitsPerObject()); -+ chunkPacketInfo.setDataPalette(chunkSectionIndex, this.getDataPalette()); -+ chunkPacketInfo.setDataBitsIndex(chunkSectionIndex, packetdataserializer.writerIndex() + PacketDataSerializer.countBytes(this.getDataBits().getDataBits().length)); -+ chunkPacketInfo.setPredefinedObjects(chunkSectionIndex, this.predefinedObjects); -+ } -+ // Paper end -+ - packetdataserializer.a(this.a.a()); - this.c(); - } -@@ -128,13 +189,15 @@ public class DataPaletteBlock implements DataPaletteExpandable { - public void a(NBTTagCompound nbttagcompound, String s, String s1) { - this.b(); - NBTTagList nbttaglist = nbttagcompound.getList(s, 10); -- int i = Math.max(4, MathHelper.d(nbttaglist.size())); -+ // 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, MathHelper.d(nbttaglist.size() + (this.predefinedObjects == null ? 0 : this.predefinedObjects.length))); // Paper - Anti-Xray - Calculate the size with predefined objects - -- if (i != this.i) { -+ if (true || i != this.i) { // Paper - Anti-Xray - Not initialized yet - this.b(i); - } - - this.h.a(nbttaglist); -+ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects - long[] along = nbttagcompound.o(s1); - int j = along.length * 64 / 4096; - -diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java -index 4a50aab512..4c1110479c 100644 ---- a/src/main/java/net/minecraft/server/NetworkManager.java -+++ b/src/main/java/net/minecraft/server/NetworkManager.java -@@ -157,8 +157,8 @@ public class NetworkManager extends SimpleChannelInboundHandler> { - } - - public void sendPacket(Packet packet, @Nullable GenericFutureListener> genericfuturelistener) { -- if (this.isConnected()) { -- this.o(); -+ if (this.isConnected() && this.sendPacketQueue() && !(packet instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) packet).isReady())) { // Paper - Async-Anti-Xray - Add chunk packets which are not ready or all packets if the packet queue contains chunk packets which are not ready to the packet queue and send the packets later in the right order -+ //this.o(); // Paper - Async-Anti-Xray - Move to if statement (this.sendPacketQueue()) - this.b(packet, genericfuturelistener); - } else { - this.j.writeLock().lock(); -@@ -213,23 +213,38 @@ public class NetworkManager extends SimpleChannelInboundHandler> { - - } - -- private void sendPacketQueue() { this.o(); } // Paper - OBFHELPER -- private void o() { -+ // Paper start - Async-Anti-Xray - Stop dispatching further packets and return false if the peeked packet is a chunk packet which is not ready -+ private boolean sendPacketQueue() { return this.o(); } // OBFHELPER // void -> boolean -+ private boolean o() { // void -> boolean - if (this.channel != null && this.channel.isOpen()) { -- this.j.readLock().lock(); -+ if (this.packetQueue.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all -+ return true; -+ } -+ -+ this.j.writeLock().lock(); // readLock -> writeLock (because of race condition between peek and poll) - - try { - while (!this.packetQueue.isEmpty()) { -- NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.packetQueue.poll(); -- -- this.b(networkmanager_queuedpacket.a, networkmanager_queuedpacket.b); -+ NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.getPacketQueue().peek(); // poll -> peek -+ -+ if (networkmanager_queuedpacket != null) { // Fix NPE (Spigot bug caused by handleDisconnection()) -+ if (networkmanager_queuedpacket.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) networkmanager_queuedpacket.getPacket()).isReady()) { // Check if the peeked packet is a chunk packet which is not ready -+ return false; // Return false if the peeked packet is a chunk packet which is not ready -+ } else { -+ this.getPacketQueue().poll(); // poll here -+ this.dispatchPacket(networkmanager_queuedpacket.getPacket(), networkmanager_queuedpacket.getGenericFutureListener()); // dispatch the packet -+ } -+ } - } - } finally { -- this.j.readLock().unlock(); -+ this.j.writeLock().unlock(); // readLock -> writeLock (because of race condition between peek and poll) - } - - } -+ -+ return true; // Return true if all packets were dispatched - } -+ // Paper end - - public void a() { - this.o(); -diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java -index 18ef7232ec..8e35d14f97 100644 ---- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java -+++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java -@@ -1,5 +1,6 @@ - package net.minecraft.server; - -+import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray - import com.google.common.collect.Lists; - import io.netty.buffer.ByteBuf; - import io.netty.buffer.Unpooled; -@@ -16,17 +17,28 @@ public class PacketPlayOutMapChunk implements Packet { - private byte[] d; private byte[] getData() { return this.d; } // Paper - OBFHELPER - private List e; - private boolean f; -+ private volatile boolean ready = false; // Paper - Async-Anti-Xray - Ready flag for the network manager - -- public PacketPlayOutMapChunk() {} -+ public PacketPlayOutMapChunk() { -+ this.ready = true; // Paper - Async-Anti-Xray - Set the ready flag to true -+ } - - public PacketPlayOutMapChunk(Chunk chunk, int i) { -+ ChunkPacketInfo chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info - this.a = chunk.locX; - this.b = chunk.locZ; - this.f = i == 65535; - boolean flag = chunk.getWorld().worldProvider.g(); - - this.d = new byte[this.a(chunk, flag, i)]; -- this.c = this.a(new PacketDataSerializer(this.h()), chunk, flag, i); -+ -+ // Paper start - Anti-Xray - Add chunk packet info -+ if (chunkPacketInfo != null) { -+ chunkPacketInfo.setData(this.getData()); -+ } -+ // Paper end -+ -+ this.c = this.writeChunk(new PacketDataSerializer(this.h()), chunk, flag, i, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info - this.e = Lists.newArrayList(); - Iterator iterator = chunk.getTileEntities().entrySet().iterator(); - -@@ -44,7 +56,18 @@ public class PacketPlayOutMapChunk implements Packet { - } - } - -+ chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks -+ } -+ -+ // Paper start - Async-Anti-Xray - Getter and Setter for the ready flag -+ public boolean isReady() { -+ return this.ready; -+ } -+ -+ public void setReady(boolean ready) { -+ this.ready = ready; - } -+ // Paper end - - public void a(PacketDataSerializer packetdataserializer) throws IOException { - this.a = packetdataserializer.readInt(); -@@ -98,8 +121,15 @@ public class PacketPlayOutMapChunk implements Packet { - return bytebuf; - } - -- public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } // Paper - OBFHELPER -+ // Paper start - Anti-Xray - Support default methods -+ public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } - public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i) { -+ return this.a(packetdataserializer, chunk, flag, i, null); -+ } -+ // Paper end -+ -+ public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector, ChunkPacketInfo chunkPacketInfo) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector, chunkPacketInfo); } // Paper - OBFHELPER // Paper - Anti-Xray - Add chunk packet info -+ public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i, ChunkPacketInfo chunkPacketInfo) { // Paper - Anti-Xray - Add chunk packet info - int j = 0; - ChunkSection[] achunksection = chunk.getSections(); - int k = 0; -@@ -111,7 +141,7 @@ public class PacketPlayOutMapChunk implements Packet { - - if (chunksection != Chunk.a && (!this.f() || !chunksection.a()) && (i & 1 << k) != 0) { - j |= 1 << k; -- chunksection.getBlocks().b(packetdataserializer); -+ chunksection.getBlocks().writeDataPaletteBlock(packetdataserializer, chunkPacketInfo, k); // Paper - Anti-Xray - Add chunk packet info - packetdataserializer.writeBytes(chunksection.getEmittedLightArray().asBytes()); - if (flag) { - packetdataserializer.writeBytes(chunksection.getSkyLightArray().asBytes()); -diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 7d3f846a19..240f590666 100644 ---- a/src/main/java/net/minecraft/server/PlayerChunk.java -+++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -103,6 +103,8 @@ public class PlayerChunk { - return false; - } else if (!this.chunk.isReady()) { - return false; -+ } else if (!this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', false)) { // Paper - Anti-Xray - Load nearby chunks if necessary -+ return false; // Paper - Anti-Xray - Wait and try again later - } else { - this.dirtyCount = 0; - this.h = 0; -@@ -125,6 +127,7 @@ public class PlayerChunk { - - public void sendChunk(EntityPlayer entityplayer) { - if (this.done) { -+ this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', true); // Paper - Anti-Xray - Load nearby chunks if necessary - entityplayer.playerConnection.sendPacket(new PacketPlayOutMapChunk(this.chunk, 65535)); - this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk); - } -@@ -189,6 +192,9 @@ public class PlayerChunk { - this.a(this.playerChunkMap.getWorld().getTileEntity(blockposition)); - } - } else if (this.dirtyCount == 64) { -+ // Paper - Anti-Xray - Loading chunks here could cause a ConcurrentModificationException #1104 -+ // Paper - Anti-Xray - TODO: Check if this is still the case for 1.13 -+ //this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, this.h, true); // Paper - Anti-Xray - Load nearby chunks if necessary - this.a((Packet) (new PacketPlayOutMapChunk(this.chunk, this.h))); - } else { - this.a((Packet) (new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, this.chunk))); -diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java -index 07f9b8d2f7..22378b6bc8 100644 ---- a/src/main/java/net/minecraft/server/PlayerInteractManager.java -+++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java -@@ -207,6 +207,8 @@ public class PlayerInteractManager { - } - - } -+ -+ this.world.chunkPacketBlockController.onPlayerLeftClickBlock(this, blockposition, enumdirection); // Paper - Anti-Xray - } - - public void a(BlockPosition blockposition) { -diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java -index 16e3469d01..e4c0cc6a33 100644 ---- a/src/main/java/net/minecraft/server/ProtoChunk.java -+++ b/src/main/java/net/minecraft/server/ProtoChunk.java -@@ -41,12 +41,24 @@ public class ProtoChunk implements IChunkAccess { - private long s; - private final Map t; - private boolean u; -+ private GeneratorAccess world; // Paper - Anti-Xray - -+ // Paper start - Anti-Xray - Support default constructors - public ProtoChunk(int i, int j, ChunkConverter chunkconverter) { -- this(new ChunkCoordIntPair(i, j), chunkconverter); -+ this(new ChunkCoordIntPair(i, j), chunkconverter, null); - } - - public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter) { -+ this(chunkcoordintpair, chunkconverter, null); -+ } -+ -+ public ProtoChunk(int i, int j, ChunkConverter chunkconverter, GeneratorAccess world) { -+ this(new ChunkCoordIntPair(i, j), chunkconverter, world); -+ } -+ -+ public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, GeneratorAccess world) { -+ this.world = world; -+ // Paper end - this.d = new AtomicInteger(); - this.f = Maps.newEnumMap(HeightMap.Type.class); - this.g = ChunkStatus.EMPTY; -@@ -135,7 +147,7 @@ public class ProtoChunk implements IChunkAccess { - return iblockdata; - } - -- this.j[j >> 4] = new ChunkSection(j >> 4 << 4, this.x()); -+ this.j[j >> 4] = new ChunkSection(j >> 4 << 4, this.x(), this, this.world, true); // Paper - Anti-Xray - } - - IBlockData iblockdata1 = this.j[j >> 4].getType(i & 15, j & 15, k & 15); -@@ -389,7 +401,7 @@ public class ProtoChunk implements IChunkAccess { - return; - } - -- this.j[i1] = new ChunkSection(i1 << 4, this.x()); -+ this.j[i1] = new ChunkSection(i1 << 4, this.x(), this, this.world, true); // Paper - Anti-Xray - } - - if (enumskyblock == EnumSkyBlock.SKY) { -diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index b007eb36c7..14f419deb8 100644 ---- a/src/main/java/net/minecraft/server/World.java -+++ b/src/main/java/net/minecraft/server/World.java -@@ -1,6 +1,8 @@ - package net.minecraft.server; - - import co.aikar.timings.Timings; -+import com.destroystokyo.paper.antixray.ChunkPacketBlockController; // Paper - Anti-Xray -+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray; // Paper - Anti-Xray - import com.destroystokyo.paper.event.server.ServerExceptionEvent; - import com.destroystokyo.paper.exception.ServerInternalException; - import com.google.common.base.MoreObjects; -@@ -139,6 +141,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc - public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot - - public final com.destroystokyo.paper.PaperWorldConfig paperConfig; // Paper -+ public final ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray - - public final co.aikar.timings.WorldTimingsHandler timings; // Paper - public boolean guardEntityList; // Spigot // Paper - public -@@ -165,6 +168,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc - protected World(IDataManager idatamanager, @Nullable PersistentCollection persistentcollection, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag, ChunkGenerator gen, org.bukkit.World.Environment env) { - this.spigotConfig = new org.spigotmc.SpigotWorldConfig( worlddata.getName() ); // Spigot - this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(worlddata.getName(), this.spigotConfig); // Paper -+ this.chunkPacketBlockController = this.paperConfig.antiXray ? new ChunkPacketBlockControllerAntiXray(this.paperConfig) : ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray - this.generator = gen; - this.world = new CraftWorld((WorldServer) this, gen, env); - this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit -@@ -407,6 +411,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc - // CraftBukkit end - - IBlockData iblockdata1 = chunk.setType(blockposition, iblockdata, (i & 64) != 0, (i & 1024) == 0); // CraftBukkit custom NO_PLACE flag -+ this.chunkPacketBlockController.onBlockChange(this, blockposition, iblockdata, iblockdata1, i); // Paper - Anti-Xray - - if (iblockdata1 == null) { - // CraftBukkit start - remove blockstate if failed -diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java -index 791017258a..29695da2e5 100644 ---- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java -+++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java -@@ -21,9 +21,11 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { - private final int maxHeight; - private final ChunkSection[] sections; - private Set tiles; -+ private World world; // Paper - Anti-Xray - - public CraftChunkData(World world) { - this(world.getMaxHeight()); -+ this.world = world; // Paper - Anti-Xray - } - - /* pp for tests */ CraftChunkData(int maxHeight) { -@@ -157,7 +159,7 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { - private ChunkSection getChunkSection(int y, boolean create) { - ChunkSection section = sections[y >> 4]; - if (create && section == null) { -- sections[y >> 4] = section = new ChunkSection(y, create); -+ sections[y >> 4] = section = new ChunkSection(y, create, null, world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) world).getHandle() : null, true); // Paper - Anti-Xray - } - return section; - } --- -2.21.0 - From 76eb2a5e5cc73b41c55b0ef7792ae90c4275d0d2 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Tue, 25 Jun 2019 22:20:20 -0700 Subject: [PATCH 3/8] Use getChunkIfLoadedImmediately not getChunkIfCachedImmediately --- Spigot-Server-Patches/0408-Anti-Xray.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Spigot-Server-Patches/0408-Anti-Xray.patch b/Spigot-Server-Patches/0408-Anti-Xray.patch index ad50f76897..8607b939bf 100644 --- a/Spigot-Server-Patches/0408-Anti-Xray.patch +++ b/Spigot-Server-Patches/0408-Anti-Xray.patch @@ -274,7 +274,7 @@ index 0000000000..3aa614833d + chunk.world.getChunkAt(locX, locZ - 1); + chunk.world.getChunkAt(locX, locZ + 1); + } else if (chunkEdgeMode == ChunkEdgeMode.WAIT) { -+ if (chunkProvider.getChunkAtIfCachedImmediately(locX - 1, locZ) == null || chunkProvider.getChunkAtIfCachedImmediately(locX + 1, locZ) == null || chunkProvider.getChunkAtIfCachedImmediately(locX, locZ - 1) == null || chunkProvider.getChunkAtIfCachedImmediately(locX, locZ + 1) == null) { ++ if (chunkProvider.getChunkIfLoadedImmediately(locX - 1, locZ) == null || chunkProvider.getChunkIfLoadedImmediately(locX + 1, locZ) == null || chunkProvider.getChunkIfLoadedImmediately(locX, locZ - 1) == null || chunkProvider.getChunkIfLoadedImmediately(locX, locZ + 1) == null) { + //Don't create the chunk packet now, wait until nearby chunks are loaded and create it later + return false; + } From f1f8060a4e703732863b297e8f1ebb21194992b4 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Tue, 25 Jun 2019 22:27:22 -0700 Subject: [PATCH 4/8] Fix build? --- Spigot-Server-Patches/0408-Anti-Xray.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Spigot-Server-Patches/0408-Anti-Xray.patch b/Spigot-Server-Patches/0408-Anti-Xray.patch index 8607b939bf..99c6274e25 100644 --- a/Spigot-Server-Patches/0408-Anti-Xray.patch +++ b/Spigot-Server-Patches/0408-Anti-Xray.patch @@ -274,7 +274,7 @@ index 0000000000..3aa614833d + chunk.world.getChunkAt(locX, locZ - 1); + chunk.world.getChunkAt(locX, locZ + 1); + } else if (chunkEdgeMode == ChunkEdgeMode.WAIT) { -+ if (chunkProvider.getChunkIfLoadedImmediately(locX - 1, locZ) == null || chunkProvider.getChunkIfLoadedImmediately(locX + 1, locZ) == null || chunkProvider.getChunkIfLoadedImmediately(locX, locZ - 1) == null || chunkProvider.getChunkIfLoadedImmediately(locX, locZ + 1) == null) { ++ if (chunkProvider.getChunkAtIfLoadedImmediately(locX - 1, locZ) == null || chunkProvider.getChunkAtIfLoadedImmediately(locX + 1, locZ) == null || chunkProvider.getChunkAtIfLoadedImmediately(locX, locZ - 1) == null || chunkProvider.getChunkAtIfLoadedImmediately(locX, locZ + 1) == null) { + //Don't create the chunk packet now, wait until nearby chunks are loaded and create it later + return false; + } From 9a4a185fc1acdf9365a846ecfeb998f317bc7be3 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Wed, 26 Jun 2019 19:04:28 -0700 Subject: [PATCH 5/8] Keep neighbour chunks loaded while obfuscating --- Spigot-Server-Patches/0408-Anti-Xray.patch | 269 +++++++++++++-------- 1 file changed, 162 insertions(+), 107 deletions(-) diff --git a/Spigot-Server-Patches/0408-Anti-Xray.patch b/Spigot-Server-Patches/0408-Anti-Xray.patch index 99c6274e25..6ae0d7760c 100644 --- a/Spigot-Server-Patches/0408-Anti-Xray.patch +++ b/Spigot-Server-Patches/0408-Anti-Xray.patch @@ -1,4 +1,4 @@ -From 9f304c5c1ecc2202f221f1e80b67d2cc1b4494e7 Mon Sep 17 00:00:00 2001 +From a63f34ceb95fa2baa35df2afbe3c3f823fdac89f Mon Sep 17 00:00:00 2001 From: stonar96 Date: Mon, 20 Aug 2018 03:03:58 +0200 Subject: [PATCH] Anti-Xray @@ -108,10 +108,10 @@ index 0000000000..dc534d239e +} 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 0000000000..3aa614833d +index 0000000000..6f7069f548 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -0,0 +1,691 @@ +@@ -0,0 +1,738 @@ +package com.destroystokyo.paper.antixray; + +import java.util.ArrayList; @@ -120,6 +120,8 @@ index 0000000000..3aa614833d +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; ++import java.util.concurrent.atomic.AtomicInteger; ++import java.util.function.Supplier; + +import net.minecraft.server.*; +import org.bukkit.World.Environment; @@ -259,6 +261,30 @@ index 0000000000..3aa614833d + return null; + } + ++ private final AtomicInteger xrayRequests = new AtomicInteger(); ++ ++ private Integer addXrayTickets(final int x, final int z, final ChunkProviderServer chunkProvider) { ++ final Integer hold = Integer.valueOf(this.xrayRequests.getAndIncrement()); ++ ++ // Add at ticket level 33, which is just enough to keep chunks loaded ++ chunkProvider.addTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z), 0, hold); ++ chunkProvider.addTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x - 1, z), 0, hold); ++ chunkProvider.addTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x + 1, z), 0, hold); ++ chunkProvider.addTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z - 1), 0, hold); ++ chunkProvider.addTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z + 1), 0, hold); ++ ++ return hold; ++ } ++ ++ private void removeXrayTickets(final int x, final int z, final ChunkProviderServer chunkProvider, final Integer hold) { ++ // Remove at ticket level 33 (same one we added as), which is just enough to keep chunks loaded ++ chunkProvider.removeTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z), 0, hold); ++ chunkProvider.removeTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x - 1, z), 0, hold); ++ chunkProvider.removeTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x + 1, z), 0, hold); ++ chunkProvider.removeTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z - 1), 0, hold); ++ chunkProvider.removeTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z + 1), 0, hold); ++ } ++ + @Override + public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { + int locX = chunk.getPos().x; @@ -274,7 +300,10 @@ index 0000000000..3aa614833d + chunk.world.getChunkAt(locX, locZ - 1); + chunk.world.getChunkAt(locX, locZ + 1); + } else if (chunkEdgeMode == ChunkEdgeMode.WAIT) { -+ if (chunkProvider.getChunkAtIfLoadedImmediately(locX - 1, locZ) == null || chunkProvider.getChunkAtIfLoadedImmediately(locX + 1, locZ) == null || chunkProvider.getChunkAtIfLoadedImmediately(locX, locZ - 1) == null || chunkProvider.getChunkAtIfLoadedImmediately(locX, locZ + 1) == null) { ++ if (chunkProvider.getChunkAtIfCachedImmediately(locX - 1, locZ) == null || ++ chunkProvider.getChunkAtIfCachedImmediately(locX + 1, locZ) == null || ++ chunkProvider.getChunkAtIfCachedImmediately(locX, locZ - 1) == null || ++ chunkProvider.getChunkAtIfCachedImmediately(locX, locZ + 1) == null) { + //Don't create the chunk packet now, wait until nearby chunks are loaded and create it later + return false; + } @@ -302,7 +331,14 @@ index 0000000000..3aa614833d + //Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later + int locX = chunk.getPos().x; + int locZ = chunk.getPos().z; -+ ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this); ++ ++ Integer hold = MCUtil.ensureMain("chunk packet creation", (Supplier)() -> { ++ return this.addXrayTickets(chunk.getPos().x, chunk.getPos().z, (ChunkProviderServer)chunk.world.getChunkProvider()); ++ }); ++ ++ this.onChunkPacketCreate(chunk, chunkSectionSelector, true); // force loads now, we need them ++ ++ ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this, hold); + chunkPacketInfoAntiXray.setNearbyChunks((Chunk)chunk.world.getChunkIfLoadedImmediately(locX - 1, locZ), + (Chunk)chunk.world.getChunkIfLoadedImmediately(locX + 1, locZ), + (Chunk)chunk.world.getChunkIfLoadedImmediately(locX, locZ - 1), @@ -332,108 +368,119 @@ index 0000000000..3aa614833d + private final ChunkSection[] nearbyChunkSections = new ChunkSection[4]; + + public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) { -+ boolean[] solidTemp = null; -+ boolean[] obfuscateTemp = null; -+ dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData()); -+ dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData()); -+ int counter = 0; ++ try { ++ boolean[] solidTemp = null; ++ boolean[] obfuscateTemp = null; ++ dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData()); ++ dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData()); ++ int counter = 0; + -+ for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) { -+ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) { -+ int[] predefinedBlockDataBitsTemp; ++ for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) { ++ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) { ++ int[] predefinedBlockDataBitsTemp; + -+ if (chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex) == ChunkSection.GLOBAL_PALETTE) { -+ predefinedBlockDataBitsTemp = engineMode == EngineMode.HIDE ? chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.NETHER ? predefinedBlockDataBitsNetherrackGlobal : chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.THE_END ? predefinedBlockDataBitsEndStoneGlobal : predefinedBlockDataBitsStoneGlobal : predefinedBlockDataBitsGlobal; -+ } else { -+ predefinedBlockDataBitsTemp = predefinedBlockDataBits == null ? predefinedBlockDataBits = engineMode == EngineMode.HIDE ? new int[1] : new int[predefinedBlockData.length] : predefinedBlockDataBits; ++ if (chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex) == ChunkSection.GLOBAL_PALETTE) { ++ predefinedBlockDataBitsTemp = engineMode == EngineMode.HIDE ? chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.NETHER ? predefinedBlockDataBitsNetherrackGlobal : chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.THE_END ? predefinedBlockDataBitsEndStoneGlobal : predefinedBlockDataBitsStoneGlobal : predefinedBlockDataBitsGlobal; ++ } else { ++ predefinedBlockDataBitsTemp = predefinedBlockDataBits == null ? predefinedBlockDataBits = engineMode == EngineMode.HIDE ? new int[1] : new int[predefinedBlockData.length] : predefinedBlockDataBits; + -+ for (int i = 0; i < predefinedBlockDataBitsTemp.length; i++) { -+ predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).getOrCreateIdFor(chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex)[i]); -+ } -+ } -+ -+ dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); -+ -+ //Check if the chunk section below was not obfuscated -+ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex - 1) == null) { -+ //If so, initialize some stuff -+ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); -+ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); -+ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), solid, solidGlobal); -+ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), obfuscate, obfuscateGlobal); -+ //Read the blocks of the upper layer of the chunk section below if it exists -+ ChunkSection belowChunkSection = null; -+ boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex - 1]) == Chunk.EMPTY_CHUNK_SECTION; -+ -+ for (int z = 0; z < 16; z++) { -+ for (int x = 0; x < 16; x++) { -+ current[z][x] = true; -+ next[z][x] = skipFirstLayer || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(belowChunkSection.getType(x, 15, z))]; ++ for (int i = 0; i < predefinedBlockDataBitsTemp.length; i++) { ++ predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).getOrCreateIdFor(chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex)[i]); + } + } + -+ //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, counter); -+ } ++ dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); + -+ dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); -+ nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex]; ++ //Check if the chunk section below was not obfuscated ++ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex - 1) == null) { ++ //If so, initialize some stuff ++ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); ++ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); ++ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), solid, solidGlobal); ++ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), obfuscate, obfuscateGlobal); ++ //Read the blocks of the upper layer of the chunk section below if it exists ++ ChunkSection belowChunkSection = null; ++ boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex - 1]) == Chunk.EMPTY_CHUNK_SECTION; + -+ //Obfuscate all layers of the current chunk section except the upper one -+ for (int y = 0; y < 15; y++) { -+ boolean[][] temp = current; -+ current = next; -+ next = nextNext; -+ nextNext = temp; -+ counter = obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); -+ } ++ for (int z = 0; z < 16; z++) { ++ for (int x = 0; x < 16; x++) { ++ current[z][x] = true; ++ next[z][x] = skipFirstLayer || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(belowChunkSection.getType(x, 15, z))]; ++ } ++ } + -+ //Check if the chunk section above doesn't need obfuscation -+ if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPredefinedObjects(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 -+ ChunkSection aboveChunkSection; ++ //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, counter); ++ } + -+ if (chunkSectionIndex != 15 && (aboveChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex + 1]) != Chunk.EMPTY_CHUNK_SECTION) { ++ dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); ++ nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex]; ++ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex]; ++ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex]; ++ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex]; ++ ++ //Obfuscate all layers of the current chunk section except the upper one ++ for (int y = 0; y < 15; y++) { + boolean[][] temp = current; + current = next; + next = nextNext; + nextNext = temp; ++ counter = obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); ++ } + -+ for (int z = 0; z < 16; z++) { -+ for (int x = 0; x < 16; x++) { -+ if (!solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(aboveChunkSection.getType(x, 0, z))]) { -+ current[z][x] = true; ++ //Check if the chunk section above doesn't need obfuscation ++ if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPredefinedObjects(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 ++ ChunkSection aboveChunkSection; ++ ++ if (chunkSectionIndex != 15 && (aboveChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex + 1]) != Chunk.EMPTY_CHUNK_SECTION) { ++ boolean[][] temp = current; ++ current = next; ++ next = nextNext; ++ nextNext = temp; ++ ++ for (int z = 0; z < 16; z++) { ++ for (int x = 0; x < 16; x++) { ++ if (!solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(aboveChunkSection.getType(x, 0, z))]) { ++ current[z][x] = true; ++ } + } + } ++ ++ //There is nothing to read anymore ++ dataBitsReader.setBitsPerObject(0); ++ solid[0] = true; ++ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); + } -+ -+ //There is nothing to read anymore -+ dataBitsReader.setBitsPerObject(0); -+ solid[0] = true; -+ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); ++ } 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.getOrCreateIdForIndex(chunkSectionIndex + 1)); ++ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), solid, solidGlobal); ++ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal); ++ boolean[][] temp = current; ++ current = next; ++ next = nextNext; ++ nextNext = temp; ++ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); + } -+ } 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.getOrCreateIdForIndex(chunkSectionIndex + 1)); -+ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), solid, solidGlobal); -+ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal); -+ boolean[][] temp = current; -+ current = next; -+ next = nextNext; -+ nextNext = temp; -+ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); ++ ++ dataBitsWriter.finish(); + } -+ -+ dataBitsWriter.finish(); + } -+ } + -+ chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true); ++ chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true); ++ ++ } finally { ++ MCUtil.ensureMain(null, (Runnable)() -> { ++ Chunk chunk = chunkPacketInfoAntiXray.getChunk(); ++ ChunkCoordIntPair chunkPos = chunk.getPos(); ++ ++ ChunkPacketBlockControllerAntiXray.this.removeXrayTickets(chunkPos.x, chunkPos.z, (ChunkProviderServer) chunk.world.getChunkProvider(), ++ chunkPacketInfoAntiXray.hold); ++ }); ++ } + } + + private int obfuscateLayer(int y, DataBitsReader dataBitsReader, DataBitsWriter dataBitsWriter, boolean[] solid, boolean[] obfuscate, int[] predefinedBlockDataBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, ChunkSection[] nearbyChunkSections, int counter) { @@ -892,10 +939,10 @@ index 0000000000..a68bace353 +} 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 0000000000..e255a45fa3 +index 0000000000..c8856f1a48 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java -@@ -0,0 +1,29 @@ +@@ -0,0 +1,32 @@ +package com.destroystokyo.paper.antixray; + +import net.minecraft.server.Chunk; @@ -906,10 +953,13 @@ index 0000000000..e255a45fa3 + + private Chunk[] nearbyChunks; + private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray; ++ public final Integer hold; + -+ public ChunkPacketInfoAntiXray(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) { ++ public ChunkPacketInfoAntiXray(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector, ++ ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray, Integer hold) { + super(packetPlayOutMapChunk, chunk, chunkSectionSelector); + this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray; ++ this.hold = hold; + } + + public Chunk[] getNearbyChunks() { @@ -1422,21 +1472,23 @@ index ef71a1feb3..316fb7cc3c 100644 } diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 761cd1355b..cb3363a01e 100644 +index 761cd1355b..aff4967d4c 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -150,6 +150,9 @@ public class PlayerChunk { - } - - public void a(Chunk chunk) { -+ if (!chunk.world.chunkPacketBlockController.onChunkPacketCreate(chunk, '\uffff', false)) { // Paper - Anti-Xray - Load nearby chunks if necessary -+ return; -+ } - if (this.dirtyCount != 0 || this.u != 0 || this.t != 0) { +@@ -154,6 +154,11 @@ public class PlayerChunk { World world = chunk.getWorld(); + if (this.dirtyCount == 64) { ++ // Paper start - Anti-Xray - Load nearby chunks if necessary ++ if (!chunk.world.chunkPacketBlockController.onChunkPacketCreate(chunk, '\uffff', false)) { ++ return; ++ } ++ // Paper end + this.s = -1; + } + diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 8e16d6ac87..5e0c73ece6 100644 +index 8e16d6ac87..62e804da7c 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -491,7 +491,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -1448,15 +1500,6 @@ index 8e16d6ac87..5e0c73ece6 100644 }, this.executor); } -@@ -1111,6 +1111,8 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - - private void a(EntityPlayer entityplayer, Packet[] apacket, Chunk chunk) { - if (apacket[0] == null) { -+ // Note that this is ALWAYS the case as of 1.14 (at least once). re-check on update -+ chunk.world.chunkPacketBlockController.onChunkPacketCreate(chunk, '\uffff', true); // Paper - Anti-Xray - Load nearby chunks if necessary - apacket[0] = new PacketPlayOutMapChunk(chunk, 65535); - apacket[1] = new PacketPlayOutLightUpdate(chunk.getPos(), this.lightEngine); - } diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java index 83b36b3e7f..8fef6008d1 100644 --- a/src/main/java/net/minecraft/server/PlayerInteractManager.java @@ -1513,6 +1556,18 @@ index 6bdd7dda04..7bad12eb00 100644 } return this.j[i]; +diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java +index d2bf158a91..2eeae60d52 100644 +--- a/src/main/java/net/minecraft/server/TicketType.java ++++ b/src/main/java/net/minecraft/server/TicketType.java +@@ -20,6 +20,7 @@ public class TicketType { + public static final TicketType POST_TELEPORT = a("post_teleport", Integer::compareTo, 5); + public static final TicketType UNKNOWN = a("unknown", Comparator.comparingLong(ChunkCoordIntPair::pair), 1); + public static final TicketType PLUGIN = a("plugin", (a, b) -> 0); // CraftBukkit ++ public static final TicketType ANTIXRAY = a("antixray", Integer::compareTo); // Paper - Anti-Xray + + public static TicketType a(String s, Comparator comparator) { + return new TicketType<>(s, comparator, 0L); diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java index a7a35d6a6f..ff64089857 100644 --- a/src/main/java/net/minecraft/server/World.java From 5ffa0dae250c6adf48985f729e7dbc708681f5e0 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Fri, 28 Jun 2019 08:05:52 -0700 Subject: [PATCH 6/8] Do not force load chunks for plugin constructed packets --- ...8-Anti-Xray.patch => 0407-Anti-Xray.patch} | 121 +++++++++++------- 1 file changed, 78 insertions(+), 43 deletions(-) rename Spigot-Server-Patches/{0408-Anti-Xray.patch => 0407-Anti-Xray.patch} (95%) diff --git a/Spigot-Server-Patches/0408-Anti-Xray.patch b/Spigot-Server-Patches/0407-Anti-Xray.patch similarity index 95% rename from Spigot-Server-Patches/0408-Anti-Xray.patch rename to Spigot-Server-Patches/0407-Anti-Xray.patch index 6ae0d7760c..a59bf33a3b 100644 --- a/Spigot-Server-Patches/0408-Anti-Xray.patch +++ b/Spigot-Server-Patches/0407-Anti-Xray.patch @@ -1,11 +1,11 @@ -From a63f34ceb95fa2baa35df2afbe3c3f823fdac89f Mon Sep 17 00:00:00 2001 +From 1d5692a680ebc830fe090a12067d29f294205bfe Mon Sep 17 00:00:00 2001 From: stonar96 Date: Mon, 20 Aug 2018 03:03:58 +0200 Subject: [PATCH] Anti-Xray diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 58109e1308..93d397d317 100644 +index 58109e130..93d397d31 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -1,7 +1,11 @@ @@ -57,10 +57,10 @@ index 58109e1308..93d397d317 100644 } 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 0000000000..dc534d239e +index 000000000..2f70fcd19 --- /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.server.BlockPosition; @@ -90,7 +90,8 @@ index 0000000000..dc534d239e + return true; + } + -+ public ChunkPacketInfo getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { ++ public ChunkPacketInfo getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, ++ int chunkSectionSelector, boolean forceLoad) { + return null; + } + @@ -108,10 +109,10 @@ index 0000000000..dc534d239e +} 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 0000000000..6f7069f548 +index 000000000..c113f71c0 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -0,0 +1,738 @@ +@@ -0,0 +1,749 @@ +package com.destroystokyo.paper.antixray; + +import java.util.ArrayList; @@ -285,6 +286,15 @@ index 0000000000..6f7069f548 + chunkProvider.removeTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z + 1), 0, hold); + } + ++ private void loadNeighbours(Chunk chunk) { ++ int locX = chunk.getPos().x; ++ int locZ = chunk.getPos().z; ++ chunk.world.getChunkAt(locX - 1, locZ); ++ chunk.world.getChunkAt(locX + 1, locZ); ++ chunk.world.getChunkAt(locX, locZ - 1); ++ chunk.world.getChunkAt(locX, locZ + 1); ++ } ++ + @Override + public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { + int locX = chunk.getPos().x; @@ -295,10 +305,7 @@ index 0000000000..6f7069f548 + //Load nearby chunks if necessary + if (force || chunkEdgeMode == ChunkEdgeMode.LOAD) { // TODO temporary + // if forced, load NOW; -+ chunk.world.getChunkAt(locX - 1, locZ); -+ chunk.world.getChunkAt(locX + 1, locZ); -+ chunk.world.getChunkAt(locX, locZ - 1); -+ chunk.world.getChunkAt(locX, locZ + 1); ++ this.loadNeighbours(chunk); + } else if (chunkEdgeMode == ChunkEdgeMode.WAIT) { + if (chunkProvider.getChunkAtIfCachedImmediately(locX - 1, locZ) == null || + chunkProvider.getChunkAtIfCachedImmediately(locX + 1, locZ) == null || @@ -327,16 +334,19 @@ index 0000000000..6f7069f548 + } + + @Override -+ public ChunkPacketInfoAntiXray getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { ++ public ChunkPacketInfoAntiXray getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, ++ int chunkSectionSelector, boolean forceLoad) { + //Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later + int locX = chunk.getPos().x; + int locZ = chunk.getPos().z; + -+ Integer hold = MCUtil.ensureMain("chunk packet creation", (Supplier)() -> { ++ Integer hold = !forceLoad ? null : MCUtil.ensureMain("chunk packet creation", (Supplier)() -> { + return this.addXrayTickets(chunk.getPos().x, chunk.getPos().z, (ChunkProviderServer)chunk.world.getChunkProvider()); + }); + -+ this.onChunkPacketCreate(chunk, chunkSectionSelector, true); // force loads now, we need them ++ if (forceLoad) { ++ this.loadNeighbours(chunk);// force loads now, we need them ++ } + + ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this, hold); + chunkPacketInfoAntiXray.setNearbyChunks((Chunk)chunk.world.getChunkIfLoadedImmediately(locX - 1, locZ), @@ -473,13 +483,15 @@ index 0000000000..6f7069f548 + chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true); + + } finally { -+ MCUtil.ensureMain(null, (Runnable)() -> { -+ Chunk chunk = chunkPacketInfoAntiXray.getChunk(); -+ ChunkCoordIntPair chunkPos = chunk.getPos(); ++ if (chunkPacketInfoAntiXray.hold != null) { ++ MCUtil.ensureMain(null, (Runnable) () -> { ++ Chunk chunk = chunkPacketInfoAntiXray.getChunk(); ++ ChunkCoordIntPair chunkPos = chunk.getPos(); + -+ ChunkPacketBlockControllerAntiXray.this.removeXrayTickets(chunkPos.x, chunkPos.z, (ChunkProviderServer) chunk.world.getChunkProvider(), -+ chunkPacketInfoAntiXray.hold); -+ }); ++ ChunkPacketBlockControllerAntiXray.this.removeXrayTickets(chunkPos.x, chunkPos.z, (ChunkProviderServer) chunk.world.getChunkProvider(), ++ chunkPacketInfoAntiXray.hold); ++ }); ++ } + } + } + @@ -852,7 +864,7 @@ index 0000000000..6f7069f548 +} 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 0000000000..a68bace353 +index 000000000..a68bace35 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java @@ -0,0 +1,81 @@ @@ -939,7 +951,7 @@ index 0000000000..a68bace353 +} 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 0000000000..c8856f1a48 +index 000000000..c8856f1a4 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java @@ -0,0 +1,32 @@ @@ -977,7 +989,7 @@ index 0000000000..c8856f1a48 +} 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 0000000000..cc586827aa +index 000000000..cc586827a --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java @@ -0,0 +1,56 @@ @@ -1039,7 +1051,7 @@ index 0000000000..cc586827aa +} 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 0000000000..37093419cf +index 000000000..37093419c --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java @@ -0,0 +1,84 @@ @@ -1128,7 +1140,7 @@ index 0000000000..37093419cf + } +} diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index e2a48695df..d19412f186 100644 +index e2a48695d..d19412f18 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -315,7 +315,7 @@ public class Chunk implements IChunkAccess { @@ -1141,7 +1153,7 @@ index e2a48695df..d19412f186 100644 } diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 287f113581..f88e3d957f 100644 +index 287f11358..f88e3d957 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -74,7 +74,7 @@ public class ChunkRegionLoader { @@ -1163,7 +1175,7 @@ index 287f113581..f88e3d957f 100644 object = protochunk; protochunk.a(abiomebase); diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java -index c4c181c1d5..d6b327eff2 100644 +index c4c181c1d..d6b327eff 100644 --- a/src/main/java/net/minecraft/server/ChunkSection.java +++ b/src/main/java/net/minecraft/server/ChunkSection.java @@ -6,21 +6,31 @@ public class ChunkSection { @@ -1202,7 +1214,7 @@ index c4c181c1d5..d6b327eff2 100644 public IBlockData getType(int i, int j, int k) { diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java -index e05b9d606a..cbc9dc902e 100644 +index e05b9d606..cbc9dc902 100644 --- a/src/main/java/net/minecraft/server/DataPaletteBlock.java +++ b/src/main/java/net/minecraft/server/DataPaletteBlock.java @@ -1,5 +1,6 @@ @@ -1323,7 +1335,7 @@ index e05b9d606a..cbc9dc902e 100644 if (this.h == this.b) { diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java -index ba23d28335..8dcaaf8676 100644 +index ba23d2833..8dcaaf867 100644 --- a/src/main/java/net/minecraft/server/NetworkManager.java +++ b/src/main/java/net/minecraft/server/NetworkManager.java @@ -166,8 +166,8 @@ public class NetworkManager extends SimpleChannelInboundHandler> { @@ -1384,7 +1396,7 @@ index ba23d28335..8dcaaf8676 100644 public void a() { this.o(); diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java -index ef71a1feb3..316fb7cc3c 100644 +index ef71a1feb..04e1241e9 100644 --- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java +++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java @@ -1,5 +1,6 @@ @@ -1407,15 +1419,20 @@ index ef71a1feb3..316fb7cc3c 100644 // Paper start private final java.util.List extraPackets = new java.util.ArrayList<>(); -@@ -30,6 +34,7 @@ public class PacketPlayOutMapChunk implements Packet { +@@ -30,6 +34,12 @@ public class PacketPlayOutMapChunk implements Packet { } // Paper end public PacketPlayOutMapChunk(Chunk chunk, int i) { -+ ChunkPacketInfo chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info ++ // Paper start - add forceLoad param ++ this(chunk, i, false); ++ } ++ public PacketPlayOutMapChunk(Chunk chunk, int i, boolean forceLoad) { ++ // Paper end ++ ChunkPacketInfo chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i, forceLoad); // Paper - Anti-Xray - Add chunk packet info ChunkCoordIntPair chunkcoordintpair = chunk.getPos(); this.a = chunkcoordintpair.x; -@@ -48,7 +53,12 @@ public class PacketPlayOutMapChunk implements Packet { +@@ -48,7 +58,12 @@ public class PacketPlayOutMapChunk implements Packet { } this.e = new byte[this.a(chunk, i)]; @@ -1429,7 +1446,7 @@ index ef71a1feb3..316fb7cc3c 100644 this.f = Lists.newArrayList(); iterator = chunk.getTileEntities().entrySet().iterator(); int totalSigns = 0; // Paper -@@ -74,9 +84,19 @@ public class PacketPlayOutMapChunk implements Packet { +@@ -74,9 +89,19 @@ public class PacketPlayOutMapChunk implements Packet { this.f.add(nbttagcompound); } } @@ -1449,7 +1466,7 @@ index ef71a1feb3..316fb7cc3c 100644 @Override public void a(PacketDataSerializer packetdataserializer) throws IOException { this.a = packetdataserializer.readInt(); -@@ -135,6 +155,11 @@ public class PacketPlayOutMapChunk implements Packet { +@@ -135,6 +160,11 @@ public class PacketPlayOutMapChunk implements Packet { public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, chunkSectionSelector); } // Paper - OBFHELPER public int a(PacketDataSerializer packetdataserializer, Chunk chunk, int i) { @@ -1461,7 +1478,7 @@ index ef71a1feb3..316fb7cc3c 100644 int j = 0; ChunkSection[] achunksection = chunk.getSections(); int k = 0; -@@ -146,7 +171,8 @@ public class PacketPlayOutMapChunk implements Packet { +@@ -146,7 +176,8 @@ public class PacketPlayOutMapChunk implements Packet { if (chunksection != Chunk.a && (!this.f() || !chunksection.c()) && (i & 1 << k) != 0) { j |= 1 << k; @@ -1472,7 +1489,7 @@ index ef71a1feb3..316fb7cc3c 100644 } diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 761cd1355b..aff4967d4c 100644 +index 761cd1355..956a47132 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -154,6 +154,11 @@ public class PlayerChunk { @@ -1487,8 +1504,17 @@ index 761cd1355b..aff4967d4c 100644 this.s = -1; } +@@ -186,7 +191,7 @@ public class PlayerChunk { + this.a(world, blockposition); + } + } else if (this.dirtyCount == 64) { +- this.a(new PacketPlayOutMapChunk(chunk, this.r), false); ++ this.a(new PacketPlayOutMapChunk(chunk, this.r, true), false); // Paper - Anti-Xray + } else if (this.dirtyCount != 0) { + this.a(new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, chunk), false); + diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 8e16d6ac87..62e804da7c 100644 +index 8e16d6ac8..f48633111 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -491,7 +491,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -1500,8 +1526,17 @@ index 8e16d6ac87..62e804da7c 100644 }, this.executor); } +@@ -1111,7 +1111,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + + private void a(EntityPlayer entityplayer, Packet[] apacket, Chunk chunk) { + if (apacket[0] == null) { +- apacket[0] = new PacketPlayOutMapChunk(chunk, 65535); ++ apacket[0] = new PacketPlayOutMapChunk(chunk, 65535, true); // Paper - Anti-Xray + apacket[1] = new PacketPlayOutLightUpdate(chunk.getPos(), this.lightEngine); + } + diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java -index 83b36b3e7f..8fef6008d1 100644 +index 83b36b3e7..8fef6008d 100644 --- a/src/main/java/net/minecraft/server/PlayerInteractManager.java +++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java @@ -207,6 +207,8 @@ public class PlayerInteractManager { @@ -1514,7 +1549,7 @@ index 83b36b3e7f..8fef6008d1 100644 public void a(BlockPosition blockposition) { diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java -index 6bdd7dda04..7bad12eb00 100644 +index 6bdd7dda0..7bad12eb0 100644 --- a/src/main/java/net/minecraft/server/ProtoChunk.java +++ b/src/main/java/net/minecraft/server/ProtoChunk.java @@ -44,16 +44,28 @@ public class ProtoChunk implements IChunkAccess { @@ -1557,7 +1592,7 @@ index 6bdd7dda04..7bad12eb00 100644 return this.j[i]; diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java -index d2bf158a91..2eeae60d52 100644 +index d2bf158a9..2eeae60d5 100644 --- a/src/main/java/net/minecraft/server/TicketType.java +++ b/src/main/java/net/minecraft/server/TicketType.java @@ -20,6 +20,7 @@ public class TicketType { @@ -1569,7 +1604,7 @@ index d2bf158a91..2eeae60d52 100644 public static TicketType a(String s, Comparator comparator) { return new TicketType<>(s, comparator, 0L); diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index a7a35d6a6f..ff64089857 100644 +index a7a35d6a6..ff6408985 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -2,6 +2,8 @@ package net.minecraft.server; @@ -1606,7 +1641,7 @@ index a7a35d6a6f..ff64089857 100644 if (iblockdata1 == null) { // CraftBukkit start - remove blockstate if failed diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java -index 7772d59005..4570ed9991 100644 +index 7772d5900..4570ed999 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java @@ -21,9 +21,11 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { From 0a667728c8288bf54d560bd1df68d02b7f2ae80f Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Mon, 1 Jul 2019 14:36:31 -0700 Subject: [PATCH 7/8] Resolve crash issue by shoving chunk loads onto the next tick --- Spigot-Server-Patches/0407-Anti-Xray.patch | 123 +++++++++++++-------- 1 file changed, 75 insertions(+), 48 deletions(-) diff --git a/Spigot-Server-Patches/0407-Anti-Xray.patch b/Spigot-Server-Patches/0407-Anti-Xray.patch index a59bf33a3b..a4a7454fbd 100644 --- a/Spigot-Server-Patches/0407-Anti-Xray.patch +++ b/Spigot-Server-Patches/0407-Anti-Xray.patch @@ -1,11 +1,11 @@ -From 1d5692a680ebc830fe090a12067d29f294205bfe Mon Sep 17 00:00:00 2001 +From ca6f7c97ec5f1f366d5f42fe49360b8fc4dea510 Mon Sep 17 00:00:00 2001 From: stonar96 Date: Mon, 20 Aug 2018 03:03:58 +0200 Subject: [PATCH] Anti-Xray diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 58109e130..93d397d31 100644 +index 58109e1308..93d397d317 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -1,7 +1,11 @@ @@ -57,7 +57,7 @@ index 58109e130..93d397d31 100644 } 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 000000000..2f70fcd19 +index 0000000000..f7e376ce6a --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java @@ -0,0 +1,46 @@ @@ -95,7 +95,7 @@ index 000000000..2f70fcd19 + return null; + } + -+ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { ++ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo, boolean loadChunks, Integer ticketHold) { + packetPlayOutMapChunk.setReady(true); + } + @@ -109,10 +109,10 @@ index 000000000..2f70fcd19 +} 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 000000000..c113f71c0 +index 0000000000..9d8bee5cac --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -0,0 +1,749 @@ +@@ -0,0 +1,777 @@ +package com.destroystokyo.paper.antixray; + +import java.util.ArrayList; @@ -125,6 +125,7 @@ index 000000000..c113f71c0 +import java.util.function.Supplier; + +import net.minecraft.server.*; ++import org.bukkit.Bukkit; +import org.bukkit.World.Environment; + +import com.destroystokyo.paper.PaperWorldConfig; @@ -336,28 +337,55 @@ index 000000000..c113f71c0 + @Override + public ChunkPacketInfoAntiXray getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, + int chunkSectionSelector, boolean forceLoad) { -+ //Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later -+ int locX = chunk.getPos().x; -+ int locZ = chunk.getPos().z; ++ // 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. + -+ Integer hold = !forceLoad ? null : MCUtil.ensureMain("chunk packet creation", (Supplier)() -> { -+ return this.addXrayTickets(chunk.getPos().x, chunk.getPos().z, (ChunkProviderServer)chunk.world.getChunkProvider()); -+ }); -+ -+ if (forceLoad) { -+ this.loadNeighbours(chunk);// force loads now, we need them -+ } -+ -+ ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this, hold); -+ chunkPacketInfoAntiXray.setNearbyChunks((Chunk)chunk.world.getChunkIfLoadedImmediately(locX - 1, locZ), -+ (Chunk)chunk.world.getChunkIfLoadedImmediately(locX + 1, locZ), -+ (Chunk)chunk.world.getChunkIfLoadedImmediately(locX, locZ - 1), -+ (Chunk)chunk.world.getChunkIfLoadedImmediately(locX, locZ + 1)); ++ ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this); + return chunkPacketInfoAntiXray; + } + + @Override -+ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { ++ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo, boolean loadChunks, Integer hold) { ++ if (!Bukkit.isPrimaryThread()) { ++ // plugins? ++ final Integer finalHold = hold; ++ MinecraftServer.getServer().scheduleOnMain(() -> { ++ this.modifyBlocks(packetPlayOutMapChunk, chunkPacketInfo, loadChunks, finalHold); ++ }); ++ return; ++ } ++ Chunk chunk = chunkPacketInfo.getChunk(); ++ int locX = chunk.getPos().x; ++ int locZ = chunk.getPos().z; ++ WorldServer world = (WorldServer)chunk.world; ++ ++ Chunk[] chunks = new Chunk[] { ++ (Chunk)world.getChunkIfLoadedImmediately(locX - 1, locZ), ++ (Chunk)world.getChunkIfLoadedImmediately(locX + 1, locZ), ++ (Chunk)world.getChunkIfLoadedImmediately(locX, locZ - 1), ++ (Chunk)world.getChunkIfLoadedImmediately(locX, locZ + 1) ++ }; ++ ++ if (loadChunks) { ++ // Note: This ugly hack is to get us out of the general chunk load/unload queue to prevent deadlock ++ ++ if (chunks[0] == null || chunks[1] == null || chunks[2] == null || chunks[3] == null) { ++ // we need to load ++ MinecraftServer.getServer().scheduleOnMain(() -> { ++ Integer ticketHold = this.addXrayTickets(locX, locZ, world.getChunkProvider()); ++ this.loadNeighbours(chunk); ++ this.modifyBlocks(packetPlayOutMapChunk, chunkPacketInfo, false, ticketHold); ++ }); ++ return; ++ } ++ ++ hold = this.addXrayTickets(locX, locZ, world.getChunkProvider()); ++ // fall through to normal behavior, our chunks are now loaded & have a ticket ++ } ++ ++ ((ChunkPacketInfoAntiXray)chunkPacketInfo).setNearbyChunks(chunks); ++ ((ChunkPacketInfoAntiXray)chunkPacketInfo).ticketHold = hold; ++ + if (asynchronous) { + executorService.submit((ChunkPacketInfoAntiXray) chunkPacketInfo); + } else { @@ -483,13 +511,13 @@ index 000000000..c113f71c0 + chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true); + + } finally { -+ if (chunkPacketInfoAntiXray.hold != null) { ++ if (chunkPacketInfoAntiXray.ticketHold != null) { + MCUtil.ensureMain(null, (Runnable) () -> { + Chunk chunk = chunkPacketInfoAntiXray.getChunk(); + ChunkCoordIntPair chunkPos = chunk.getPos(); + + ChunkPacketBlockControllerAntiXray.this.removeXrayTickets(chunkPos.x, chunkPos.z, (ChunkProviderServer) chunk.world.getChunkProvider(), -+ chunkPacketInfoAntiXray.hold); ++ chunkPacketInfoAntiXray.ticketHold); + }); + } + } @@ -864,7 +892,7 @@ index 000000000..c113f71c0 +} 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 000000000..a68bace35 +index 0000000000..a68bace353 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java @@ -0,0 +1,81 @@ @@ -951,10 +979,10 @@ index 000000000..a68bace35 +} 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 000000000..c8856f1a4 +index 0000000000..067dfb2f14 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java -@@ -0,0 +1,32 @@ +@@ -0,0 +1,31 @@ +package com.destroystokyo.paper.antixray; + +import net.minecraft.server.Chunk; @@ -965,13 +993,12 @@ index 000000000..c8856f1a4 + + private Chunk[] nearbyChunks; + private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray; -+ public final Integer hold; ++ public Integer ticketHold; + + public ChunkPacketInfoAntiXray(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector, -+ ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray, Integer hold) { ++ ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) { + super(packetPlayOutMapChunk, chunk, chunkSectionSelector); + this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray; -+ this.hold = hold; + } + + public Chunk[] getNearbyChunks() { @@ -989,7 +1016,7 @@ index 000000000..c8856f1a4 +} 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 000000000..cc586827a +index 0000000000..cc586827aa --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java @@ -0,0 +1,56 @@ @@ -1051,7 +1078,7 @@ index 000000000..cc586827a +} 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 000000000..37093419c +index 0000000000..37093419cf --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java @@ -0,0 +1,84 @@ @@ -1140,7 +1167,7 @@ index 000000000..37093419c + } +} diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index e2a48695d..d19412f18 100644 +index e2a48695df..d19412f186 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -315,7 +315,7 @@ public class Chunk implements IChunkAccess { @@ -1153,7 +1180,7 @@ index e2a48695d..d19412f18 100644 } diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 287f11358..f88e3d957 100644 +index 287f113581..f88e3d957f 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -74,7 +74,7 @@ public class ChunkRegionLoader { @@ -1175,7 +1202,7 @@ index 287f11358..f88e3d957 100644 object = protochunk; protochunk.a(abiomebase); diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java -index c4c181c1d..d6b327eff 100644 +index c4c181c1d5..d6b327eff2 100644 --- a/src/main/java/net/minecraft/server/ChunkSection.java +++ b/src/main/java/net/minecraft/server/ChunkSection.java @@ -6,21 +6,31 @@ public class ChunkSection { @@ -1214,7 +1241,7 @@ index c4c181c1d..d6b327eff 100644 public IBlockData getType(int i, int j, int k) { diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java -index e05b9d606..cbc9dc902 100644 +index e05b9d606a..cbc9dc902e 100644 --- a/src/main/java/net/minecraft/server/DataPaletteBlock.java +++ b/src/main/java/net/minecraft/server/DataPaletteBlock.java @@ -1,5 +1,6 @@ @@ -1335,7 +1362,7 @@ index e05b9d606..cbc9dc902 100644 if (this.h == this.b) { diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java -index ba23d2833..8dcaaf867 100644 +index ba23d28335..8dcaaf8676 100644 --- a/src/main/java/net/minecraft/server/NetworkManager.java +++ b/src/main/java/net/minecraft/server/NetworkManager.java @@ -166,8 +166,8 @@ public class NetworkManager extends SimpleChannelInboundHandler> { @@ -1396,7 +1423,7 @@ index ba23d2833..8dcaaf867 100644 public void a() { this.o(); diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java -index ef71a1feb..04e1241e9 100644 +index ef71a1feb3..483317608c 100644 --- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java +++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java @@ -1,5 +1,6 @@ @@ -1450,7 +1477,7 @@ index ef71a1feb..04e1241e9 100644 this.f.add(nbttagcompound); } } -+ chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks ++ chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo, forceLoad, null); // Paper - Anti-Xray - Modify blocks + } + // Paper start - Async-Anti-Xray - Getter and Setter for the ready flag @@ -1489,7 +1516,7 @@ index ef71a1feb..04e1241e9 100644 } diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 761cd1355..956a47132 100644 +index 761cd1355b..956a47132f 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -154,6 +154,11 @@ public class PlayerChunk { @@ -1514,7 +1541,7 @@ index 761cd1355..956a47132 100644 this.a(new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, chunk), false); diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 8e16d6ac8..f48633111 100644 +index 8e16d6ac87..f486331118 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -491,7 +491,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -1536,7 +1563,7 @@ index 8e16d6ac8..f48633111 100644 } diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java -index 83b36b3e7..8fef6008d 100644 +index 83b36b3e7f..8fef6008d1 100644 --- a/src/main/java/net/minecraft/server/PlayerInteractManager.java +++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java @@ -207,6 +207,8 @@ public class PlayerInteractManager { @@ -1549,7 +1576,7 @@ index 83b36b3e7..8fef6008d 100644 public void a(BlockPosition blockposition) { diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java -index 6bdd7dda0..7bad12eb0 100644 +index 6bdd7dda04..7bad12eb00 100644 --- a/src/main/java/net/minecraft/server/ProtoChunk.java +++ b/src/main/java/net/minecraft/server/ProtoChunk.java @@ -44,16 +44,28 @@ public class ProtoChunk implements IChunkAccess { @@ -1592,7 +1619,7 @@ index 6bdd7dda0..7bad12eb0 100644 return this.j[i]; diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java -index d2bf158a9..2eeae60d5 100644 +index d2bf158a91..2eeae60d52 100644 --- a/src/main/java/net/minecraft/server/TicketType.java +++ b/src/main/java/net/minecraft/server/TicketType.java @@ -20,6 +20,7 @@ public class TicketType { @@ -1604,7 +1631,7 @@ index d2bf158a9..2eeae60d5 100644 public static TicketType a(String s, Comparator comparator) { return new TicketType<>(s, comparator, 0L); diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index a7a35d6a6..ff6408985 100644 +index 11113c9614..ae286aaf29 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -2,6 +2,8 @@ package net.minecraft.server; @@ -1641,7 +1668,7 @@ index a7a35d6a6..ff6408985 100644 if (iblockdata1 == null) { // CraftBukkit start - remove blockstate if failed diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java -index 7772d5900..4570ed999 100644 +index 7772d59005..4570ed9991 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java @@ -21,9 +21,11 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { @@ -1666,5 +1693,5 @@ index 7772d5900..4570ed999 100644 return section; } -- -2.21.0 +2.22.0 From e679a7c11d58364573055ec3047d6a97382ca55b Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Mon, 1 Jul 2019 14:53:51 -0700 Subject: [PATCH 8/8] Config migrations --- Spigot-Server-Patches/0407-Anti-Xray.patch | 30 +++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/Spigot-Server-Patches/0407-Anti-Xray.patch b/Spigot-Server-Patches/0407-Anti-Xray.patch index a4a7454fbd..4c33ef8423 100644 --- a/Spigot-Server-Patches/0407-Anti-Xray.patch +++ b/Spigot-Server-Patches/0407-Anti-Xray.patch @@ -1,11 +1,26 @@ -From ca6f7c97ec5f1f366d5f42fe49360b8fc4dea510 Mon Sep 17 00:00:00 2001 +From 5b18d35d10af4ba8c655f65dc378abf8c1d81c98 Mon Sep 17 00:00:00 2001 From: stonar96 Date: Mon, 20 Aug 2018 03:03:58 +0200 Subject: [PATCH] Anti-Xray +diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java +index 81987e4ad9..5942c3438e 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java +@@ -71,8 +71,8 @@ public class PaperConfig { + commands = new HashMap(); + commands.put("paper", new PaperCommand("paper")); + +- version = getInt("config-version", 18); +- set("config-version", 18); ++ version = getInt("config-version", 19); ++ set("config-version", 19); + readConfig(PaperConfig.class, null); + } + diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 58109e1308..93d397d317 100644 +index 58109e1308..b03d3ee84b 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -1,7 +1,11 @@ @@ -20,7 +35,7 @@ index 58109e1308..93d397d317 100644 import org.bukkit.Bukkit; import org.bukkit.configuration.file.YamlConfiguration; import org.spigotmc.SpigotWorldConfig; -@@ -509,4 +513,34 @@ public class PaperWorldConfig { +@@ -509,4 +513,43 @@ public class PaperWorldConfig { private void maxAutoSaveChunksPerTick() { maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24); } @@ -52,6 +67,15 @@ index 58109e1308..93d397d317 100644 + updateRadius = getInt("anti-xray.update-radius", 2); + hiddenBlocks = getList("anti-xray.hidden-blocks", Arrays.asList("gold_ore", "iron_ore", "coal_ore", "lapis_ore", "mossy_cobblestone", "obsidian", "chest", "diamond_ore", "redstone_ore", "clay", "emerald_ore", "ender_chest")); + replacementBlocks = getList("anti-xray.replacement-blocks", Arrays.asList("stone", "oak_planks")); ++ if (PaperConfig.version < 19) { ++ hiddenBlocks.remove("lit_redstone_ore"); ++ int index = replacementBlocks.indexOf("planks"); ++ if (index != -1) { ++ replacementBlocks.set(index, "oak_planks"); ++ } ++ set("anti-xray.hidden-blocks", hiddenBlocks); ++ set("anti-xray.replacement-blocks", replacementBlocks); ++ } + log("Anti-Xray: " + (antiXray ? "enabled" : "disabled") + " / Engine Mode: " + engineMode.getDescription() + " / Chunk Edge Mode: " + chunkEdgeMode.getDescription() + " / Up to " + ((maxChunkSectionIndex + 1) * 16) + " blocks / Update Radius: " + updateRadius); + } }