2019-04-05 11:17:10 +02:00
From 5d077d44d4bbe2d4436cafe783621f53061000c8 Mon Sep 17 00:00:00 2001
2018-08-26 04:37:58 +02:00
From: stonar96 <minecraft.stonar96@gmail.com>
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
2019-04-05 07:27:37 +02:00
index e87fb94c0..8f4926222 100644
2018-08-26 04:37:58 +02:00
--- 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;
2019-04-05 07:27:37 +02:00
@@ -522,4 +525,34 @@ public class PaperWorldConfig {
2018-08-26 04:37:58 +02:00
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;
2018-08-26 20:11:49 +02:00
+ public List<String> hiddenBlocks;
+ public List<String> replacementBlocks;
2018-08-26 04:37:58 +02:00
+ 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;
2019-01-26 01:11:42 +01:00
+ chunkEdgeMode = ChunkEdgeMode.getById(getInt("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId()));
2018-08-26 04:37:58 +02:00
+ chunkEdgeMode = chunkEdgeMode == null ? ChunkEdgeMode.DEFAULT : chunkEdgeMode;
2019-01-13 00:06:26 +01:00
+
2019-01-26 01:11:42 +01:00
+ 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());
2019-01-13 00:06:26 +01:00
+ }
+
2018-08-26 04:37:58 +02:00
+ maxChunkSectionIndex = getInt("anti-xray.max-chunk-section-index", 3);
+ maxChunkSectionIndex = maxChunkSectionIndex > 15 ? 15 : maxChunkSectionIndex;
+ updateRadius = getInt("anti-xray.update-radius", 2);
2018-08-26 20:11:49 +02:00
+ 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"));
2018-08-26 04:37:58 +02:00
+ 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
2019-01-26 01:11:42 +01:00
index 000000000..1ba8477bf
2018-08-26 04:37:58 +02:00
--- /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<IBlockData> getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
+ return null;
+ }
+
+ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo<IBlockData> 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
2019-01-26 01:11:42 +01:00
index 000000000..e3da35b6b
2018-08-26 04:37:58 +02:00
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
2019-01-21 21:48:27 +01:00
@@ -0,0 +1,684 @@
2018-08-26 04:37:58 +02:00
+package com.destroystokyo.paper.antixray;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
2018-08-26 20:11:49 +02:00
+import net.minecraft.server.IRegistry;
+import net.minecraft.server.MinecraftKey;
2018-08-26 04:37:58 +02:00
+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;
2019-01-21 08:08:58 +01:00
+import net.minecraft.server.MinecraftServer;
2018-08-26 04:37:58 +02:00
+import net.minecraft.server.PacketPlayOutMapChunk;
+import net.minecraft.server.PlayerInteractManager;
+import net.minecraft.server.World;
2019-01-21 08:08:58 +01:00
+import net.minecraft.server.WorldServer;
2018-08-26 04:37:58 +02:00
+
+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;
2018-09-01 06:30:04 +02:00
+ 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())};
2018-08-26 04:37:58 +02:00
+ } else {
+ Set<IBlockData> predefinedBlockDataSet = new HashSet<IBlockData>();
+
2018-08-26 20:11:49 +02:00
+ for (String id : paperWorldConfig.hiddenBlocks) {
+ Block block = IRegistry.BLOCK.get(new MinecraftKey(id));
2018-08-26 04:37:58 +02:00
+
+ 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++) {
2018-09-01 06:30:04 +02:00
+ predefinedBlockDataBitsGlobal[i] = ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(predefinedBlockData[i]);
2018-08-26 04:37:58 +02:00
+ }
+
+ predefinedBlockDataBitsStoneGlobal = null;
+ predefinedBlockDataBitsNetherrackGlobal = null;
+ predefinedBlockDataBitsEndStoneGlobal = null;
+ }
+
2018-08-26 20:11:49 +02:00
+ for (String id : (engineMode == EngineMode.HIDE) ? paperWorldConfig.hiddenBlocks : paperWorldConfig.replacementBlocks) {
+ Block block = IRegistry.BLOCK.get(new MinecraftKey(id));
2018-08-26 04:37:58 +02:00
+
+ if (block != null) {
2018-09-01 06:30:04 +02:00
+ obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(block.getBlockData())] = true;
2018-08-26 04:37:58 +02:00
+ }
+ }
+
+ 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
2019-01-21 21:48:27 +01:00
+ 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) {
2018-08-26 04:37:58 +02:00
+ 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;
+ }
2019-01-21 21:48:27 +01:00
+ } else if (chunkEdgeMode == ChunkEdgeMode.LOAD) {
2019-01-21 08:08:58 +01:00
+ 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;
+ }
2018-08-26 04:37:58 +02:00
+ }
+
+ //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<IBlockData> 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++) {
2018-09-01 06:30:04 +02:00
+ predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).getOrCreateIdFor(chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex)[i]);
2018-08-26 04:37:58 +02:00
+ }
+ }
+
2018-09-01 06:30:04 +02:00
+ dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex));
2018-08-26 04:37:58 +02:00
+
+ //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));
2018-09-01 06:30:04 +02:00
+ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex));
2018-08-26 04:37:58 +02:00
+ 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;
2018-09-01 06:30:04 +02:00
+ next[z][x] = skipFirstLayer || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(belowChunkSection.getType(x, 15, z))];
2018-08-26 04:37:58 +02:00
+ }
+ }
+
+ //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++) {
2018-09-01 06:30:04 +02:00
+ if (!solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(aboveChunkSection.getType(x, 0, z))]) {
2018-08-26 04:37:58 +02:00
+ 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));
2018-09-01 06:30:04 +02:00
+ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex + 1));
2018-08-26 04:37:58 +02:00
+ 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 {
2018-09-01 06:30:04 +02:00
+ 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]) {
2018-08-26 04:37:58 +02:00
+ 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 {
2018-09-01 06:30:04 +02:00
+ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(x, y, 15))] || current[0][x]) {
2018-08-26 04:37:58 +02:00
+ 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 {
2018-09-01 06:30:04 +02:00
+ 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]) {
2018-08-26 04:37:58 +02:00
+ 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 {
2018-09-01 06:30:04 +02:00
+ if (nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, z))] || current[z][0]) {
2018-08-26 04:37:58 +02:00
+ 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 {
2018-09-01 06:30:04 +02:00
+ if (nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, z))] || current[z][15]) {
2018-08-26 04:37:58 +02:00
+ 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 {
2018-09-01 06:30:04 +02:00
+ 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]) {
2018-08-26 04:37:58 +02:00
+ 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 {
2018-09-01 06:30:04 +02:00
+ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(x, y, 0))] || current[15][x]) {
2018-08-26 04:37:58 +02:00
+ 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 {
2018-09-01 06:30:04 +02:00
+ 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]) {
2018-08-26 04:37:58 +02:00
+ 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<IBlockData> dataPalette, boolean[] temp, boolean[] global) {
+ if (dataPalette == ChunkSection.GLOBAL_PALETTE) {
+ return global;
+ }
+
+ IBlockData blockData;
+
+ for (int i = 0; (blockData = dataPalette.getObject(i)) != null; i++) {
2018-09-01 06:30:04 +02:00
+ temp[i] = global[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)];
2018-08-26 04:37:58 +02:00
+ }
+
+ return temp;
+ }
+
+ @Override
+ public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) {
2018-09-01 06:30:04 +02:00
+ if (oldBlockData != null && solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(oldBlockData)] && !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(newBlockData)] && blockPosition.getY() <= maxBlockYUpdatePosition) {
2018-08-26 04:37:58 +02:00
+ 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) {
2018-08-26 04:48:24 +02:00
+ IBlockData blockData = world.getTypeIfLoaded(blockPosition);
2018-08-26 04:37:58 +02:00
+
2018-09-01 06:30:04 +02:00
+ if (blockData != null && obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)]) {
2018-08-26 04:48:24 +02:00
+ world.notify(blockPosition, blockData, blockData, 3);
2018-08-26 04:37:58 +02:00
+ }
+ }
+
+ 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
2019-01-26 01:11:42 +01:00
index 000000000..a68bace35
2018-08-26 04:37:58 +02:00
--- /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<T> {
+
+ 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<T> getDataPalette(int chunkSectionIndex) {
+ return (DataPalette<T>) dataPalettes[chunkSectionIndex];
+ }
+
+ public void setDataPalette(int chunkSectionIndex, DataPalette<T> dataPalette) {
+ dataPalettes[chunkSectionIndex] = dataPalette;
+ }
+
2018-09-01 06:30:04 +02:00
+ public int getOrCreateIdForIndex(int chunkSectionIndex) {
2018-08-26 04:37:58 +02:00
+ 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
2019-01-26 01:11:42 +01:00
index 000000000..e255a45fa
2018-08-26 04:37:58 +02:00
--- /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<IBlockData> 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
2019-01-26 01:11:42 +01:00
index 000000000..cc586827a
2018-08-26 04:37:58 +02:00
--- /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
2019-01-26 01:11:42 +01:00
index 000000000..37093419c
2018-08-26 04:37:58 +02:00
--- /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
2019-03-20 02:46:00 +01:00
index a915d0184..9f28d7775 100644
2018-08-26 04:37:58 +02:00
--- a/src/main/java/net/minecraft/server/Chunk.java
+++ b/src/main/java/net/minecraft/server/Chunk.java
2019-01-01 04:15:55 +01:00
@@ -541,7 +541,7 @@ public class Chunk implements IChunkAccess {
2018-08-26 04:37:58 +02:00
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;
}
2019-01-01 04:15:55 +01:00
@@ -641,7 +641,7 @@ public class Chunk implements IChunkAccess {
2018-08-26 04:37:58 +02:00
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
2019-01-26 01:11:42 +01:00
index 60143ff63..d938eb374 100644
2018-08-26 04:37:58 +02:00
--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
2019-01-04 20:19:36 +01:00
@@ -849,7 +849,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
2018-08-26 04:37:58 +02:00
}
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"));
2019-01-04 20:19:36 +01:00
@@ -955,7 +955,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
2018-08-26 04:37:58 +02:00
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
2019-01-26 01:11:42 +01:00
index 621ed1fc5..2af07ae59 100644
2018-08-26 04:37:58 +02:00
--- a/src/main/java/net/minecraft/server/ChunkSection.java
+++ b/src/main/java/net/minecraft/server/ChunkSection.java
2019-01-01 04:15:55 +01:00
@@ -11,9 +11,15 @@ public class ChunkSection {
2018-08-26 04:37:58 +02:00
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;
2019-01-01 04:15:55 +01:00
- 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
2018-08-26 04:37:58 +02:00
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
2019-01-26 01:11:42 +01:00
index 56958a5ce..d3898599f 100644
2018-08-26 04:37:58 +02:00
--- a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java
+++ b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java
2019-01-01 04:15:55 +01:00
@@ -39,7 +39,7 @@ public class ChunkTaskScheduler extends Scheduler<ChunkCoordIntPair, ChunkStatus
2018-08-26 04:37:58 +02:00
// CraftBukkit start
public void forcePolluteCache(ChunkCoordIntPair chunkcoordintpair) {
2018-08-26 20:11:49 +02:00
- this.progressCache.put(chunkcoordintpair.a(), new Scheduler.a(chunkcoordintpair, new ProtoChunk(chunkcoordintpair, ChunkConverter.a), ChunkStatus.EMPTY));
+ this.progressCache.put(chunkcoordintpair.a(), new Scheduler.a(chunkcoordintpair, new ProtoChunk(chunkcoordintpair, ChunkConverter.a, this.getWorld()), ChunkStatus.EMPTY)); // Paper - Anti-Xray
2018-08-26 04:37:58 +02:00
}
// CraftBukkit end
2019-01-01 04:15:55 +01:00
@@ -65,7 +65,7 @@ public class ChunkTaskScheduler extends Scheduler<ChunkCoordIntPair, ChunkStatus
2018-08-26 20:11:49 +02:00
protochunk.setLastSaved(this.c.getTime());
return new Scheduler.a(chunkcoordintpair, protochunk, protochunk.i());
} else {
- return new Scheduler.a(chunkcoordintpair, new ProtoChunk(chunkcoordintpair, ChunkConverter.a), ChunkStatus.EMPTY);
+ return new Scheduler.a(chunkcoordintpair, new ProtoChunk(chunkcoordintpair, ChunkConverter.a, this.getWorld()), ChunkStatus.EMPTY); // Paper - Anti-Xray
}
}) : (Scheduler.a) this.progressCache.get(chunkcoordintpair.a());
}
2018-08-26 04:37:58 +02:00
diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java
2019-01-26 01:11:42 +01:00
index 6fcfc5ef7..454903a0e 100644
2018-08-26 04:37:58 +02:00
--- 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;
2019-01-01 04:15:55 +01:00
@@ -16,6 +17,7 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
2018-08-26 04:37:58 +02:00
private final Function<NBTTagCompound, T> e;
private final Function<T, NBTTagCompound> 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<T> h; private DataPalette<T> getDataPalette() { return this.h; } // Paper - OBFHELPER
private int i; private int getBitsPerObject() { return this.i; } // Paper - OBFHELPER
2019-01-01 04:15:55 +01:00
@@ -41,12 +43,43 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
2018-08-26 04:37:58 +02:00
}
2019-01-01 04:15:55 +01:00
public DataPaletteBlock(DataPalette<T> datapalette, RegistryBlockID<T> registryblockid, Function<NBTTagCompound, T> function, Function<T, NBTTagCompound> function1, T t0) {
2018-09-01 00:56:57 +02:00
+ // Paper start - Anti-Xray - Support default constructor
2019-01-01 04:15:55 +01:00
+ this(datapalette, registryblockid, function, function1, t0, null, true);
2018-08-26 04:37:58 +02:00
+ }
+
2019-01-01 04:15:55 +01:00
+ public DataPaletteBlock(DataPalette<T> datapalette, RegistryBlockID<T> registryblockid, Function<NBTTagCompound, T> function, Function<T, NBTTagCompound> function1, T t0, T[] predefinedObjects, boolean initialize) {
2018-09-01 00:56:57 +02:00
+ // Paper end - Anti-Xray - Add predefined objects
2018-08-26 04:37:58 +02:00
this.b = datapalette;
this.d = registryblockid;
this.e = function;
this.f = function1;
2019-01-01 04:15:55 +01:00
this.g = t0;
2018-08-26 04:37:58 +02:00
- 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
}
2019-01-01 04:15:55 +01:00
private static int b(int i, int j, int k) {
@@ -72,6 +105,16 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
2018-08-26 04:37:58 +02:00
}
}
+ // 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++) {
2018-09-01 06:30:04 +02:00
+ this.getDataPalette().getOrCreateIdFor(this.predefinedObjects[i]);
2018-08-26 04:37:58 +02:00
+ }
+ }
+ }
+ // Paper end
2019-01-01 04:15:55 +01:00
+
public int onResize(int i, T t0) {
2018-08-26 04:37:58 +02:00
this.b();
DataBits databits = this.a;
2019-01-01 04:15:55 +01:00
@@ -81,6 +124,7 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
int j;
2018-08-26 04:37:58 +02:00
+ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects
2019-01-01 04:15:55 +01:00
for (j = 0; j < databits.b(); ++j) {
T t1 = datapalette.a(databits.a(j));
@@ -116,11 +160,28 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
return t0 == null ? this.g : t0;
2018-08-26 04:37:58 +02:00
}
- 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<T> chunkPacketInfo, int chunkSectionIndex) { this.b(packetDataSerializer, chunkPacketInfo, chunkSectionIndex); } // Paper - OBFHELPER // Paper - Anti-Xray - Add chunk packet info
+ public void b(PacketDataSerializer packetdataserializer, ChunkPacketInfo<T> 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();
}
2019-01-01 04:15:55 +01:00
@@ -128,13 +189,15 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
2018-08-26 04:37:58 +02:00
public void a(NBTTagCompound nbttagcompound, String s, String s1) {
this.b();
NBTTagList nbttaglist = nbttagcompound.getList(s, 10);
2019-01-01 04:15:55 +01:00
- int i = Math.max(4, MathHelper.d(nbttaglist.size()));
2018-08-26 04:37:58 +02:00
+ // 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)?
2019-01-01 04:15:55 +01:00
+ 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);
2018-08-26 04:37:58 +02:00
}
this.h.a(nbttaglist);
+ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects
long[] along = nbttagcompound.o(s1);
2019-01-01 04:15:55 +01:00
int j = along.length * 64 / 4096;
2018-08-26 04:37:58 +02:00
diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java
2019-01-26 01:11:42 +01:00
index 4a50aab51..4c1110479 100644
2018-08-26 04:37:58 +02:00
--- a/src/main/java/net/minecraft/server/NetworkManager.java
+++ b/src/main/java/net/minecraft/server/NetworkManager.java
2019-01-01 04:15:55 +01:00
@@ -157,8 +157,8 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
2018-08-26 04:37:58 +02:00
}
public void sendPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> 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();
2019-01-01 04:15:55 +01:00
@@ -213,23 +213,38 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
2018-08-26 04:37:58 +02:00
}
- 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();
2018-12-17 06:18:06 +01:00
+ 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
2018-08-26 04:37:58 +02:00
+ return true;
+ }
+
+ this.j.writeLock().lock(); // readLock -> writeLock (because of race condition between peek and poll)
try {
2018-12-17 06:18:06 +01:00
while (!this.packetQueue.isEmpty()) {
- NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.packetQueue.poll();
2018-08-26 04:37:58 +02:00
-
- 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
2019-01-26 01:11:42 +01:00
index 18ef7232e..8e35d14f9 100644
2018-08-26 04:37:58 +02:00
--- 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;
2019-01-01 04:15:55 +01:00
@@ -16,17 +17,28 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
2018-08-26 04:37:58 +02:00
private byte[] d; private byte[] getData() { return this.d; } // Paper - OBFHELPER
private List<NBTTagCompound> e;
private boolean f;
+ private volatile boolean ready = false; // Paper - Async-Anti-Xray - Ready flag for the network manager
2019-01-01 04:15:55 +01:00
- public PacketPlayOutMapChunk() {}
+ public PacketPlayOutMapChunk() {
2018-09-01 00:56:57 +02:00
+ this.ready = true; // Paper - Async-Anti-Xray - Set the ready flag to true
2019-01-01 04:15:55 +01:00
+ }
2018-08-26 04:37:58 +02:00
public PacketPlayOutMapChunk(Chunk chunk, int i) {
+ ChunkPacketInfo<IBlockData> chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info
this.a = chunk.locX;
this.b = chunk.locZ;
2018-09-01 00:56:57 +02:00
this.f = i == 65535;
2018-08-26 04:37:58 +02:00
boolean flag = chunk.getWorld().worldProvider.g();
2019-01-01 04:15:55 +01:00
2018-08-26 04:37:58 +02:00
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();
2019-01-01 04:15:55 +01:00
Iterator iterator = chunk.getTileEntities().entrySet().iterator();
2018-08-26 04:37:58 +02:00
2019-01-01 04:15:55 +01:00
@@ -44,7 +56,18 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
2018-08-26 04:37:58 +02:00
}
}
+ 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;
2019-01-01 04:15:55 +01:00
+ }
+
2018-08-26 04:37:58 +02:00
+ public void setReady(boolean ready) {
+ this.ready = ready;
2019-01-01 04:15:55 +01:00
}
2018-08-26 04:37:58 +02:00
+ // Paper end
2019-01-01 04:15:55 +01:00
2018-08-26 04:37:58 +02:00
public void a(PacketDataSerializer packetdataserializer) throws IOException {
this.a = packetdataserializer.readInt();
2019-01-01 04:15:55 +01:00
@@ -98,8 +121,15 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
2018-08-26 04:37:58 +02:00
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<IBlockData> 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<IBlockData> chunkPacketInfo) { // Paper - Anti-Xray - Add chunk packet info
int j = 0;
ChunkSection[] achunksection = chunk.getSections();
int k = 0;
2019-01-01 04:15:55 +01:00
@@ -111,7 +141,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
2018-08-26 04:37:58 +02:00
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
2019-01-26 01:11:42 +01:00
index 7d3f846a1..240f59066 100644
2018-08-26 04:37:58 +02:00
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
2019-01-01 04:15:55 +01:00
@@ -103,6 +103,8 @@ public class PlayerChunk {
2018-08-26 04:37:58 +02:00
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;
2019-01-01 04:15:55 +01:00
@@ -125,6 +127,7 @@ public class PlayerChunk {
2018-08-26 04:37:58 +02:00
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
2018-12-17 06:18:06 +01:00
entityplayer.playerConnection.sendPacket(new PacketPlayOutMapChunk(this.chunk, 65535));
2018-08-26 04:37:58 +02:00
this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk);
}
2019-01-01 04:15:55 +01:00
@@ -189,6 +192,9 @@ public class PlayerChunk {
2018-08-26 04:37:58 +02:00
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
2019-01-01 04:15:55 +01:00
this.a((Packet) (new PacketPlayOutMapChunk(this.chunk, this.h)));
2018-08-26 04:37:58 +02:00
} else {
2019-01-01 04:15:55 +01:00
this.a((Packet) (new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, this.chunk)));
2018-08-26 04:37:58 +02:00
diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java
2019-02-03 16:34:04 +01:00
index 07f9b8d2f..22378b6bc 100644
2018-08-26 04:37:58 +02:00
--- a/src/main/java/net/minecraft/server/PlayerInteractManager.java
+++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java
2018-10-31 10:01:59 +01:00
@@ -207,6 +207,8 @@ public class PlayerInteractManager {
2018-08-26 04:37:58 +02:00
}
}
+
+ 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
2019-01-26 01:11:42 +01:00
index 16e3469d0..e4c0cc6a3 100644
2018-08-26 04:37:58 +02:00
--- a/src/main/java/net/minecraft/server/ProtoChunk.java
+++ b/src/main/java/net/minecraft/server/ProtoChunk.java
2019-01-01 04:15:55 +01:00
@@ -41,12 +41,24 @@ public class ProtoChunk implements IChunkAccess {
2018-10-28 23:08:08 +01:00
private long s;
private final Map<WorldGenStage.Features, BitSet> t;
2018-08-26 04:37:58 +02:00
private boolean u;
2018-10-28 23:08:08 +01:00
+ private GeneratorAccess world; // Paper - Anti-Xray
2018-08-26 04:37:58 +02:00
+ // Paper start - Anti-Xray - Support default constructors
2019-01-01 04:15:55 +01:00
public ProtoChunk(int i, int j, ChunkConverter chunkconverter) {
- this(new ChunkCoordIntPair(i, j), chunkconverter);
+ this(new ChunkCoordIntPair(i, j), chunkconverter, null);
2018-08-26 04:37:58 +02:00
}
public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter) {
+ this(chunkcoordintpair, chunkconverter, null);
+ }
+
2018-10-28 23:08:08 +01:00
+ public ProtoChunk(int i, int j, ChunkConverter chunkconverter, GeneratorAccess world) {
+ this(new ChunkCoordIntPair(i, j), chunkconverter, world);
2018-08-26 04:37:58 +02:00
+ }
+
2018-10-28 23:08:08 +01:00
+ public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, GeneratorAccess world) {
+ this.world = world;
+ // Paper end
2018-08-26 04:37:58 +02:00
this.d = new AtomicInteger();
this.f = Maps.newEnumMap(HeightMap.Type.class);
this.g = ChunkStatus.EMPTY;
2019-01-01 04:15:55 +01:00
@@ -135,7 +147,7 @@ public class ProtoChunk implements IChunkAccess {
2018-08-26 04:37:58 +02:00
return iblockdata;
}
2019-01-01 04:15:55 +01:00
- 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
2018-08-26 04:37:58 +02:00
}
2019-01-01 04:15:55 +01:00
IBlockData iblockdata1 = this.j[j >> 4].getType(i & 15, j & 15, k & 15);
@@ -389,7 +401,7 @@ public class ProtoChunk implements IChunkAccess {
2018-08-26 04:37:58 +02:00
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
2019-03-20 02:46:00 +01:00
index c25c82bf1..1bd2167aa 100644
2018-08-26 04:37:58 +02:00
--- 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;
2019-01-01 04:15:55 +01:00
@@ -139,6 +141,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
2018-08-26 04:37:58 +02:00
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
2019-02-03 16:34:04 +01:00
@@ -165,6 +168,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
2018-08-26 20:11:49 +02:00
protected World(IDataManager idatamanager, @Nullable PersistentCollection persistentcollection, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag, ChunkGenerator gen, org.bukkit.World.Environment env) {
2018-08-26 04:37:58 +02:00
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
2019-02-03 16:34:04 +01:00
@@ -407,6 +411,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
2018-08-26 04:37:58 +02:00
// CraftBukkit end
2018-10-23 01:16:21 +02:00
IBlockData iblockdata1 = chunk.setType(blockposition, iblockdata, (i & 64) != 0, (i & 1024) == 0); // CraftBukkit custom NO_PLACE flag
2018-08-26 04:37:58 +02:00
+ 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
2019-04-05 11:17:10 +02:00
index 791017258..29695da2e 100644
2018-08-26 04:37:58 +02:00
--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java
+++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java
2019-04-05 11:17:10 +02:00
@@ -21,9 +21,11 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData {
2018-08-26 04:37:58 +02:00
private final int maxHeight;
private final ChunkSection[] sections;
2019-04-05 11:17:10 +02:00
private Set<BlockPosition> tiles;
2018-08-26 04:37:58 +02:00
+ 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) {
2019-04-05 11:17:10 +02:00
@@ -157,7 +159,7 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData {
2018-08-26 04:37:58 +02:00
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;
}
--
2019-03-20 02:46:00 +01:00
2.21.0
2018-08-26 04:37:58 +02:00