mirror of
https://github.com/PaperMC/Paper.git
synced 2024-08-21 20:02:55 +02:00
Undo the accidental renaming of a method in0aad8bf
Aikar wanted to rename DataPalette#getDataBits(T object) to getOrCreateIdFor in0aad8bf
but he also accidentally renamed ChunkPacketInfo#getDataBitsIndex(int chunkSectionIndex) to getOrCreateIdForIndex. Remove chunk-edge-mode and chunk loading entirely from Anti-Xray The chunk-edge-mode is broken since several versions. Loading chunk neighbors for chunk edge obfuscation isn't needed anymore. Unlike in previous versions, these are under normal circumstances already loaded at the time we need them (plugins for example can bypass this). Use the modified methods and constructors everywhere Anti-Xray provides support for the default nms methods and constructors, which where modified by Anti-Xray to avoid breaking stuff (plugins) which somehow uses these methods. However, the modified versions of those methods and constructors should be used where possible.
1536 lines
76 KiB
Diff
1536 lines
76 KiB
Diff
From 5428b63f7eed04603e23443185f1593dff63141f Mon Sep 17 00:00:00 2001
|
|
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
|
|
index 48676152152..ca2ac17747d 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
@@ -1,7 +1,9 @@
|
|
package com.destroystokyo.paper;
|
|
|
|
+import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.configuration.file.YamlConfiguration;
|
|
import org.spigotmc.SpigotWorldConfig;
|
|
@@ -502,4 +504,33 @@ 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 int maxChunkSectionIndex;
|
|
+ public int updateRadius;
|
|
+ public List<String> hiddenBlocks;
|
|
+ public List<String> 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;
|
|
+ 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"));
|
|
+ if (PaperConfig.version < 19) {
|
|
+ hiddenBlocks.remove("lit_redstone_ore");
|
|
+ int index = replacementBlocks.indexOf("planks");
|
|
+ if (index != -1) {
|
|
+ replacementBlocks.set(index, "oak_planks");
|
|
+ }
|
|
+ set("anti-xray.hidden-blocks", hiddenBlocks);
|
|
+ set("anti-xray.replacement-blocks", replacementBlocks);
|
|
+ }
|
|
+ log("Anti-Xray: " + (antiXray ? "enabled" : "disabled") + " / Engine Mode: " + engineMode.getDescription() + " / Up to " + ((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 00000000000..df7e4183d88
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java
|
|
@@ -0,0 +1,40 @@
|
|
+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.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(World world, IChunkAccess chunk, ChunkSection chunkSection, boolean initializeBlocks) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ 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
|
|
index 00000000000..9c017eb027c
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
|
@@ -0,0 +1,628 @@
|
|
+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.Bukkit;
|
|
+import org.bukkit.World.Environment;
|
|
+
|
|
+import com.destroystokyo.paper.PaperWorldConfig;
|
|
+
|
|
+public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController {
|
|
+
|
|
+ private static ExecutorService executorServiceInstance = null;
|
|
+ private final ExecutorService executorService;
|
|
+ private final boolean asynchronous;
|
|
+ private final EngineMode engineMode;
|
|
+ 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;
|
|
+ maxChunkSectionIndex = paperWorldConfig.maxChunkSectionIndex;
|
|
+ updateRadius = paperWorldConfig.updateRadius;
|
|
+
|
|
+ if (asynchronous) {
|
|
+ executorService = getExecutorServiceInstance();
|
|
+ } else {
|
|
+ executorService = null;
|
|
+ }
|
|
+
|
|
+ List<String> 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<IBlockData> predefinedBlockDataSet = new HashSet<IBlockData>();
|
|
+
|
|
+ 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(World 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:
|
|
+ switch (world.getWorld().getEnvironment()) {
|
|
+ case NETHER:
|
|
+ return predefinedBlockDataNetherrack;
|
|
+ case THE_END:
|
|
+ return predefinedBlockDataEndStone;
|
|
+ default:
|
|
+ return predefinedBlockDataStone;
|
|
+ }
|
|
+ default:
|
|
+ return predefinedBlockData;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ @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
|
|
+ // Note: As of 1.14 this has to be moved later due to the chunk system.
|
|
+ ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this);
|
|
+ return chunkPacketInfoAntiXray;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo<IBlockData> chunkPacketInfo) {
|
|
+ if (!Bukkit.isPrimaryThread()) {
|
|
+ // plugins?
|
|
+ MinecraftServer.getServer().scheduleOnMain(() -> {
|
|
+ this.modifyBlocks(packetPlayOutMapChunk, chunkPacketInfo);
|
|
+ });
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Chunk chunk = chunkPacketInfo.getChunk();
|
|
+ int x = chunk.getPos().x;
|
|
+ int z = chunk.getPos().z;
|
|
+ WorldServer world = (WorldServer)chunk.world;
|
|
+ ((ChunkPacketInfoAntiXray) chunkPacketInfo).setNearbyChunks(
|
|
+ (Chunk) world.getChunkIfLoadedImmediately(x - 1, z),
|
|
+ (Chunk) world.getChunkIfLoadedImmediately(x + 1, z),
|
|
+ (Chunk) world.getChunkIfLoadedImmediately(x, z - 1),
|
|
+ (Chunk) world.getChunkIfLoadedImmediately(x, z + 1));
|
|
+
|
|
+ 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.getDataBitsIndex(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.getDataBitsIndex(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.getDataBitsIndex(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<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++) {
|
|
+ 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;
|
|
+ }
|
|
+ }
|
|
+}
|
|
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 00000000000..41618994b46
|
|
--- /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;
|
|
+ }
|
|
+
|
|
+ public int getDataBitsIndex(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 00000000000..e61421d87a1
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java
|
|
@@ -0,0 +1,30 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+import net.minecraft.server.Chunk;
|
|
+import net.minecraft.server.IBlockData;
|
|
+import net.minecraft.server.PacketPlayOutMapChunk;
|
|
+
|
|
+public final 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
|
|
index 00000000000..c5a7b186e96
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java
|
|
@@ -0,0 +1,56 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+public final class DataBitsReader {
|
|
+
|
|
+ private byte[] dataBits;
|
|
+ private int bitsPerObject;
|
|
+ private int mask;
|
|
+ private int longInDataBitsIndex;
|
|
+ private int bitInLongIndex;
|
|
+ private long current;
|
|
+
|
|
+ public void setDataBits(byte[] dataBits) {
|
|
+ this.dataBits = dataBits;
|
|
+ }
|
|
+
|
|
+ public void setBitsPerObject(int bitsPerObject) {
|
|
+ this.bitsPerObject = bitsPerObject;
|
|
+ mask = (1 << bitsPerObject) - 1;
|
|
+ }
|
|
+
|
|
+ public void setIndex(int index) {
|
|
+ this.longInDataBitsIndex = index;
|
|
+ bitInLongIndex = 0;
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ private void init() {
|
|
+ if (dataBits.length > longInDataBitsIndex + 7) {
|
|
+ current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public int read() {
|
|
+ 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 00000000000..2eff19f6aaa
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java
|
|
@@ -0,0 +1,84 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+public final class DataBitsWriter {
|
|
+
|
|
+ private byte[] dataBits;
|
|
+ private int bitsPerObject;
|
|
+ private long mask;
|
|
+ private int longInDataBitsIndex;
|
|
+ private int bitInLongIndex;
|
|
+ private long current;
|
|
+ private boolean dirty;
|
|
+
|
|
+ public void setDataBits(byte[] dataBits) {
|
|
+ this.dataBits = dataBits;
|
|
+ }
|
|
+
|
|
+ public void setBitsPerObject(int bitsPerObject) {
|
|
+ this.bitsPerObject = bitsPerObject;
|
|
+ mask = (1 << bitsPerObject) - 1;
|
|
+ }
|
|
+
|
|
+ public void setIndex(int index) {
|
|
+ this.longInDataBitsIndex = index;
|
|
+ bitInLongIndex = 0;
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ private void init() {
|
|
+ if (dataBits.length > longInDataBitsIndex + 7) {
|
|
+ current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
|
+ }
|
|
+
|
|
+ dirty = false;
|
|
+ }
|
|
+
|
|
+ public void finish() {
|
|
+ if (dirty && dataBits.length > longInDataBitsIndex + 7) {
|
|
+ dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void write(int value) {
|
|
+ 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 af0d6aff4de..2604fe9756e 100644
|
|
--- a/src/main/java/net/minecraft/server/Chunk.java
|
|
+++ b/src/main/java/net/minecraft/server/Chunk.java
|
|
@@ -416,7 +416,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 - Add parameters
|
|
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 8e4b3e52cbc..d287ea55c55 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
|
@@ -57,7 +57,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 - Add parameters
|
|
|
|
chunksection.getBlocks().a(nbttagcompound2.getList("Palette", 10), nbttagcompound2.getLongArray("BlockStates"));
|
|
chunksection.recalcBlockCounts();
|
|
@@ -115,7 +115,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 - Add parameter
|
|
|
|
protochunk.a(biomestorage);
|
|
object = protochunk;
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java
|
|
index e056fbcb216..eeb7eee925d 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkSection.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkSection.java
|
|
@@ -1,5 +1,6 @@
|
|
package net.minecraft.server;
|
|
|
|
+import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray - Add chunk packet info
|
|
import javax.annotation.Nullable;
|
|
|
|
public class ChunkSection {
|
|
@@ -11,16 +12,22 @@ public class ChunkSection {
|
|
private short e;
|
|
final DataPaletteBlock<IBlockData> blockIds;
|
|
|
|
- public ChunkSection(int i) {
|
|
- this(i, (short) 0, (short) 0, (short) 0);
|
|
+ // Paper start - Anti-Xray - Add parameters
|
|
+ @Deprecated public ChunkSection(int i) { this(i, null, null, true); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public ChunkSection(int i, IChunkAccess chunk, World 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 - Anti-Xray - Add parameters
|
|
+ @Deprecated public ChunkSection(int i, short short0, short short1, short short2) { this(i, short0, short1, short2, null, null, true); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public ChunkSection(int i, short short0, short short1, short short2, IChunkAccess chunk, World 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 == null ? null : world.chunkPacketBlockController.getPredefinedBlockData(world, chunk, this, initializeBlocks), initializeBlocks); // Paper - Anti-Xray - Add predefined block data
|
|
}
|
|
|
|
public IBlockData getType(int i, int j, int k) {
|
|
@@ -132,10 +139,14 @@ public class ChunkSection {
|
|
return this.blockIds;
|
|
}
|
|
|
|
- public void writeChunkSection(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } // Paper - OBFHELPER
|
|
- public void b(PacketDataSerializer packetdataserializer) {
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ @Deprecated public void writeChunkSection(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } // OBFHELPER // Notice for updates: Please make sure this method isn't used anywhere
|
|
+ @Deprecated public void b(PacketDataSerializer packetdataserializer) { this.writeChunkSection(packetdataserializer, null); } // Notice for updates: Please make sure this method isn't used anywhere
|
|
+ public void writeChunkSection(PacketDataSerializer packetDataSerializer, ChunkPacketInfo<IBlockData> chunkPacketInfo) { this.b(packetDataSerializer, chunkPacketInfo); } // OBFHELPER
|
|
+ public void b(PacketDataSerializer packetdataserializer, ChunkPacketInfo<IBlockData> chunkPacketInfo) {
|
|
+ // Paper end
|
|
packetdataserializer.writeShort(this.nonEmptyBlockCount);
|
|
- this.blockIds.b(packetdataserializer);
|
|
+ this.blockIds.writeDataPaletteBlock(packetdataserializer, chunkPacketInfo, this.yPos >> 4); // Paper - Anti-Xray - Add chunk packet info
|
|
}
|
|
|
|
public int j() {
|
|
diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java
|
|
index 2c1d1b1a556..2c7872bd051 100644
|
|
--- a/src/main/java/net/minecraft/server/DataPaletteBlock.java
|
|
+++ b/src/main/java/net/minecraft/server/DataPaletteBlock.java
|
|
@@ -3,6 +3,7 @@ package net.minecraft.server;
|
|
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
|
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
|
import it.unimi.dsi.fastutil.ints.Int2IntMap.Entry;
|
|
+import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray - Add chunk packet info
|
|
import java.util.Arrays;
|
|
import java.util.Objects;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
@@ -19,6 +20,7 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
|
|
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
|
|
@@ -46,14 +48,47 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
|
|
this.j.unlock();
|
|
}
|
|
|
|
- public DataPaletteBlock(DataPalette<T> datapalette, RegistryBlockID<T> registryblockid, Function<NBTTagCompound, T> function, Function<T, NBTTagCompound> function1, T t0) {
|
|
+ // Paper start - Anti-Xray - Add predefined objects
|
|
+ @Deprecated public DataPaletteBlock(DataPalette<T> datapalette, RegistryBlockID<T> registryblockid, Function<NBTTagCompound, T> function, Function<T, NBTTagCompound> function1, T t0) { this(datapalette, registryblockid, function, function1, t0, null, true); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public DataPaletteBlock(DataPalette<T> datapalette, RegistryBlockID<T> registryblockid, Function<NBTTagCompound, T> function, Function<T, NBTTagCompound> function1, T t0, T[] predefinedObjects, boolean initialize) {
|
|
+ // Paper end
|
|
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;
|
|
@@ -88,6 +123,7 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
|
|
|
|
int j;
|
|
|
|
+ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects
|
|
for (j = 0; j < databits.b(); ++j) {
|
|
T t1 = datapalette.a(databits.a(j));
|
|
|
|
@@ -137,24 +173,38 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
|
|
return t0 == null ? this.g : t0;
|
|
}
|
|
|
|
- public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } // Paper - OBFHELPER
|
|
- public void b(PacketDataSerializer packetdataserializer) {
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ @Deprecated public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } // OBFHELPER // Notice for updates: Please make sure this method isn't used anywhere
|
|
+ @Deprecated public void b(PacketDataSerializer packetdataserializer) { this.writeDataPaletteBlock(packetdataserializer, null, 0); } // Notice for updates: Please make sure this method isn't used anywhere
|
|
+ public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer, ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) { this.b(packetDataSerializer, chunkPacketInfo, chunkSectionIndex); } // OBFHELPER
|
|
+ public void b(PacketDataSerializer packetdataserializer, ChunkPacketInfo<T> 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 02a9f3d5fa4..55441e10023 100644
|
|
--- a/src/main/java/net/minecraft/server/NetworkManager.java
|
|
+++ b/src/main/java/net/minecraft/server/NetworkManager.java
|
|
@@ -188,6 +188,11 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
|
|
|
public void sendPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
|
|
// Paper start - handle oversized packets better
|
|
+ // Special case keepalive, allow it to go out of queue order
|
|
+ if (packet instanceof PacketPlayOutKeepAlive && this.isConnected()) {
|
|
+ this.dispatchPacket(packet, genericfuturelistener);
|
|
+ return;
|
|
+ }
|
|
// write the packets to the queue, then flush - antixray hooks there already
|
|
java.util.List<Packet> extraPackets = InnerUtil.buildExtraPackets(packet);
|
|
boolean hasExtraPackets = extraPackets != null && !extraPackets.isEmpty();
|
|
@@ -250,21 +255,32 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
|
|
|
}
|
|
|
|
- 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()) {
|
|
Queue queue = this.packetQueue;
|
|
|
|
synchronized (this.packetQueue) {
|
|
- NetworkManager.QueuedPacket networkmanager_queuedpacket;
|
|
-
|
|
- while ((networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.packetQueue.poll()) != null) {
|
|
- this.b(networkmanager_queuedpacket.a, networkmanager_queuedpacket.b);
|
|
+ while (!this.packetQueue.isEmpty()) {
|
|
+ NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) queue.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 {
|
|
+ queue.poll(); // poll here
|
|
+ this.dispatchPacket(networkmanager_queuedpacket.getPacket(), networkmanager_queuedpacket.getGenericFutureListener()); // dispatch the packet
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
}
|
|
}
|
|
+
|
|
+ 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 23223f3f452..0d485064cac 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 - Add chunk packet info
|
|
import com.google.common.collect.Lists;
|
|
import io.netty.buffer.ByteBuf;
|
|
import io.netty.buffer.Unpooled;
|
|
@@ -20,8 +21,13 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|
private byte[] f; private byte[] getData() { return this.f; } // Paper - OBFHELPER
|
|
private List<NBTTagCompound> g;
|
|
private boolean h;
|
|
+ private volatile boolean ready; // Paper - Async-Anti-Xray - Ready flag for the network manager
|
|
|
|
- public PacketPlayOutMapChunk() {}
|
|
+ // Paper start - Async-Anti-Xray - Set the ready flag to true
|
|
+ public PacketPlayOutMapChunk() {
|
|
+ this.ready = true;
|
|
+ }
|
|
+ // Paper end
|
|
|
|
// Paper start
|
|
private final java.util.List<Packet> extraPackets = new java.util.ArrayList<>();
|
|
@@ -33,6 +39,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|
}
|
|
// Paper end
|
|
public PacketPlayOutMapChunk(Chunk chunk, int i) {
|
|
+ ChunkPacketInfo<IBlockData> chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info
|
|
ChunkCoordIntPair chunkcoordintpair = chunk.getPos();
|
|
|
|
this.a = chunkcoordintpair.x;
|
|
@@ -55,7 +62,12 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|
}
|
|
|
|
this.f = new byte[this.a(chunk, i)];
|
|
- this.c = this.a(new PacketDataSerializer(this.j()), chunk, i);
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ if (chunkPacketInfo != null) {
|
|
+ chunkPacketInfo.setData(this.getData());
|
|
+ }
|
|
+ this.c = this.writeChunk(new PacketDataSerializer(this.j()), chunk, i, chunkPacketInfo);
|
|
+ // Paper end
|
|
this.g = Lists.newArrayList();
|
|
iterator = chunk.getTileEntities().entrySet().iterator();
|
|
int totalTileEntities = 0; // Paper
|
|
@@ -82,9 +94,19 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|
this.g.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();
|
|
@@ -149,8 +171,12 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|
return bytebuf;
|
|
}
|
|
|
|
- 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 - Anti-Xray - Add chunk packet info
|
|
+ @Deprecated public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, chunkSectionSelector); } // OBFHELPER // Notice for updates: Please make sure this method isn't used anywhere
|
|
+ @Deprecated public int a(PacketDataSerializer packetdataserializer, Chunk chunk, int i) { return this.writeChunk(packetdataserializer, chunk, i, null); } // Notice for updates: Please make sure this method isn't used anywhere
|
|
+ public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, int chunkSectionSelector, ChunkPacketInfo<IBlockData> chunkPacketInfo) { return this.a(packetDataSerializer, chunk, chunkSectionSelector, chunkPacketInfo); } // OBFHELPER
|
|
+ public int a(PacketDataSerializer packetdataserializer, Chunk chunk, int i, ChunkPacketInfo<IBlockData> chunkPacketInfo) {
|
|
+ // Paper end
|
|
int j = 0;
|
|
ChunkSection[] achunksection = chunk.getSections();
|
|
int k = 0;
|
|
@@ -160,7 +186,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|
|
|
if (chunksection != Chunk.a && (!this.f() || !chunksection.c()) && (i & 1 << k) != 0) {
|
|
j |= 1 << k;
|
|
- chunksection.b(packetdataserializer);
|
|
+ chunksection.writeChunkSection(packetdataserializer, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
index 9171785ad54..94b0c54d9d4 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
@@ -604,7 +604,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 - Add parameter
|
|
}, this.executor);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java
|
|
index 17b7eddac4f..ce66090b8dc 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerInteractManager.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java
|
|
@@ -266,6 +266,8 @@ public class PlayerInteractManager {
|
|
}
|
|
|
|
}
|
|
+
|
|
+ this.world.chunkPacketBlockController.onPlayerLeftClickBlock(this, blockposition, enumdirection); // Paper - Anti-Xray
|
|
}
|
|
|
|
public void a(BlockPosition blockposition, PacketPlayInBlockDig.EnumPlayerDigType packetplayinblockdig_enumplayerdigtype, String s) {
|
|
diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java
|
|
index 39339fa2755..deb7fb09019 100644
|
|
--- a/src/main/java/net/minecraft/server/ProtoChunk.java
|
|
+++ b/src/main/java/net/minecraft/server/ProtoChunk.java
|
|
@@ -45,16 +45,24 @@ public class ProtoChunk implements IChunkAccess {
|
|
private long s;
|
|
private final Map<WorldGenStage.Features, BitSet> t;
|
|
private volatile boolean u;
|
|
+ private final World world; // Paper - Anti-Xray - Add world
|
|
|
|
- public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter) {
|
|
+ // Paper start - Anti-Xray - Add world
|
|
+ @Deprecated public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter) { this(chunkcoordintpair, chunkconverter, null); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, World 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 - Anti-Xray - Add world
|
|
}
|
|
|
|
- public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, @Nullable ChunkSection[] achunksection, ProtoChunkTickList<Block> protochunkticklist, ProtoChunkTickList<FluidType> protochunkticklist1) {
|
|
+ // Paper start - Anti-Xray - Add world
|
|
+ @Deprecated public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, @Nullable ChunkSection[] achunksection, ProtoChunkTickList<Block> protochunkticklist, ProtoChunkTickList<FluidType> protochunkticklist1) { this(chunkcoordintpair, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, null); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, @Nullable ChunkSection[] achunksection, ProtoChunkTickList<Block> protochunkticklist, ProtoChunkTickList<FluidType> protochunkticklist1, World world) {
|
|
+ this.world = world;
|
|
+ // Paper end
|
|
this.f = Maps.newEnumMap(HeightMap.Type.class);
|
|
this.g = ChunkStatus.EMPTY;
|
|
this.h = Maps.newHashMap();
|
|
@@ -207,7 +215,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 - Add parameters
|
|
}
|
|
|
|
return this.j[i];
|
|
diff --git a/src/main/java/net/minecraft/server/ProtoChunkExtension.java b/src/main/java/net/minecraft/server/ProtoChunkExtension.java
|
|
index 01bf28dc34d..caa18b046cd 100644
|
|
--- a/src/main/java/net/minecraft/server/ProtoChunkExtension.java
|
|
+++ b/src/main/java/net/minecraft/server/ProtoChunkExtension.java
|
|
@@ -11,7 +11,7 @@ public class ProtoChunkExtension extends ProtoChunk {
|
|
private final Chunk a;
|
|
|
|
public ProtoChunkExtension(Chunk chunk) {
|
|
- super(chunk.getPos(), ChunkConverter.a);
|
|
+ super(chunk.getPos(), ChunkConverter.a, chunk.world); // Paper - Anti-Xray - Add parameter
|
|
this.a = chunk;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
|
index 8cf3c10274b..0bde1717431 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;
|
|
@@ -78,6 +80,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
|
|
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
|
|
@@ -119,6 +122,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
|
|
protected World(WorldData worlddata, DimensionManager dimensionmanager, BiFunction<World, WorldProvider, IChunkProvider> 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
|
|
@@ -342,6 +346,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
|
|
// 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 (or the same)
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
|
index a53bb7295c5..47f1b970b9b 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
|
@@ -38,7 +38,7 @@ public class CraftChunk implements Chunk {
|
|
private final WorldServer worldServer;
|
|
private final int x;
|
|
private final int z;
|
|
- private static final DataPaletteBlock<IBlockData> emptyBlockIDs = new ChunkSection(0).getBlocks();
|
|
+ private static final DataPaletteBlock<IBlockData> emptyBlockIDs = new ChunkSection(0, null, null, true).getBlocks(); // Paper - Anti-Xray - Add parameters
|
|
private static final byte[] emptyLight = new byte[2048];
|
|
|
|
public CraftChunk(net.minecraft.server.Chunk chunk) {
|
|
@@ -260,7 +260,7 @@ public class CraftChunk implements Chunk {
|
|
NBTTagCompound data = new NBTTagCompound();
|
|
cs[i].getBlocks().a(data, "Palette", "BlockStates");
|
|
|
|
- DataPaletteBlock blockids = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, net.minecraft.server.Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData()); // TODO: snapshot whole ChunkSection
|
|
+ DataPaletteBlock blockids = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, net.minecraft.server.Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData(), null, false); // TODO: snapshot whole ChunkSection // Paper - Anti-Xray - Add no predefined block data and don't initialize because it's done in the line below internally
|
|
blockids.a(data.getList("Palette", CraftMagicNumbers.NBT.TAG_COMPOUND), data.getLongArray("BlockStates"));
|
|
|
|
sectionBlockIDs[i] = blockids;
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java
|
|
index 8191e7c3489..bb18740ebdf 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<BlockPosition> tiles;
|
|
+ private World world; // Paper - Anti-Xray - Add world
|
|
|
|
public CraftChunkData(World world) {
|
|
this(world.getMaxHeight());
|
|
+ this.world = world; // Paper - Anti-Xray - Add world
|
|
}
|
|
|
|
/* 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 >> 4 << 4);
|
|
+ sections[y >> 4] = section = new ChunkSection(y >> 4 << 4, null, world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) world).getHandle() : null, true); // Paper - Anti-Xray - Add parameters
|
|
}
|
|
return section;
|
|
}
|
|
--
|
|
2.26.2
|
|
|