5792c8626a
Upstream has released updates that appears to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: 93e39ce1 Clarify documentation regarding getMaterial with legacyName = true c3aeaea0 Improve dependency tracker 14c9d275 Add support for transitive depends in load access warning c8afe560 SPIGOT-5526: Add EntityEnterBlockEvent 6bb6f07d SPIGOT-5548: Show error that hints towards plugins misusing reflection ed75537d SPIGOT-5546: Fix bad depend access using wrong provider in message 4e4c0ee9 Fix buggy classloader warning triggering for all classes 89586a4c Print warning when loading classes from depends that have not been specified d4fe9680 Fix bug where disablePlugin could remove ConfigurationSerializable classes from other plugins 85e683b7 Add additional checkstyle checks 612fd8e1 Correct max page count in BookMeta docs fa8a9781 Correct max title length in BookMeta docs CraftBukkit Changes: ab13a117 SPIGOT-5550: Cancelled ProjectileLaunchEvent still plays sound for eggs 44016b1d SPIGOT-5538: Using javaw to run GUI prints input error e653ae76 SPIGOT-5526: Call EntityEnterBlockEvent for bees trying to enter hives 6515ea49 SPIGOT-5537: Bee nests generated by growing trees near flower have no bees d82b3149 Remove unused CraftWorld.getId method 10763a88 Change some block == AIR checks to isAir to catch CAVE_AIR Spigot Changes: f2c1cd15 Rebuild patches bcd458ad Reformat patches
1714 lines
82 KiB
Diff
1714 lines
82 KiB
Diff
From 1774cd5c93235fad7e6f9f9fa318b358f40cb6de 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 4867615215..df24e3297b 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
@@ -1,7 +1,11 @@
|
|
package com.destroystokyo.paper;
|
|
|
|
+import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.ChunkEdgeMode;
|
|
+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.configuration.file.YamlConfiguration;
|
|
import org.spigotmc.SpigotWorldConfig;
|
|
@@ -502,4 +506,43 @@ public class PaperWorldConfig {
|
|
private void maxAutoSaveChunksPerTick() {
|
|
maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24);
|
|
}
|
|
+
|
|
+ public boolean antiXray;
|
|
+ public boolean asynchronous;
|
|
+ public EngineMode engineMode;
|
|
+ public ChunkEdgeMode chunkEdgeMode;
|
|
+ public int maxChunkSectionIndex;
|
|
+ public int updateRadius;
|
|
+ public List<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;
|
|
+ chunkEdgeMode = ChunkEdgeMode.getById(getInt("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId()));
|
|
+ chunkEdgeMode = chunkEdgeMode == null ? ChunkEdgeMode.DEFAULT : chunkEdgeMode;
|
|
+
|
|
+ if (chunkEdgeMode != ChunkEdgeMode.WAIT) {
|
|
+ log("Migrating anti-xray chunk edge mode to " + ChunkEdgeMode.WAIT + " (" + ChunkEdgeMode.WAIT.getId() + ")");
|
|
+ chunkEdgeMode = ChunkEdgeMode.WAIT;
|
|
+ set("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId());
|
|
+ }
|
|
+
|
|
+ maxChunkSectionIndex = getInt("anti-xray.max-chunk-section-index", 3);
|
|
+ maxChunkSectionIndex = maxChunkSectionIndex > 15 ? 15 : maxChunkSectionIndex;
|
|
+ updateRadius = getInt("anti-xray.update-radius", 2);
|
|
+ hiddenBlocks = getList("anti-xray.hidden-blocks", Arrays.asList("gold_ore", "iron_ore", "coal_ore", "lapis_ore", "mossy_cobblestone", "obsidian", "chest", "diamond_ore", "redstone_ore", "clay", "emerald_ore", "ender_chest"));
|
|
+ replacementBlocks = getList("anti-xray.replacement-blocks", Arrays.asList("stone", "oak_planks"));
|
|
+ if (PaperConfig.version < 19) {
|
|
+ hiddenBlocks.remove("lit_redstone_ore");
|
|
+ int index = replacementBlocks.indexOf("planks");
|
|
+ if (index != -1) {
|
|
+ replacementBlocks.set(index, "oak_planks");
|
|
+ }
|
|
+ set("anti-xray.hidden-blocks", hiddenBlocks);
|
|
+ set("anti-xray.replacement-blocks", replacementBlocks);
|
|
+ }
|
|
+ log("Anti-Xray: " + (antiXray ? "enabled" : "disabled") + " / Engine Mode: " + engineMode.getDescription() + " / Chunk Edge Mode: " + chunkEdgeMode.getDescription() + " / Up to " + ((maxChunkSectionIndex + 1) * 16) + " blocks / Update Radius: " + updateRadius);
|
|
+ }
|
|
}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java
|
|
new file mode 100644
|
|
index 0000000000..f7e376ce6a
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java
|
|
@@ -0,0 +1,46 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+import net.minecraft.server.BlockPosition;
|
|
+import net.minecraft.server.Chunk;
|
|
+import net.minecraft.server.ChunkSection;
|
|
+import net.minecraft.server.EnumDirection;
|
|
+import net.minecraft.server.IBlockData;
|
|
+import net.minecraft.server.IChunkAccess;
|
|
+import net.minecraft.server.IWorldReader;
|
|
+import net.minecraft.server.PacketPlayOutMapChunk;
|
|
+import net.minecraft.server.PlayerInteractManager;
|
|
+import net.minecraft.server.World;
|
|
+
|
|
+public class ChunkPacketBlockController {
|
|
+
|
|
+ public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController();
|
|
+
|
|
+ protected ChunkPacketBlockController() {
|
|
+
|
|
+ }
|
|
+
|
|
+ public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean initializeBlocks) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public ChunkPacketInfo<IBlockData> getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk,
|
|
+ int chunkSectionSelector, boolean forceLoad) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo<IBlockData> chunkPacketInfo, boolean loadChunks, Integer ticketHold) {
|
|
+ packetPlayOutMapChunk.setReady(true);
|
|
+ }
|
|
+
|
|
+ public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) {
|
|
+
|
|
+ }
|
|
+
|
|
+ public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) {
|
|
+
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
|
new file mode 100644
|
|
index 0000000000..23626bef3a
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
|
@@ -0,0 +1,782 @@
|
|
+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 java.util.concurrent.atomic.AtomicInteger;
|
|
+import java.util.function.Supplier;
|
|
+
|
|
+import net.minecraft.server.*;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.World.Environment;
|
|
+
|
|
+import com.destroystokyo.paper.PaperWorldConfig;
|
|
+
|
|
+public class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController {
|
|
+
|
|
+ private static ExecutorService executorServiceInstance = null;
|
|
+ private final ExecutorService executorService;
|
|
+ private final boolean asynchronous;
|
|
+ private final EngineMode engineMode;
|
|
+ private final ChunkEdgeMode chunkEdgeMode;
|
|
+ private final int maxChunkSectionIndex;
|
|
+ private final int updateRadius;
|
|
+ private final IBlockData[] predefinedBlockData;
|
|
+ private final IBlockData[] predefinedBlockDataStone;
|
|
+ private final IBlockData[] predefinedBlockDataNetherrack;
|
|
+ private final IBlockData[] predefinedBlockDataEndStone;
|
|
+ private final int[] predefinedBlockDataBitsGlobal;
|
|
+ private final int[] predefinedBlockDataBitsStoneGlobal;
|
|
+ private final int[] predefinedBlockDataBitsNetherrackGlobal;
|
|
+ private final int[] predefinedBlockDataBitsEndStoneGlobal;
|
|
+ private final boolean[] solidGlobal = new boolean[Block.REGISTRY_ID.size()];
|
|
+ private final boolean[] obfuscateGlobal = new boolean[Block.REGISTRY_ID.size()];
|
|
+ private final ChunkSection[] emptyNearbyChunkSections = {Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION};
|
|
+ private final int maxBlockYUpdatePosition;
|
|
+
|
|
+ public ChunkPacketBlockControllerAntiXray(PaperWorldConfig paperWorldConfig) {
|
|
+ asynchronous = paperWorldConfig.asynchronous;
|
|
+ engineMode = paperWorldConfig.engineMode;
|
|
+ chunkEdgeMode = paperWorldConfig.chunkEdgeMode;
|
|
+ maxChunkSectionIndex = paperWorldConfig.maxChunkSectionIndex;
|
|
+ updateRadius = paperWorldConfig.updateRadius;
|
|
+
|
|
+ if (asynchronous) {
|
|
+ executorService = getExecutorServiceInstance();
|
|
+ } else {
|
|
+ executorService = null;
|
|
+ }
|
|
+
|
|
+ List<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(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean initializeBlocks) {
|
|
+ //Return the block data which should be added to the data palettes so that they can be used for the obfuscation
|
|
+ if (chunkSection.getYPosition() >> 4 <= maxChunkSectionIndex) {
|
|
+ switch (engineMode) {
|
|
+ case HIDE:
|
|
+ if (world instanceof GeneratorAccess) {
|
|
+ switch (((GeneratorAccess) world).getMinecraftWorld().getWorld().getEnvironment()) {
|
|
+ case NETHER:
|
|
+ return predefinedBlockDataNetherrack;
|
|
+ case THE_END:
|
|
+ return predefinedBlockDataEndStone;
|
|
+ default:
|
|
+ return predefinedBlockDataStone;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ default:
|
|
+ return predefinedBlockData;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ private final AtomicInteger xrayRequests = new AtomicInteger();
|
|
+
|
|
+ private Integer addXrayTickets(final int x, final int z, final ChunkProviderServer chunkProvider) {
|
|
+ final Integer hold = Integer.valueOf(this.xrayRequests.getAndIncrement());
|
|
+
|
|
+ // Add at ticket level 33, which is just enough to keep chunks loaded
|
|
+ chunkProvider.addTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z), 0, hold);
|
|
+ chunkProvider.addTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x - 1, z), 0, hold);
|
|
+ chunkProvider.addTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x + 1, z), 0, hold);
|
|
+ chunkProvider.addTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z - 1), 0, hold);
|
|
+ chunkProvider.addTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z + 1), 0, hold);
|
|
+
|
|
+ return hold;
|
|
+ }
|
|
+
|
|
+ private void removeXrayTickets(final int x, final int z, final ChunkProviderServer chunkProvider, final Integer hold) {
|
|
+ // Remove at ticket level 33 (same one we added as), which is just enough to keep chunks loaded
|
|
+ chunkProvider.removeTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z), 0, hold);
|
|
+ chunkProvider.removeTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x - 1, z), 0, hold);
|
|
+ chunkProvider.removeTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x + 1, z), 0, hold);
|
|
+ chunkProvider.removeTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z - 1), 0, hold);
|
|
+ chunkProvider.removeTicket(TicketType.ANTIXRAY, new ChunkCoordIntPair(x, z + 1), 0, hold);
|
|
+ }
|
|
+
|
|
+ private void loadNeighbours(Chunk chunk) {
|
|
+ int locX = chunk.getPos().x;
|
|
+ int locZ = chunk.getPos().z;
|
|
+ chunk.world.getChunkAt(locX - 1, locZ);
|
|
+ chunk.world.getChunkAt(locX + 1, locZ);
|
|
+ chunk.world.getChunkAt(locX, locZ - 1);
|
|
+ chunk.world.getChunkAt(locX, locZ + 1);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) {
|
|
+ int locX = chunk.getPos().x;
|
|
+ int locZ = chunk.getPos().z;
|
|
+ WorldServer world = (WorldServer)chunk.world;
|
|
+ ChunkProviderServer chunkProvider = world.getChunkProvider();
|
|
+
|
|
+ //Load nearby chunks if necessary
|
|
+ if (force || chunkEdgeMode == ChunkEdgeMode.LOAD) { // TODO temporary
|
|
+ // if forced, load NOW;
|
|
+ this.loadNeighbours(chunk);
|
|
+ } else if (chunkEdgeMode == ChunkEdgeMode.WAIT) {
|
|
+ if (chunkProvider.getChunkAtIfCachedImmediately(locX - 1, locZ) == null ||
|
|
+ chunkProvider.getChunkAtIfCachedImmediately(locX + 1, locZ) == null ||
|
|
+ chunkProvider.getChunkAtIfCachedImmediately(locX, locZ - 1) == null ||
|
|
+ chunkProvider.getChunkAtIfCachedImmediately(locX, locZ + 1) == null) {
|
|
+ //Don't create the chunk packet now, wait until nearby chunks are loaded and create it later
|
|
+ return false;
|
|
+ }
|
|
+ } else if (false && chunkEdgeMode == ChunkEdgeMode.LOAD) {
|
|
+ // TODO Note: These should be asynchronous loads; however we have no such thing in 1.14.
|
|
+ boolean missingChunk = false;
|
|
+ //noinspection ConstantConditions
|
|
+ /*
|
|
+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX - 1, chunk.locZ, true, true, c -> {}) == null;
|
|
+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX + 1, chunk.locZ, true, true, c -> {}) == null;
|
|
+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ - 1, true, true, c -> {}) == null;
|
|
+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ + 1, true, true, c -> {}) == null;
|
|
+ */
|
|
+ if (missingChunk) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ //Create the chunk packet now
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ChunkPacketInfoAntiXray getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk,
|
|
+ int chunkSectionSelector, boolean forceLoad) {
|
|
+ // Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later
|
|
+ // 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, boolean loadChunks, Integer hold) {
|
|
+ if (!Bukkit.isPrimaryThread()) {
|
|
+ // plugins?
|
|
+ final Integer finalHold = hold;
|
|
+ MinecraftServer.getServer().scheduleOnMain(() -> {
|
|
+ this.modifyBlocks(packetPlayOutMapChunk, chunkPacketInfo, loadChunks, finalHold);
|
|
+ });
|
|
+ return;
|
|
+ }
|
|
+ Chunk chunk = chunkPacketInfo.getChunk();
|
|
+ int locX = chunk.getPos().x;
|
|
+ int locZ = chunk.getPos().z;
|
|
+ WorldServer world = (WorldServer)chunk.world;
|
|
+
|
|
+ Chunk[] chunks = new Chunk[] {
|
|
+ (Chunk)world.getChunkIfLoadedImmediately(locX - 1, locZ),
|
|
+ (Chunk)world.getChunkIfLoadedImmediately(locX + 1, locZ),
|
|
+ (Chunk)world.getChunkIfLoadedImmediately(locX, locZ - 1),
|
|
+ (Chunk)world.getChunkIfLoadedImmediately(locX, locZ + 1)
|
|
+ };
|
|
+
|
|
+ if (loadChunks) {
|
|
+ // Note: This ugly hack is to get us out of the general chunk load/unload queue to prevent deadlock
|
|
+
|
|
+ if (chunks[0] == null || chunks[1] == null || chunks[2] == null || chunks[3] == null) {
|
|
+ // we need to load
|
|
+ MinecraftServer.getServer().scheduleOnMain(() -> {
|
|
+ Integer ticketHold = this.addXrayTickets(locX, locZ, world.getChunkProvider());
|
|
+ this.loadNeighbours(chunk);
|
|
+ this.modifyBlocks(packetPlayOutMapChunk, chunkPacketInfo, false, ticketHold);
|
|
+ });
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ hold = this.addXrayTickets(locX, locZ, world.getChunkProvider());
|
|
+ // fall through to normal behavior, our chunks are now loaded & have a ticket
|
|
+ }
|
|
+
|
|
+ ((ChunkPacketInfoAntiXray)chunkPacketInfo).setNearbyChunks(chunks);
|
|
+ ((ChunkPacketInfoAntiXray)chunkPacketInfo).ticketHold = hold;
|
|
+
|
|
+ if (asynchronous) {
|
|
+ executorService.submit((ChunkPacketInfoAntiXray) chunkPacketInfo);
|
|
+ } else {
|
|
+ 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) {
|
|
+ try {
|
|
+ boolean[] solidTemp = null;
|
|
+ boolean[] obfuscateTemp = null;
|
|
+ dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData());
|
|
+ dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData());
|
|
+ int counter = 0;
|
|
+
|
|
+ for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) {
|
|
+ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) {
|
|
+ int[] predefinedBlockDataBitsTemp;
|
|
+
|
|
+ if (chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex) == ChunkSection.GLOBAL_PALETTE) {
|
|
+ predefinedBlockDataBitsTemp = engineMode == EngineMode.HIDE ? chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.NETHER ? predefinedBlockDataBitsNetherrackGlobal : chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.THE_END ? predefinedBlockDataBitsEndStoneGlobal : predefinedBlockDataBitsStoneGlobal : predefinedBlockDataBitsGlobal;
|
|
+ } else {
|
|
+ predefinedBlockDataBitsTemp = predefinedBlockDataBits == null ? predefinedBlockDataBits = engineMode == EngineMode.HIDE ? new int[1] : new int[predefinedBlockData.length] : predefinedBlockDataBits;
|
|
+
|
|
+ for (int i = 0; i < predefinedBlockDataBitsTemp.length; i++) {
|
|
+ predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).getOrCreateIdFor(chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex)[i]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex));
|
|
+
|
|
+ //Check if the chunk section below was not obfuscated
|
|
+ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex - 1) == null) {
|
|
+ //If so, initialize some stuff
|
|
+ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex));
|
|
+ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex));
|
|
+ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), solid, solidGlobal);
|
|
+ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), obfuscate, obfuscateGlobal);
|
|
+ //Read the blocks of the upper layer of the chunk section below if it exists
|
|
+ ChunkSection belowChunkSection = null;
|
|
+ boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex - 1]) == Chunk.EMPTY_CHUNK_SECTION;
|
|
+
|
|
+ for (int z = 0; z < 16; z++) {
|
|
+ for (int x = 0; x < 16; x++) {
|
|
+ current[z][x] = true;
|
|
+ next[z][x] = skipFirstLayer || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(belowChunkSection.getType(x, 15, z))];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ //Abuse the obfuscateLayer method to read the blocks of the first layer of the current chunk section
|
|
+ dataBitsWriter.setBitsPerObject(0);
|
|
+ obfuscateLayer(-1, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, emptyNearbyChunkSections, counter);
|
|
+ }
|
|
+
|
|
+ dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex));
|
|
+ nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex];
|
|
+ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex];
|
|
+ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex];
|
|
+ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex];
|
|
+
|
|
+ //Obfuscate all layers of the current chunk section except the upper one
|
|
+ for (int y = 0; y < 15; y++) {
|
|
+ boolean[][] temp = current;
|
|
+ current = next;
|
|
+ next = nextNext;
|
|
+ nextNext = temp;
|
|
+ counter = obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
|
|
+ }
|
|
+
|
|
+ //Check if the chunk section above doesn't need obfuscation
|
|
+ if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex + 1) == null) {
|
|
+ //If so, obfuscate the upper layer of the current chunk section by reading blocks of the first layer from the chunk section above if it exists
|
|
+ ChunkSection aboveChunkSection;
|
|
+
|
|
+ if (chunkSectionIndex != 15 && (aboveChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex + 1]) != Chunk.EMPTY_CHUNK_SECTION) {
|
|
+ boolean[][] temp = current;
|
|
+ current = next;
|
|
+ next = nextNext;
|
|
+ nextNext = temp;
|
|
+
|
|
+ for (int z = 0; z < 16; z++) {
|
|
+ for (int x = 0; x < 16; x++) {
|
|
+ if (!solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(aboveChunkSection.getType(x, 0, z))]) {
|
|
+ current[z][x] = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ //There is nothing to read anymore
|
|
+ dataBitsReader.setBitsPerObject(0);
|
|
+ solid[0] = true;
|
|
+ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
|
|
+ }
|
|
+ } else {
|
|
+ //If not, initialize the reader and other stuff for the chunk section above to obfuscate the upper layer of the current chunk section
|
|
+ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex + 1));
|
|
+ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex + 1));
|
|
+ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), solid, solidGlobal);
|
|
+ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal);
|
|
+ boolean[][] temp = current;
|
|
+ current = next;
|
|
+ next = nextNext;
|
|
+ nextNext = temp;
|
|
+ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
|
|
+ }
|
|
+
|
|
+ dataBitsWriter.finish();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true);
|
|
+
|
|
+ } finally {
|
|
+ if (chunkPacketInfoAntiXray.ticketHold != null) {
|
|
+ Runnable runnable = () -> {
|
|
+ Chunk chunk = chunkPacketInfoAntiXray.getChunk();
|
|
+ ChunkCoordIntPair chunkPos = chunk.getPos();
|
|
+
|
|
+ ChunkPacketBlockControllerAntiXray.this.removeXrayTickets(chunkPos.x, chunkPos.z, (ChunkProviderServer) chunk.world.getChunkProvider(),
|
|
+ chunkPacketInfoAntiXray.ticketHold);
|
|
+ };
|
|
+ if (MinecraftServer.getServer().isMainThread()) {
|
|
+ runnable.run();
|
|
+ } else {
|
|
+ MinecraftServer.getServer().scheduleOnMain(runnable);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public enum ChunkEdgeMode {
|
|
+
|
|
+ DEFAULT(1, "default"),
|
|
+ WAIT(2, "wait until nearby chunks are loaded"),
|
|
+ LOAD(3, "load nearby chunks");
|
|
+
|
|
+ private final int id;
|
|
+ private final String description;
|
|
+
|
|
+ ChunkEdgeMode(int id, String description) {
|
|
+ this.id = id;
|
|
+ this.description = description;
|
|
+ }
|
|
+
|
|
+ public static ChunkEdgeMode getById(int id) {
|
|
+ for (ChunkEdgeMode chunkEdgeMode : values()) {
|
|
+ if (chunkEdgeMode.id == id) {
|
|
+ return chunkEdgeMode;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public int getId() {
|
|
+ return id;
|
|
+ }
|
|
+
|
|
+ public String getDescription() {
|
|
+ return description;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java
|
|
new file mode 100644
|
|
index 0000000000..a68bace353
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java
|
|
@@ -0,0 +1,81 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+import net.minecraft.server.Chunk;
|
|
+import net.minecraft.server.DataPalette;
|
|
+import net.minecraft.server.PacketPlayOutMapChunk;
|
|
+
|
|
+public class ChunkPacketInfo<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 getOrCreateIdForIndex(int chunkSectionIndex) {
|
|
+ return dataBitsIndexes[chunkSectionIndex];
|
|
+ }
|
|
+
|
|
+ public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) {
|
|
+ dataBitsIndexes[chunkSectionIndex] = dataBitsIndex;
|
|
+ }
|
|
+
|
|
+ @SuppressWarnings("unchecked")
|
|
+ public T[] getPredefinedObjects(int chunkSectionIndex) {
|
|
+ return (T[]) predefinedObjects[chunkSectionIndex];
|
|
+ }
|
|
+
|
|
+ public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) {
|
|
+ this.predefinedObjects[chunkSectionIndex] = predefinedObjects;
|
|
+ }
|
|
+
|
|
+ public boolean isWritten(int chunkSectionIndex) {
|
|
+ return bitsPerObject[chunkSectionIndex] != 0;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java
|
|
new file mode 100644
|
|
index 0000000000..067dfb2f14
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java
|
|
@@ -0,0 +1,31 @@
|
|
+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 Integer ticketHold;
|
|
+
|
|
+ public ChunkPacketInfoAntiXray(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector,
|
|
+ ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) {
|
|
+ super(packetPlayOutMapChunk, chunk, chunkSectionSelector);
|
|
+ this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray;
|
|
+ }
|
|
+
|
|
+ public Chunk[] getNearbyChunks() {
|
|
+ return nearbyChunks;
|
|
+ }
|
|
+
|
|
+ public void setNearbyChunks(Chunk... nearbyChunks) {
|
|
+ this.nearbyChunks = nearbyChunks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ chunkPacketBlockControllerAntiXray.obfuscate(this);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java
|
|
new file mode 100644
|
|
index 0000000000..cc586827aa
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java
|
|
@@ -0,0 +1,56 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+public class DataBitsReader {
|
|
+
|
|
+ private byte[] dataBits;
|
|
+ private int bitsPerObject;
|
|
+ private int mask;
|
|
+ private int longInDataBitsIndex;
|
|
+ private int bitInLongIndex;
|
|
+ private long current;
|
|
+
|
|
+ public void setDataBits(byte[] dataBits) {
|
|
+ this.dataBits = dataBits;
|
|
+ }
|
|
+
|
|
+ public void setBitsPerObject(int bitsPerObject) {
|
|
+ this.bitsPerObject = bitsPerObject;
|
|
+ mask = (1 << bitsPerObject) - 1;
|
|
+ }
|
|
+
|
|
+ public void setIndex(int index) {
|
|
+ this.longInDataBitsIndex = index;
|
|
+ bitInLongIndex = 0;
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ private void init() {
|
|
+ if (dataBits.length > longInDataBitsIndex + 7) {
|
|
+ current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public int read() {
|
|
+ int value = (int) (current >>> bitInLongIndex) & mask;
|
|
+ bitInLongIndex += bitsPerObject;
|
|
+
|
|
+ if (bitInLongIndex > 63) {
|
|
+ bitInLongIndex -= 64;
|
|
+ longInDataBitsIndex += 8;
|
|
+ init();
|
|
+
|
|
+ if (bitInLongIndex > 0) {
|
|
+ value |= current << bitsPerObject - bitInLongIndex & mask;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return value;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java
|
|
new file mode 100644
|
|
index 0000000000..37093419cf
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java
|
|
@@ -0,0 +1,84 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+public class DataBitsWriter {
|
|
+
|
|
+ private byte[] dataBits;
|
|
+ private int bitsPerObject;
|
|
+ private long mask;
|
|
+ private int longInDataBitsIndex;
|
|
+ private int bitInLongIndex;
|
|
+ private long current;
|
|
+ private boolean dirty;
|
|
+
|
|
+ public void setDataBits(byte[] dataBits) {
|
|
+ this.dataBits = dataBits;
|
|
+ }
|
|
+
|
|
+ public void setBitsPerObject(int bitsPerObject) {
|
|
+ this.bitsPerObject = bitsPerObject;
|
|
+ mask = (1 << bitsPerObject) - 1;
|
|
+ }
|
|
+
|
|
+ public void setIndex(int index) {
|
|
+ this.longInDataBitsIndex = index;
|
|
+ bitInLongIndex = 0;
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ private void init() {
|
|
+ if (dataBits.length > longInDataBitsIndex + 7) {
|
|
+ current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
|
+ | (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
|
+ }
|
|
+
|
|
+ dirty = false;
|
|
+ }
|
|
+
|
|
+ public void finish() {
|
|
+ if (dirty && dataBits.length > longInDataBitsIndex + 7) {
|
|
+ dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff);
|
|
+ dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void write(int value) {
|
|
+ current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
|
|
+ dirty = true;
|
|
+ bitInLongIndex += bitsPerObject;
|
|
+
|
|
+ if (bitInLongIndex > 63) {
|
|
+ finish();
|
|
+ bitInLongIndex -= 64;
|
|
+ longInDataBitsIndex += 8;
|
|
+ init();
|
|
+
|
|
+ if (bitInLongIndex > 0) {
|
|
+ current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex;
|
|
+ dirty = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void skip() {
|
|
+ bitInLongIndex += bitsPerObject;
|
|
+
|
|
+ if (bitInLongIndex > 63) {
|
|
+ finish();
|
|
+ bitInLongIndex -= 64;
|
|
+ longInDataBitsIndex += 8;
|
|
+ init();
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
|
|
index 76e87c211b..3fdcc2e5d4 100644
|
|
--- a/src/main/java/net/minecraft/server/Chunk.java
|
|
+++ b/src/main/java/net/minecraft/server/Chunk.java
|
|
@@ -320,7 +320,7 @@ public class Chunk implements IChunkAccess {
|
|
return null;
|
|
}
|
|
|
|
- chunksection = new ChunkSection(j >> 4 << 4);
|
|
+ chunksection = new ChunkSection(j >> 4 << 4, this, this.world, true); // Paper - Anti-Xray
|
|
this.sections[j >> 4] = chunksection;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
|
index 961228e9df..a950ad801d 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
|
|
|
|
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
|
|
|
|
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 0d5deee365..4526527aca 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkSection.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkSection.java
|
|
@@ -6,21 +6,31 @@ public class ChunkSection {
|
|
|
|
public static final DataPalette<IBlockData> GLOBAL_PALETTE = new DataPaletteGlobal<>(Block.REGISTRY_ID, Blocks.AIR.getBlockData());
|
|
private final int yPos;
|
|
- private short nonEmptyBlockCount;
|
|
+ short nonEmptyBlockCount; // Paper - private -> package-private
|
|
private short tickingBlockCount;
|
|
private short e;
|
|
final DataPaletteBlock<IBlockData> blockIds;
|
|
|
|
public ChunkSection(int i) {
|
|
- this(i, (short) 0, (short) 0, (short) 0);
|
|
+ // Paper start - add parameters
|
|
+ this(i, (IChunkAccess)null, (IWorldReader)null, true);
|
|
+ }
|
|
+ public ChunkSection(int i, IChunkAccess chunk, IWorldReader world, boolean initializeBlocks) {
|
|
+ this(i, (short) 0, (short) 0, (short) 0, chunk, world, initializeBlocks);
|
|
+ // Paper end
|
|
}
|
|
|
|
public ChunkSection(int i, short short0, short short1, short short2) {
|
|
+ // Paper start - add parameters
|
|
+ this(i, short0, short1, short2, (IChunkAccess)null, (IWorldReader)null, true);
|
|
+ }
|
|
+ public ChunkSection(int i, short short0, short short1, short short2, IChunkAccess chunk, IWorldReader world, boolean initializeBlocks) {
|
|
+ // Paper end
|
|
this.yPos = i;
|
|
this.nonEmptyBlockCount = short0;
|
|
this.tickingBlockCount = short1;
|
|
this.e = short2;
|
|
- this.blockIds = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData());
|
|
+ this.blockIds = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData(), world instanceof GeneratorAccess ? ((GeneratorAccess) world).getMinecraftWorld().chunkPacketBlockController.getPredefinedBlockData(world, chunk, this, initializeBlocks) : null, initializeBlocks); // Paper - Anti-Xray - Add predefined block data
|
|
}
|
|
|
|
public IBlockData getType(int i, int j, int k) {
|
|
diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java
|
|
index 2c1d1b1a55..44aed67274 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
|
|
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
|
|
@@ -47,14 +49,50 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
|
|
}
|
|
|
|
public DataPaletteBlock(DataPalette<T> datapalette, RegistryBlockID<T> registryblockid, Function<NBTTagCompound, T> function, Function<T, NBTTagCompound> function1, T t0) {
|
|
+ // Paper start - Anti-Xray - Support default constructor
|
|
+ this(datapalette, registryblockid, function, function1, t0, null, true);
|
|
+ }
|
|
+
|
|
+ public DataPaletteBlock(DataPalette<T> datapalette, RegistryBlockID<T> registryblockid, Function<NBTTagCompound, T> function, Function<T, NBTTagCompound> function1, T t0, T[] predefinedObjects, boolean initialize) {
|
|
+ // Paper end - Anti-Xray - Add predefined objects
|
|
this.b = datapalette;
|
|
this.d = registryblockid;
|
|
this.e = function;
|
|
this.f = function1;
|
|
this.g = t0;
|
|
- this.b(4);
|
|
+ // Paper start - Anti-Xray - Add predefined objects
|
|
+ this.predefinedObjects = predefinedObjects;
|
|
+
|
|
+ if (initialize) {
|
|
+ if (predefinedObjects == null) {
|
|
+ // Default
|
|
+ this.initialize(4);
|
|
+ } else {
|
|
+ // MathHelper.d() is trailingBits(roundCeilPow2(n)), alternatively; (int)ceil(log2(n)); however it's trash, use numberOfLeadingZeros instead
|
|
+ // Count the bits of the maximum array index to initialize a data palette with enough space from the beginning
|
|
+ // The length of the array is used because air is also added to the data palette from the beginning
|
|
+ // Start with at least 4
|
|
+ int maxIndex = predefinedObjects.length >> 4;
|
|
+ int bitCount = (32 - Integer.numberOfLeadingZeros(Math.max(16, maxIndex) - 1));
|
|
+
|
|
+ // Initialize with at least 15 free indixes
|
|
+ this.initialize((1 << bitCount) - predefinedObjects.length < 16 ? bitCount + 1 : bitCount);
|
|
+ this.addPredefinedObjects();
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
}
|
|
|
|
+ // Paper start - Anti-Xray - Add predefined objects
|
|
+ private void addPredefinedObjects() {
|
|
+ if (this.predefinedObjects != null && this.getDataPalette() != this.getDataPaletteGlobal()) {
|
|
+ for (int i = 0; i < this.predefinedObjects.length; i++) {
|
|
+ this.getDataPalette().getOrCreateIdFor(this.predefinedObjects[i]);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
private static int b(int i, int j, int k) {
|
|
return j << 8 | k << 4 | i;
|
|
}
|
|
@@ -88,6 +126,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));
|
|
|
|
@@ -139,22 +178,39 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
|
|
|
|
public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } // Paper - OBFHELPER
|
|
public void b(PacketDataSerializer packetdataserializer) {
|
|
+ // Paper start - add parameters
|
|
+ this.writeDataPaletteBlock(packetdataserializer, null, 0);
|
|
+ }
|
|
+ public void writeDataPaletteBlock(PacketDataSerializer packetdataserializer, ChunkPacketInfo<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 e156804f7a..96a785af27 100644
|
|
--- a/src/main/java/net/minecraft/server/NetworkManager.java
|
|
+++ b/src/main/java/net/minecraft/server/NetworkManager.java
|
|
@@ -42,7 +42,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
|
return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
|
|
});
|
|
private final EnumProtocolDirection h;
|
|
- private final Queue<NetworkManager.QueuedPacket> packetQueue = Queues.newConcurrentLinkedQueue();
|
|
+ private final Queue<NetworkManager.QueuedPacket> packetQueue = Queues.newConcurrentLinkedQueue(); private final Queue<NetworkManager.QueuedPacket> getPacketQueue() { return this.packetQueue; } // Paper - OBFHELPER
|
|
public Channel channel;
|
|
public SocketAddress socketAddress; public void setSpoofedRemoteAddress(SocketAddress address) { this.socketAddress = address; } // Paper - OBFHELPER
|
|
// Spigot Start
|
|
@@ -164,8 +164,8 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
|
}
|
|
|
|
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.packetQueue.add(new NetworkManager.QueuedPacket(packet, genericfuturelistener));
|
|
@@ -223,21 +223,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) 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
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
}
|
|
}
|
|
+
|
|
+ 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 47710067a6..ef7ade797b 100644
|
|
--- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java
|
|
+++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java
|
|
@@ -1,5 +1,6 @@
|
|
package net.minecraft.server;
|
|
|
|
+import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray
|
|
import com.google.common.collect.Lists;
|
|
import io.netty.buffer.ByteBuf;
|
|
import io.netty.buffer.Unpooled;
|
|
@@ -20,8 +21,11 @@ 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() {}
|
|
+ public PacketPlayOutMapChunk() {
|
|
+ this.ready = true; // Paper - Async-Anti-Xray - Set the ready flag to true
|
|
+ }
|
|
|
|
// Paper start
|
|
private final java.util.List<Packet> extraPackets = new java.util.ArrayList<>();
|
|
@@ -33,6 +37,12 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|
}
|
|
// Paper end
|
|
public PacketPlayOutMapChunk(Chunk chunk, int i) {
|
|
+ // Paper start - add forceLoad param
|
|
+ this(chunk, i, false);
|
|
+ }
|
|
+ public PacketPlayOutMapChunk(Chunk chunk, int i, boolean forceLoad) {
|
|
+ // Paper end
|
|
+ ChunkPacketInfo<IBlockData> chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i, forceLoad); // Paper - Anti-Xray - Add chunk packet info
|
|
ChunkCoordIntPair chunkcoordintpair = chunk.getPos();
|
|
|
|
this.a = chunkcoordintpair.x;
|
|
@@ -55,7 +65,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());
|
|
+ }
|
|
+ // Paper end
|
|
+ this.c = this.writeChunk(new PacketDataSerializer(this.j()), chunk, i, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info
|
|
this.g = Lists.newArrayList();
|
|
iterator = chunk.getTileEntities().entrySet().iterator();
|
|
int totalSigns = 0; // Paper
|
|
@@ -81,9 +96,19 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|
this.g.add(nbttagcompound);
|
|
}
|
|
}
|
|
+ chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo, forceLoad, null); // Paper - Anti-Xray - Modify blocks
|
|
+ }
|
|
|
|
+ // Paper start - Async-Anti-Xray - Getter and Setter for the ready flag
|
|
+ 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();
|
|
@@ -150,6 +175,11 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|
|
|
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, chunkSectionSelector); } // Paper - OBFHELPER
|
|
public int a(PacketDataSerializer packetdataserializer, Chunk chunk, int i) {
|
|
+ // Paper start - Add parameter
|
|
+ return this.writeChunk(packetdataserializer, chunk, i, null);
|
|
+ }
|
|
+ public int writeChunk(PacketDataSerializer packetdataserializer, Chunk chunk, int i, ChunkPacketInfo<IBlockData> chunkPacketInfo) {
|
|
+ // Paper end
|
|
int j = 0;
|
|
ChunkSection[] achunksection = chunk.getSections();
|
|
int k = 0;
|
|
@@ -159,7 +189,8 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|
|
|
if (chunksection != Chunk.a && (!this.f() || !chunksection.c()) && (i & 1 << k) != 0) {
|
|
j |= 1 << k;
|
|
- chunksection.b(packetdataserializer);
|
|
+ packetdataserializer.writeShort(chunksection.nonEmptyBlockCount); // Paper - Anti-Xray - Add chunk packet info
|
|
+ chunksection.getBlocks().writeDataPaletteBlock(packetdataserializer, chunkPacketInfo, k); // Paper - Anti-Xray - Add chunk packet info
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
index 5108d3ee98..b556a8fefa 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
@@ -220,6 +220,11 @@ public class PlayerChunk {
|
|
World world = chunk.getWorld();
|
|
|
|
if (this.dirtyCount == 64) {
|
|
+ // Paper start - Anti-Xray - Load nearby chunks if necessary
|
|
+ if (!chunk.world.chunkPacketBlockController.onChunkPacketCreate(chunk, '\uffff', false)) {
|
|
+ return;
|
|
+ }
|
|
+ // Paper end
|
|
this.s = -1;
|
|
}
|
|
|
|
@@ -252,7 +257,7 @@ public class PlayerChunk {
|
|
this.a(world, blockposition);
|
|
}
|
|
} else if (this.dirtyCount == 64) {
|
|
- this.a(new PacketPlayOutMapChunk(chunk, this.r), false);
|
|
+ this.a(new PacketPlayOutMapChunk(chunk, this.r, true), false); // Paper - Anti-Xray
|
|
} else if (this.dirtyCount != 0) {
|
|
this.a(new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, chunk), false);
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
index 8bd212388a..be3dcf375b 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
@@ -603,7 +603,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
PlayerChunkMap.LOGGER.error("Couldn't load chunk {}", chunkcoordintpair, exception);
|
|
}
|
|
|
|
- return Either.left(new ProtoChunk(chunkcoordintpair, ChunkConverter.a));
|
|
+ return Either.left(new ProtoChunk(chunkcoordintpair, ChunkConverter.a, this.world)); // Paper - Anti-Xray
|
|
}, this.executor);
|
|
}
|
|
|
|
@@ -1322,7 +1322,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
|
|
private void a(EntityPlayer entityplayer, Packet<?>[] apacket, Chunk chunk) {
|
|
if (apacket[0] == null) {
|
|
- apacket[0] = new PacketPlayOutMapChunk(chunk, 65535);
|
|
+ apacket[0] = new PacketPlayOutMapChunk(chunk, 65535, true); // Paper - Anti-Xray
|
|
apacket[1] = new PacketPlayOutLightUpdate(chunk.getPos(), this.lightEngine);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java
|
|
index e2e5c17c24..ce4340a476 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerInteractManager.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java
|
|
@@ -264,6 +264,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 39339fa275..f376e21068 100644
|
|
--- a/src/main/java/net/minecraft/server/ProtoChunk.java
|
|
+++ b/src/main/java/net/minecraft/server/ProtoChunk.java
|
|
@@ -45,16 +45,28 @@ public class ProtoChunk implements IChunkAccess {
|
|
private long s;
|
|
private final Map<WorldGenStage.Features, BitSet> t;
|
|
private volatile boolean u;
|
|
+ private final GeneratorAccess world; // Paper - Anti-Xray
|
|
|
|
public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter) {
|
|
+ // Paper start - add world parameter
|
|
+ this(chunkcoordintpair, chunkconverter, (GeneratorAccess)null);
|
|
+ }
|
|
+ public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, GeneratorAccess world) {
|
|
+ // Paper end
|
|
this(chunkcoordintpair, chunkconverter, (ChunkSection[]) null, new ProtoChunkTickList<>((block) -> {
|
|
return block == null || block.getBlockData().isAir();
|
|
}, chunkcoordintpair), new ProtoChunkTickList<>((fluidtype) -> {
|
|
return fluidtype == null || fluidtype == FluidTypes.EMPTY;
|
|
- }, chunkcoordintpair));
|
|
+ }, chunkcoordintpair), world); // Paper - add world parameter
|
|
}
|
|
|
|
public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, @Nullable ChunkSection[] achunksection, ProtoChunkTickList<Block> protochunkticklist, ProtoChunkTickList<FluidType> protochunkticklist1) {
|
|
+ // Paper start - add world parameter
|
|
+ this(chunkcoordintpair, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, (GeneratorAccess)null);
|
|
+ }
|
|
+ public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, @Nullable ChunkSection[] achunksection, ProtoChunkTickList<Block> protochunkticklist, ProtoChunkTickList<FluidType> protochunkticklist1, GeneratorAccess world) {
|
|
+ this.world = world;
|
|
+ // Paper end
|
|
this.f = Maps.newEnumMap(HeightMap.Type.class);
|
|
this.g = ChunkStatus.EMPTY;
|
|
this.h = Maps.newHashMap();
|
|
@@ -207,7 +219,7 @@ public class ProtoChunk implements IChunkAccess {
|
|
|
|
public ChunkSection a(int i) {
|
|
if (this.j[i] == Chunk.a) {
|
|
- this.j[i] = new ChunkSection(i << 4);
|
|
+ this.j[i] = new ChunkSection(i << 4, this, this.world, true); // Paper - Anti-Xray
|
|
}
|
|
|
|
return this.j[i];
|
|
diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java
|
|
index a905a29e75..335b644351 100644
|
|
--- a/src/main/java/net/minecraft/server/TicketType.java
|
|
+++ b/src/main/java/net/minecraft/server/TicketType.java
|
|
@@ -21,6 +21,7 @@ public class TicketType<T> {
|
|
public static final TicketType<ChunkCoordIntPair> UNKNOWN = a("unknown", Comparator.comparingLong(ChunkCoordIntPair::pair), 1);
|
|
public static final TicketType<Unit> PLUGIN = a("plugin", (a, b) -> 0); // CraftBukkit
|
|
public static final TicketType<org.bukkit.plugin.Plugin> PLUGIN_TICKET = a("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // Craftbukkit
|
|
+ public static final TicketType<Integer> ANTIXRAY = a("antixray", Integer::compareTo); // Paper - Anti-Xray
|
|
|
|
public static <T> TicketType<T> a(String s, Comparator<T> comparator) {
|
|
return new TicketType<>(s, comparator, 0L);
|
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
|
index c089080a90..bcf326c859 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;
|
|
@@ -76,6 +78,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
|
|
@@ -117,6 +120,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
|
|
@@ -336,6 +340,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
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java
|
|
index 7772d59005..4570ed9991 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java
|
|
@@ -21,9 +21,11 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData {
|
|
private final int maxHeight;
|
|
private final ChunkSection[] sections;
|
|
private Set<BlockPosition> tiles;
|
|
+ private World world; // Paper - Anti-Xray
|
|
|
|
public CraftChunkData(World world) {
|
|
this(world.getMaxHeight());
|
|
+ this.world = world; // Paper - Anti-Xray
|
|
}
|
|
|
|
/* pp for tests */ CraftChunkData(int maxHeight) {
|
|
@@ -157,7 +159,7 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData {
|
|
private ChunkSection getChunkSection(int y, boolean create) {
|
|
ChunkSection section = sections[y >> 4];
|
|
if (create && section == null) {
|
|
- sections[y >> 4] = section = new ChunkSection(y);
|
|
+ sections[y >> 4] = section = new ChunkSection(y, null, world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) world).getHandle() : null, true); // Paper - Anti-Xray
|
|
}
|
|
return section;
|
|
}
|
|
--
|
|
2.25.0
|
|
|