mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-12 01:41:05 +01:00
cfa5b37fa8
Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing CraftBukkit Changes: 03b725233 SPIGOT-6823: Fix loading custom world in combination with superflat 359d0533a #970: Correct typo in README.md 110492932 Fix per-world worldborder command
1638 lines
88 KiB
Diff
1638 lines
88 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: stonar96 <minecraft.stonar96@gmail.com>
|
|
Date: Thu, 25 Nov 2021 13:27:51 +0100
|
|
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 723adafaf82a664b8bfff9d7d11e43e3eafafa6e..1cdfba84abcee02c0ac49367c97544bc4758715b 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
@@ -1,11 +1,13 @@
|
|
package com.destroystokyo.paper;
|
|
|
|
+import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
import java.util.stream.Collectors;
|
|
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
|
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
|
|
import net.minecraft.world.entity.MobCategory;
|
|
+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.configuration.file.YamlConfiguration;
|
|
import org.spigotmc.SpigotWorldConfig;
|
|
@@ -528,5 +530,40 @@ public class PaperWorldConfig {
|
|
private void lightQueueSize() {
|
|
lightQueueSize = getInt("light-queue-size", lightQueueSize);
|
|
}
|
|
+
|
|
+ public boolean antiXray;
|
|
+ public EngineMode engineMode;
|
|
+ public int maxBlockHeight;
|
|
+ public int updateRadius;
|
|
+ public boolean lavaObscures;
|
|
+ public boolean usePermission;
|
|
+ public List<String> hiddenBlocks;
|
|
+ public List<String> replacementBlocks;
|
|
+ private void antiXray() {
|
|
+ antiXray = getBoolean("anti-xray.enabled", false);
|
|
+ engineMode = EngineMode.getById(getInt("anti-xray.engine-mode", EngineMode.HIDE.getId()));
|
|
+ engineMode = engineMode == null ? EngineMode.HIDE : engineMode;
|
|
+ maxBlockHeight = getInt("anti-xray.max-block-height", 64);
|
|
+ updateRadius = getInt("anti-xray.update-radius", 2);
|
|
+ lavaObscures = getBoolean("anti-xray.lava-obscures", false);
|
|
+ usePermission = getBoolean("anti-xray.use-permission", false);
|
|
+ hiddenBlocks = getList("anti-xray.hidden-blocks", Arrays.asList("copper_ore", "deepslate_copper_ore", "gold_ore", "deepslate_gold_ore", "iron_ore", "deepslate_iron_ore",
|
|
+ "coal_ore", "deepslate_coal_ore", "lapis_ore", "deepslate_lapis_ore", "mossy_cobblestone", "obsidian", "chest", "diamond_ore", "deepslate_diamond_ore",
|
|
+ "redstone_ore", "deepslate_redstone_ore", "clay", "emerald_ore", "deepslate_emerald_ore", "ender_chest"));
|
|
+ replacementBlocks = getList("anti-xray.replacement-blocks", Arrays.asList("stone", "oak_planks", "deepslate"));
|
|
+ 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 " + ((maxBlockHeight >> 4) << 4) + " blocks / Update Radius: " + updateRadius);
|
|
+ if (antiXray && usePermission) {
|
|
+ Bukkit.getLogger().warning("You have enabled permission-based Anti-Xray checking - depending on your permission plugin, this may cause performance issues");
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
diff --git a/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java b/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..e448c26327b5f6189c3c52e698cff66c8f9ad81a
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java
|
|
@@ -0,0 +1,51 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+public final class BitStorageReader {
|
|
+
|
|
+ private byte[] buffer;
|
|
+ private int bits;
|
|
+ private int mask;
|
|
+ private int longInBufferIndex;
|
|
+ private int bitInLongIndex;
|
|
+ private long current;
|
|
+
|
|
+ public void setBuffer(byte[] buffer) {
|
|
+ this.buffer = buffer;
|
|
+ }
|
|
+
|
|
+ public void setBits(int bits) {
|
|
+ this.bits = bits;
|
|
+ mask = (1 << bits) - 1;
|
|
+ }
|
|
+
|
|
+ public void setIndex(int index) {
|
|
+ longInBufferIndex = index;
|
|
+ bitInLongIndex = 0;
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ private void init() {
|
|
+ if (buffer.length > longInBufferIndex + 7) {
|
|
+ current = ((((long) buffer[longInBufferIndex]) << 56)
|
|
+ | (((long) buffer[longInBufferIndex + 1] & 0xff) << 48)
|
|
+ | (((long) buffer[longInBufferIndex + 2] & 0xff) << 40)
|
|
+ | (((long) buffer[longInBufferIndex + 3] & 0xff) << 32)
|
|
+ | (((long) buffer[longInBufferIndex + 4] & 0xff) << 24)
|
|
+ | (((long) buffer[longInBufferIndex + 5] & 0xff) << 16)
|
|
+ | (((long) buffer[longInBufferIndex + 6] & 0xff) << 8)
|
|
+ | (((long) buffer[longInBufferIndex + 7] & 0xff)));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public int read() {
|
|
+ if (bitInLongIndex + bits > 64) {
|
|
+ bitInLongIndex = 0;
|
|
+ longInBufferIndex += 8;
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ int value = (int) (current >>> bitInLongIndex) & mask;
|
|
+ bitInLongIndex += bits;
|
|
+ return value;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java b/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..e4540ea278f2dc871cb6a3cb8897559bfd65e134
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java
|
|
@@ -0,0 +1,79 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+public final class BitStorageWriter {
|
|
+
|
|
+ private byte[] buffer;
|
|
+ private int bits;
|
|
+ private long mask;
|
|
+ private int longInBufferIndex;
|
|
+ private int bitInLongIndex;
|
|
+ private long current;
|
|
+ private boolean dirty;
|
|
+
|
|
+ public void setBuffer(byte[] buffer) {
|
|
+ this.buffer = buffer;
|
|
+ }
|
|
+
|
|
+ public void setBits(int bits) {
|
|
+ this.bits = bits;
|
|
+ mask = (1L << bits) - 1;
|
|
+ }
|
|
+
|
|
+ public void setIndex(int index) {
|
|
+ longInBufferIndex = index;
|
|
+ bitInLongIndex = 0;
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ private void init() {
|
|
+ if (buffer.length > longInBufferIndex + 7) {
|
|
+ current = ((((long) buffer[longInBufferIndex]) << 56)
|
|
+ | (((long) buffer[longInBufferIndex + 1] & 0xff) << 48)
|
|
+ | (((long) buffer[longInBufferIndex + 2] & 0xff) << 40)
|
|
+ | (((long) buffer[longInBufferIndex + 3] & 0xff) << 32)
|
|
+ | (((long) buffer[longInBufferIndex + 4] & 0xff) << 24)
|
|
+ | (((long) buffer[longInBufferIndex + 5] & 0xff) << 16)
|
|
+ | (((long) buffer[longInBufferIndex + 6] & 0xff) << 8)
|
|
+ | (((long) buffer[longInBufferIndex + 7] & 0xff)));
|
|
+ }
|
|
+
|
|
+ dirty = false;
|
|
+ }
|
|
+
|
|
+ public void flush() {
|
|
+ if (dirty && buffer.length > longInBufferIndex + 7) {
|
|
+ buffer[longInBufferIndex] = (byte) (current >> 56 & 0xff);
|
|
+ buffer[longInBufferIndex + 1] = (byte) (current >> 48 & 0xff);
|
|
+ buffer[longInBufferIndex + 2] = (byte) (current >> 40 & 0xff);
|
|
+ buffer[longInBufferIndex + 3] = (byte) (current >> 32 & 0xff);
|
|
+ buffer[longInBufferIndex + 4] = (byte) (current >> 24 & 0xff);
|
|
+ buffer[longInBufferIndex + 5] = (byte) (current >> 16 & 0xff);
|
|
+ buffer[longInBufferIndex + 6] = (byte) (current >> 8 & 0xff);
|
|
+ buffer[longInBufferIndex + 7] = (byte) (current & 0xff);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void write(int value) {
|
|
+ if (bitInLongIndex + bits > 64) {
|
|
+ flush();
|
|
+ bitInLongIndex = 0;
|
|
+ longInBufferIndex += 8;
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
|
|
+ dirty = true;
|
|
+ bitInLongIndex += bits;
|
|
+ }
|
|
+
|
|
+ public void skip() {
|
|
+ bitInLongIndex += bits;
|
|
+
|
|
+ if (bitInLongIndex > 64) {
|
|
+ flush();
|
|
+ bitInLongIndex = bits;
|
|
+ longInBufferIndex += 8;
|
|
+ init();
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..aabad39d13ead83042ec2e4dd7f4ed4966af650d
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java
|
|
@@ -0,0 +1,45 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.core.Direction;
|
|
+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
|
+import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.server.level.ServerPlayerGameMode;
|
|
+import net.minecraft.world.level.ChunkPos;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.chunk.LevelChunk;
|
|
+
|
|
+public class ChunkPacketBlockController {
|
|
+
|
|
+ public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController();
|
|
+
|
|
+ protected ChunkPacketBlockController() {
|
|
+
|
|
+ }
|
|
+
|
|
+ public BlockState[] getPresetBlockStates(Level level, ChunkPos chunkPos, int bottomBlockY) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public boolean shouldModify(ServerPlayer player, LevelChunk chunk) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public ChunkPacketInfo<BlockState> getChunkPacketInfo(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public void modifyBlocks(ClientboundLevelChunkWithLightPacket chunkPacket, ChunkPacketInfo<BlockState> chunkPacketInfo) {
|
|
+ chunkPacket.setReady(true);
|
|
+ }
|
|
+
|
|
+ public void onBlockChange(Level level, BlockPos blockPos, BlockState newBlockState, BlockState oldBlockState, int flags, int maxUpdateDepth) {
|
|
+
|
|
+ }
|
|
+
|
|
+ public void onPlayerLeftClickBlock(ServerPlayerGameMode serverPlayerGameMode, BlockPos blockPos, ServerboundPlayerActionPacket.Action action, Direction direction, int worldHeight) {
|
|
+
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..999b39127a42c2ccec0be4110c93453d38bda397
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
|
@@ -0,0 +1,659 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+import com.destroystokyo.paper.PaperWorldConfig;
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.core.Direction;
|
|
+import net.minecraft.core.Registry;
|
|
+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
|
+import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.server.level.ServerPlayerGameMode;
|
|
+import net.minecraft.world.level.ChunkPos;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+import net.minecraft.world.level.block.EntityBlock;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.chunk.*;
|
|
+import org.bukkit.Bukkit;
|
|
+
|
|
+import java.util.*;
|
|
+import java.util.concurrent.Executor;
|
|
+import java.util.concurrent.ThreadLocalRandom;
|
|
+import java.util.function.IntSupplier;
|
|
+
|
|
+public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController {
|
|
+
|
|
+ private static final Palette<BlockState> GLOBAL_BLOCKSTATE_PALETTE = new GlobalPalette<>(Block.BLOCK_STATE_REGISTRY);
|
|
+ private static final LevelChunkSection EMPTY_SECTION = null;
|
|
+ private final Executor executor;
|
|
+ private final EngineMode engineMode;
|
|
+ private final int maxBlockHeight;
|
|
+ private final int updateRadius;
|
|
+ private final boolean usePermission;
|
|
+ private final BlockState[] presetBlockStates;
|
|
+ private final BlockState[] presetBlockStatesFull;
|
|
+ private final BlockState[] presetBlockStatesStone;
|
|
+ private final BlockState[] presetBlockStatesNetherrack;
|
|
+ private final BlockState[] presetBlockStatesEndStone;
|
|
+ private final int[] presetBlockStateBitsGlobal;
|
|
+ private final int[] presetBlockStateBitsStoneGlobal;
|
|
+ private final int[] presetBlockStateBitsNetherrackGlobal;
|
|
+ private final int[] presetBlockStateBitsEndStoneGlobal;
|
|
+ private final boolean[] solidGlobal = new boolean[Block.BLOCK_STATE_REGISTRY.size()];
|
|
+ private final boolean[] obfuscateGlobal = new boolean[Block.BLOCK_STATE_REGISTRY.size()];
|
|
+ private final LevelChunkSection[] emptyNearbyChunkSections = {EMPTY_SECTION, EMPTY_SECTION, EMPTY_SECTION, EMPTY_SECTION};
|
|
+ private final int maxBlockHeightUpdatePosition;
|
|
+
|
|
+ public ChunkPacketBlockControllerAntiXray(Level level, Executor executor) {
|
|
+ this.executor = executor;
|
|
+ PaperWorldConfig paperWorldConfig = level.paperConfig;
|
|
+ engineMode = paperWorldConfig.engineMode;
|
|
+ maxBlockHeight = paperWorldConfig.maxBlockHeight >> 4 << 4;
|
|
+ updateRadius = paperWorldConfig.updateRadius;
|
|
+ usePermission = paperWorldConfig.usePermission;
|
|
+ List<String> toObfuscate;
|
|
+
|
|
+ if (engineMode == EngineMode.HIDE) {
|
|
+ toObfuscate = paperWorldConfig.hiddenBlocks;
|
|
+ presetBlockStates = null;
|
|
+ presetBlockStatesFull = null;
|
|
+ presetBlockStatesStone = new BlockState[]{Blocks.STONE.defaultBlockState()};
|
|
+ presetBlockStatesNetherrack = new BlockState[]{Blocks.NETHERRACK.defaultBlockState()};
|
|
+ presetBlockStatesEndStone = new BlockState[]{Blocks.END_STONE.defaultBlockState()};
|
|
+ presetBlockStateBitsGlobal = null;
|
|
+ presetBlockStateBitsStoneGlobal = new int[]{GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.STONE.defaultBlockState())};
|
|
+ presetBlockStateBitsNetherrackGlobal = new int[]{GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.NETHERRACK.defaultBlockState())};
|
|
+ presetBlockStateBitsEndStoneGlobal = new int[]{GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.END_STONE.defaultBlockState())};
|
|
+ } else {
|
|
+ toObfuscate = new ArrayList<>(paperWorldConfig.replacementBlocks);
|
|
+ List<BlockState> presetBlockStateList = new LinkedList<>();
|
|
+
|
|
+ for (String id : paperWorldConfig.hiddenBlocks) {
|
|
+ Block block = Registry.BLOCK.getOptional(new ResourceLocation(id)).orElse(null);
|
|
+
|
|
+ if (block != null && !(block instanceof EntityBlock)) {
|
|
+ toObfuscate.add(id);
|
|
+ presetBlockStateList.add(block.defaultBlockState());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // The doc of the LinkedHashSet(Collection<? extends E>) constructor doesn't specify that the insertion order is the predictable iteration order of the specified Collection, although it is in the implementation
|
|
+ Set<BlockState> presetBlockStateSet = new LinkedHashSet<>();
|
|
+ // Therefore addAll(Collection<? extends E>) is used, which guarantees this order in the doc
|
|
+ presetBlockStateSet.addAll(presetBlockStateList);
|
|
+ presetBlockStates = presetBlockStateSet.isEmpty() ? new BlockState[]{Blocks.DIAMOND_ORE.defaultBlockState()} : presetBlockStateSet.toArray(new BlockState[0]);
|
|
+ presetBlockStatesFull = presetBlockStateSet.isEmpty() ? new BlockState[]{Blocks.DIAMOND_ORE.defaultBlockState()} : presetBlockStateList.toArray(new BlockState[0]);
|
|
+ presetBlockStatesStone = null;
|
|
+ presetBlockStatesNetherrack = null;
|
|
+ presetBlockStatesEndStone = null;
|
|
+ presetBlockStateBitsGlobal = new int[presetBlockStatesFull.length];
|
|
+
|
|
+ for (int i = 0; i < presetBlockStatesFull.length; i++) {
|
|
+ presetBlockStateBitsGlobal[i] = GLOBAL_BLOCKSTATE_PALETTE.idFor(presetBlockStatesFull[i]);
|
|
+ }
|
|
+
|
|
+ presetBlockStateBitsStoneGlobal = null;
|
|
+ presetBlockStateBitsNetherrackGlobal = null;
|
|
+ presetBlockStateBitsEndStoneGlobal = null;
|
|
+ }
|
|
+
|
|
+ for (String id : toObfuscate) {
|
|
+ Block block = Registry.BLOCK.getOptional(new ResourceLocation(id)).orElse(null);
|
|
+
|
|
+ // Don't obfuscate air because air causes unnecessary block updates and causes block updates to fail in the void
|
|
+ if (block != null && !block.defaultBlockState().isAir()) {
|
|
+ // Replace all block states of a specified block
|
|
+ for (BlockState blockState : block.getStateDefinition().getPossibleStates()) {
|
|
+ obfuscateGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)] = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ EmptyLevelChunk emptyChunk = new EmptyLevelChunk(level, new ChunkPos(0, 0));
|
|
+ BlockPos zeroPos = new BlockPos(0, 0, 0);
|
|
+
|
|
+ for (int i = 0; i < solidGlobal.length; i++) {
|
|
+ BlockState blockState = GLOBAL_BLOCKSTATE_PALETTE.valueFor(i);
|
|
+
|
|
+ if (blockState != null) {
|
|
+ solidGlobal[i] = blockState.isRedstoneConductor(emptyChunk, zeroPos)
|
|
+ && blockState.getBlock() != Blocks.SPAWNER && blockState.getBlock() != Blocks.BARRIER && blockState.getBlock() != Blocks.SHULKER_BOX && blockState.getBlock() != Blocks.SLIME_BLOCK || paperWorldConfig.lavaObscures && blockState == Blocks.LAVA.defaultBlockState();
|
|
+ // Comparing blockState == Blocks.LAVA.defaultBlockState() instead of blockState.getBlock() == Blocks.LAVA ensures that only "stationary lava" is used
|
|
+ // shulker box checks TE.
|
|
+ }
|
|
+ }
|
|
+
|
|
+ maxBlockHeightUpdatePosition = maxBlockHeight + updateRadius - 1;
|
|
+ }
|
|
+
|
|
+ private int getPresetBlockStatesFullLength() {
|
|
+ return engineMode == EngineMode.HIDE ? 1 : presetBlockStatesFull.length;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BlockState[] getPresetBlockStates(Level level, ChunkPos chunkPos, int bottomBlockY) {
|
|
+ // Return the block states to be added to the paletted containers so that they can be used for obfuscation
|
|
+ if (bottomBlockY < maxBlockHeight) {
|
|
+ if (engineMode == EngineMode.HIDE) {
|
|
+ return switch (level.getWorld().getEnvironment()) {
|
|
+ case NETHER -> presetBlockStatesNetherrack;
|
|
+ case THE_END -> presetBlockStatesEndStone;
|
|
+ default -> presetBlockStatesStone;
|
|
+ };
|
|
+ }
|
|
+
|
|
+ return presetBlockStates;
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean shouldModify(ServerPlayer player, LevelChunk chunk) {
|
|
+ return !usePermission || !player.getBukkitEntity().hasPermission("paper.antixray.bypass");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ChunkPacketInfoAntiXray getChunkPacketInfo(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk) {
|
|
+ // Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later
|
|
+ return new ChunkPacketInfoAntiXray(chunkPacket, chunk, this);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void modifyBlocks(ClientboundLevelChunkWithLightPacket chunkPacket, ChunkPacketInfo<BlockState> chunkPacketInfo) {
|
|
+ if (!(chunkPacketInfo instanceof ChunkPacketInfoAntiXray)) {
|
|
+ chunkPacket.setReady(true);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!Bukkit.isPrimaryThread()) {
|
|
+ // Plugins?
|
|
+ MinecraftServer.getServer().scheduleOnMain(() -> modifyBlocks(chunkPacket, chunkPacketInfo));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ LevelChunk chunk = chunkPacketInfo.getChunk();
|
|
+ int x = chunk.getPos().x;
|
|
+ int z = chunk.getPos().z;
|
|
+ Level level = chunk.getLevel();
|
|
+ ((ChunkPacketInfoAntiXray) chunkPacketInfo).setNearbyChunks(level.getChunkIfLoaded(x - 1, z), level.getChunkIfLoaded(x + 1, z), level.getChunkIfLoaded(x, z - 1), level.getChunkIfLoaded(x, z + 1));
|
|
+ executor.execute((Runnable) chunkPacketInfo);
|
|
+ }
|
|
+
|
|
+ // Actually these fields should be variables inside the obfuscate method but in sync mode or with SingleThreadExecutor in async mode it's okay (even without ThreadLocal)
|
|
+ // If an ExecutorService with multiple threads is used, ThreadLocal must be used here
|
|
+ private final ThreadLocal<int[]> presetBlockStateBits = ThreadLocal.withInitial(() -> new int[getPresetBlockStatesFullLength()]);
|
|
+ private static final ThreadLocal<boolean[]> SOLID = ThreadLocal.withInitial(() -> new boolean[Block.BLOCK_STATE_REGISTRY.size()]);
|
|
+ private static final ThreadLocal<boolean[]> OBFUSCATE = ThreadLocal.withInitial(() -> new boolean[Block.BLOCK_STATE_REGISTRY.size()]);
|
|
+ // These boolean arrays represent chunk layers, true means don't obfuscate, false means obfuscate
|
|
+ private static final ThreadLocal<boolean[][]> CURRENT = ThreadLocal.withInitial(() -> new boolean[16][16]);
|
|
+ private static final ThreadLocal<boolean[][]> NEXT = ThreadLocal.withInitial(() -> new boolean[16][16]);
|
|
+ private static final ThreadLocal<boolean[][]> NEXT_NEXT = ThreadLocal.withInitial(() -> new boolean[16][16]);
|
|
+
|
|
+ public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) {
|
|
+ int[] presetBlockStateBits = this.presetBlockStateBits.get();
|
|
+ boolean[] solid = SOLID.get();
|
|
+ boolean[] obfuscate = OBFUSCATE.get();
|
|
+ boolean[][] current = CURRENT.get();
|
|
+ boolean[][] next = NEXT.get();
|
|
+ boolean[][] nextNext = NEXT_NEXT.get();
|
|
+ // bitStorageReader, bitStorageWriter and nearbyChunkSections could also be reused (with ThreadLocal if necessary) but it's not worth it
|
|
+ BitStorageReader bitStorageReader = new BitStorageReader();
|
|
+ BitStorageWriter bitStorageWriter = new BitStorageWriter();
|
|
+ LevelChunkSection[] nearbyChunkSections = new LevelChunkSection[4];
|
|
+ LevelChunk chunk = chunkPacketInfoAntiXray.getChunk();
|
|
+ Level level = chunk.getLevel();
|
|
+ int maxChunkSectionIndex = Math.min((maxBlockHeight >> 4) - chunk.getMinSection(), chunk.getSectionsCount() - 1);
|
|
+ boolean[] solidTemp = null;
|
|
+ boolean[] obfuscateTemp = null;
|
|
+ bitStorageReader.setBuffer(chunkPacketInfoAntiXray.getBuffer());
|
|
+ bitStorageWriter.setBuffer(chunkPacketInfoAntiXray.getBuffer());
|
|
+ int numberOfBlocks = presetBlockStateBits.length;
|
|
+ // Keep the lambda expressions as simple as possible. They are used very frequently.
|
|
+ IntSupplier random = numberOfBlocks == 1 ? (() -> 0) : new IntSupplier() {
|
|
+ private int state;
|
|
+
|
|
+ {
|
|
+ while ((state = ThreadLocalRandom.current().nextInt()) == 0) ;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getAsInt() {
|
|
+ // https://en.wikipedia.org/wiki/Xorshift
|
|
+ state ^= state << 13;
|
|
+ state ^= state >>> 17;
|
|
+ state ^= state << 5;
|
|
+ // https://www.pcg-random.org/posts/bounded-rands.html
|
|
+ return (int) ((Integer.toUnsignedLong(state) * numberOfBlocks) >>> 32);
|
|
+ }
|
|
+ };
|
|
+
|
|
+ for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) {
|
|
+ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex) != null) {
|
|
+ int[] presetBlockStateBitsTemp;
|
|
+
|
|
+ if (chunkPacketInfoAntiXray.getPalette(chunkSectionIndex) instanceof GlobalPalette) {
|
|
+ if (engineMode == EngineMode.HIDE) {
|
|
+ presetBlockStateBitsTemp = switch (level.getWorld().getEnvironment()) {
|
|
+ case NETHER -> presetBlockStateBitsNetherrackGlobal;
|
|
+ case THE_END -> presetBlockStateBitsEndStoneGlobal;
|
|
+ default -> presetBlockStateBitsStoneGlobal;
|
|
+ };
|
|
+ } else {
|
|
+ presetBlockStateBitsTemp = presetBlockStateBitsGlobal;
|
|
+ }
|
|
+ } else {
|
|
+ // If it's presetBlockStates, use this.presetBlockStatesFull instead
|
|
+ BlockState[] presetBlockStatesFull = chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex) == presetBlockStates ? this.presetBlockStatesFull : chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex);
|
|
+ presetBlockStateBitsTemp = presetBlockStateBits;
|
|
+
|
|
+ for (int i = 0; i < presetBlockStateBitsTemp.length; i++) {
|
|
+ // This is thread safe because we only request IDs that are guaranteed to be in the palette and are visible
|
|
+ // For more details see the comments in the readPalette method
|
|
+ presetBlockStateBitsTemp[i] = chunkPacketInfoAntiXray.getPalette(chunkSectionIndex).idFor(presetBlockStatesFull[i]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ bitStorageWriter.setIndex(chunkPacketInfoAntiXray.getIndex(chunkSectionIndex));
|
|
+
|
|
+ // Check if the chunk section below was not obfuscated
|
|
+ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex - 1) == null) {
|
|
+ // If so, initialize some stuff
|
|
+ bitStorageReader.setBits(chunkPacketInfoAntiXray.getBits(chunkSectionIndex));
|
|
+ bitStorageReader.setIndex(chunkPacketInfoAntiXray.getIndex(chunkSectionIndex));
|
|
+ solidTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex), solid, solidGlobal);
|
|
+ obfuscateTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex), obfuscate, obfuscateGlobal);
|
|
+ // Read the blocks of the upper layer of the chunk section below if it exists
|
|
+ LevelChunkSection belowChunkSection = null;
|
|
+ boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunk.getSections()[chunkSectionIndex - 1]) == EMPTY_SECTION;
|
|
+
|
|
+ for (int z = 0; z < 16; z++) {
|
|
+ for (int x = 0; x < 16; x++) {
|
|
+ current[z][x] = true;
|
|
+ next[z][x] = skipFirstLayer || isTransparent(belowChunkSection, x, 15, z);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Abuse the obfuscateLayer method to read the blocks of the first layer of the current chunk section
|
|
+ bitStorageWriter.setBits(0);
|
|
+ obfuscateLayer(-1, bitStorageReader, bitStorageWriter, solidTemp, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, emptyNearbyChunkSections, random);
|
|
+ }
|
|
+
|
|
+ bitStorageWriter.setBits(chunkPacketInfoAntiXray.getBits(chunkSectionIndex));
|
|
+ nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex];
|
|
+ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex];
|
|
+ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex];
|
|
+ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? EMPTY_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;
|
|
+ obfuscateLayer(y, bitStorageReader, bitStorageWriter, solidTemp, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, nearbyChunkSections, random);
|
|
+ }
|
|
+
|
|
+ // Check if the chunk section above doesn't need obfuscation
|
|
+ if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex + 1) == null) {
|
|
+ // If so, obfuscate the upper layer of the current chunk section by reading blocks of the first layer from the chunk section above if it exists
|
|
+ LevelChunkSection aboveChunkSection;
|
|
+
|
|
+ if (chunkSectionIndex != chunk.getSectionsCount() - 1 && (aboveChunkSection = chunk.getSections()[chunkSectionIndex + 1]) != EMPTY_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 (isTransparent(aboveChunkSection, x, 0, z)) {
|
|
+ current[z][x] = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // There is nothing to read anymore
|
|
+ bitStorageReader.setBits(0);
|
|
+ solid[0] = true;
|
|
+ obfuscateLayer(15, bitStorageReader, bitStorageWriter, solid, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, nearbyChunkSections, random);
|
|
+ }
|
|
+ } else {
|
|
+ // If not, initialize the reader and other stuff for the chunk section above to obfuscate the upper layer of the current chunk section
|
|
+ bitStorageReader.setBits(chunkPacketInfoAntiXray.getBits(chunkSectionIndex + 1));
|
|
+ bitStorageReader.setIndex(chunkPacketInfoAntiXray.getIndex(chunkSectionIndex + 1));
|
|
+ solidTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex + 1), solid, solidGlobal);
|
|
+ obfuscateTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal);
|
|
+ boolean[][] temp = current;
|
|
+ current = next;
|
|
+ next = nextNext;
|
|
+ nextNext = temp;
|
|
+ obfuscateLayer(15, bitStorageReader, bitStorageWriter, solidTemp, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, nearbyChunkSections, random);
|
|
+ }
|
|
+
|
|
+ bitStorageWriter.flush();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ chunkPacketInfoAntiXray.getChunkPacket().setReady(true);
|
|
+ }
|
|
+
|
|
+ private void obfuscateLayer(int y, BitStorageReader bitStorageReader, BitStorageWriter bitStorageWriter, boolean[] solid, boolean[] obfuscate, int[] presetBlockStateBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, LevelChunkSection[] nearbyChunkSections, IntSupplier random) {
|
|
+ // First block of first line
|
|
+ int bits = bitStorageReader.read();
|
|
+
|
|
+ if (nextNext[0][0] = !solid[bits]) {
|
|
+ bitStorageWriter.skip();
|
|
+ next[0][1] = true;
|
|
+ next[1][0] = true;
|
|
+ } else {
|
|
+ if (current[0][0] || isTransparent(nearbyChunkSections[2], 0, y, 15) || isTransparent(nearbyChunkSections[0], 15, y, 0)) {
|
|
+ bitStorageWriter.skip();
|
|
+ } else {
|
|
+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!obfuscate[bits]) {
|
|
+ next[0][0] = true;
|
|
+ }
|
|
+
|
|
+ // First line
|
|
+ for (int x = 1; x < 15; x++) {
|
|
+ bits = bitStorageReader.read();
|
|
+
|
|
+ if (nextNext[0][x] = !solid[bits]) {
|
|
+ bitStorageWriter.skip();
|
|
+ next[0][x - 1] = true;
|
|
+ next[0][x + 1] = true;
|
|
+ next[1][x] = true;
|
|
+ } else {
|
|
+ if (current[0][x] || isTransparent(nearbyChunkSections[2], x, y, 15)) {
|
|
+ bitStorageWriter.skip();
|
|
+ } else {
|
|
+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!obfuscate[bits]) {
|
|
+ next[0][x] = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Last block of first line
|
|
+ bits = bitStorageReader.read();
|
|
+
|
|
+ if (nextNext[0][15] = !solid[bits]) {
|
|
+ bitStorageWriter.skip();
|
|
+ next[0][14] = true;
|
|
+ next[1][15] = true;
|
|
+ } else {
|
|
+ if (current[0][15] || isTransparent(nearbyChunkSections[2], 15, y, 15) || isTransparent(nearbyChunkSections[1], 0, y, 0)) {
|
|
+ bitStorageWriter.skip();
|
|
+ } else {
|
|
+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!obfuscate[bits]) {
|
|
+ next[0][15] = true;
|
|
+ }
|
|
+
|
|
+ // All inner lines
|
|
+ for (int z = 1; z < 15; z++) {
|
|
+ // First block
|
|
+ bits = bitStorageReader.read();
|
|
+
|
|
+ if (nextNext[z][0] = !solid[bits]) {
|
|
+ bitStorageWriter.skip();
|
|
+ next[z][1] = true;
|
|
+ next[z - 1][0] = true;
|
|
+ next[z + 1][0] = true;
|
|
+ } else {
|
|
+ if (current[z][0] || isTransparent(nearbyChunkSections[0], 15, y, z)) {
|
|
+ bitStorageWriter.skip();
|
|
+ } else {
|
|
+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!obfuscate[bits]) {
|
|
+ next[z][0] = true;
|
|
+ }
|
|
+
|
|
+ // All inner blocks
|
|
+ for (int x = 1; x < 15; x++) {
|
|
+ bits = bitStorageReader.read();
|
|
+
|
|
+ if (nextNext[z][x] = !solid[bits]) {
|
|
+ bitStorageWriter.skip();
|
|
+ next[z][x - 1] = true;
|
|
+ next[z][x + 1] = true;
|
|
+ next[z - 1][x] = true;
|
|
+ next[z + 1][x] = true;
|
|
+ } else {
|
|
+ if (current[z][x]) {
|
|
+ bitStorageWriter.skip();
|
|
+ } else {
|
|
+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!obfuscate[bits]) {
|
|
+ next[z][x] = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Last block
|
|
+ bits = bitStorageReader.read();
|
|
+
|
|
+ if (nextNext[z][15] = !solid[bits]) {
|
|
+ bitStorageWriter.skip();
|
|
+ next[z][14] = true;
|
|
+ next[z - 1][15] = true;
|
|
+ next[z + 1][15] = true;
|
|
+ } else {
|
|
+ if (current[z][15] || isTransparent(nearbyChunkSections[1], 0, y, z)) {
|
|
+ bitStorageWriter.skip();
|
|
+ } else {
|
|
+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!obfuscate[bits]) {
|
|
+ next[z][15] = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // First block of last line
|
|
+ bits = bitStorageReader.read();
|
|
+
|
|
+ if (nextNext[15][0] = !solid[bits]) {
|
|
+ bitStorageWriter.skip();
|
|
+ next[15][1] = true;
|
|
+ next[14][0] = true;
|
|
+ } else {
|
|
+ if (current[15][0] || isTransparent(nearbyChunkSections[3], 0, y, 0) || isTransparent(nearbyChunkSections[0], 15, y, 15)) {
|
|
+ bitStorageWriter.skip();
|
|
+ } else {
|
|
+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!obfuscate[bits]) {
|
|
+ next[15][0] = true;
|
|
+ }
|
|
+
|
|
+ // Last line
|
|
+ for (int x = 1; x < 15; x++) {
|
|
+ bits = bitStorageReader.read();
|
|
+
|
|
+ if (nextNext[15][x] = !solid[bits]) {
|
|
+ bitStorageWriter.skip();
|
|
+ next[15][x - 1] = true;
|
|
+ next[15][x + 1] = true;
|
|
+ next[14][x] = true;
|
|
+ } else {
|
|
+ if (current[15][x] || isTransparent(nearbyChunkSections[3], x, y, 0)) {
|
|
+ bitStorageWriter.skip();
|
|
+ } else {
|
|
+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!obfuscate[bits]) {
|
|
+ next[15][x] = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Last block of last line
|
|
+ bits = bitStorageReader.read();
|
|
+
|
|
+ if (nextNext[15][15] = !solid[bits]) {
|
|
+ bitStorageWriter.skip();
|
|
+ next[15][14] = true;
|
|
+ next[14][15] = true;
|
|
+ } else {
|
|
+ if (current[15][15] || isTransparent(nearbyChunkSections[3], 15, y, 0) || isTransparent(nearbyChunkSections[1], 0, y, 15)) {
|
|
+ bitStorageWriter.skip();
|
|
+ } else {
|
|
+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!obfuscate[bits]) {
|
|
+ next[15][15] = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private boolean isTransparent(LevelChunkSection chunkSection, int x, int y, int z) {
|
|
+ if (chunkSection == EMPTY_SECTION) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ return !solidGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(chunkSection.getBlockState(x, y, z))];
|
|
+ } catch (MissingPaletteEntryException e) {
|
|
+ // Race condition / visibility issue / no happens-before relationship
|
|
+ // We don't care and treat the block as transparent
|
|
+ // Internal implementation details of PalettedContainer, LinearPalette, HashMapPalette, CrudeIncrementalIntIdentityHashBiMap, ... guarantee us that no (other) exceptions will occur
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private boolean[] readPalette(Palette<BlockState> palette, boolean[] temp, boolean[] global) {
|
|
+ if (palette instanceof GlobalPalette) {
|
|
+ return global;
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ for (int i = 0; i < palette.getSize(); i++) {
|
|
+ temp[i] = global[GLOBAL_BLOCKSTATE_PALETTE.idFor(palette.valueFor(i))];
|
|
+ }
|
|
+ } catch (MissingPaletteEntryException e) {
|
|
+ // Race condition / visibility issue / no happens-before relationship
|
|
+ // We don't care because we at least see the state as it was when the chunk packet was created
|
|
+ // Internal implementation details of PalettedContainer, LinearPalette, HashMapPalette, CrudeIncrementalIntIdentityHashBiMap, ... guarantee us that no (other) exceptions will occur until we have all the data that we need here
|
|
+ // Since all palettes have a fixed initial maximum size and there is no internal restructuring and no values are removed from palettes, we are also guaranteed to see the data
|
|
+ }
|
|
+
|
|
+ return temp;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onBlockChange(Level level, BlockPos blockPos, BlockState newBlockState, BlockState oldBlockState, int flags, int maxUpdateDepth) {
|
|
+ if (oldBlockState != null && solidGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(oldBlockState)] && !solidGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(newBlockState)] && blockPos.getY() <= maxBlockHeightUpdatePosition) {
|
|
+ updateNearbyBlocks(level, blockPos);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onPlayerLeftClickBlock(ServerPlayerGameMode serverPlayerGameMode, BlockPos blockPos, ServerboundPlayerActionPacket.Action action, Direction direction, int worldHeight) {
|
|
+ if (blockPos.getY() <= maxBlockHeightUpdatePosition) {
|
|
+ updateNearbyBlocks(serverPlayerGameMode.level, blockPos);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void updateNearbyBlocks(Level level, BlockPos blockPos) {
|
|
+ if (updateRadius >= 2) {
|
|
+ BlockPos temp = blockPos.west();
|
|
+ updateBlock(level, temp);
|
|
+ updateBlock(level, temp.west());
|
|
+ updateBlock(level, temp.below());
|
|
+ updateBlock(level, temp.above());
|
|
+ updateBlock(level, temp.north());
|
|
+ updateBlock(level, temp.south());
|
|
+ updateBlock(level, temp = blockPos.east());
|
|
+ updateBlock(level, temp.east());
|
|
+ updateBlock(level, temp.below());
|
|
+ updateBlock(level, temp.above());
|
|
+ updateBlock(level, temp.north());
|
|
+ updateBlock(level, temp.south());
|
|
+ updateBlock(level, temp = blockPos.below());
|
|
+ updateBlock(level, temp.below());
|
|
+ updateBlock(level, temp.north());
|
|
+ updateBlock(level, temp.south());
|
|
+ updateBlock(level, temp = blockPos.above());
|
|
+ updateBlock(level, temp.above());
|
|
+ updateBlock(level, temp.north());
|
|
+ updateBlock(level, temp.south());
|
|
+ updateBlock(level, temp = blockPos.north());
|
|
+ updateBlock(level, temp.north());
|
|
+ updateBlock(level, temp = blockPos.south());
|
|
+ updateBlock(level, temp.south());
|
|
+ } else if (updateRadius == 1) {
|
|
+ updateBlock(level, blockPos.west());
|
|
+ updateBlock(level, blockPos.east());
|
|
+ updateBlock(level, blockPos.below());
|
|
+ updateBlock(level, blockPos.above());
|
|
+ updateBlock(level, blockPos.north());
|
|
+ updateBlock(level, blockPos.south());
|
|
+ } else {
|
|
+ // Do nothing if updateRadius <= 0 (test mode)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void updateBlock(Level level, BlockPos blockPos) {
|
|
+ BlockState blockState = level.getTypeIfLoaded(blockPos);
|
|
+
|
|
+ if (blockState != null && obfuscateGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)]) {
|
|
+ ((ServerLevel) level).getChunkSource().blockChanged(blockPos);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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 0000000000000000000000000000000000000000..d98a3f5c54c67a673eb7dc456dd039cd78f9c34d
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java
|
|
@@ -0,0 +1,80 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
|
+import net.minecraft.world.level.chunk.LevelChunk;
|
|
+import net.minecraft.world.level.chunk.Palette;
|
|
+
|
|
+public class ChunkPacketInfo<T> {
|
|
+
|
|
+ private final ClientboundLevelChunkWithLightPacket chunkPacket;
|
|
+ private final LevelChunk chunk;
|
|
+ private final int[] bits;
|
|
+ private final Object[] palettes;
|
|
+ private final int[] indexes;
|
|
+ private final Object[][] presetValues;
|
|
+ private byte[] buffer;
|
|
+
|
|
+ public ChunkPacketInfo(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk) {
|
|
+ this.chunkPacket = chunkPacket;
|
|
+ this.chunk = chunk;
|
|
+ int sections = chunk.getSectionsCount();
|
|
+ bits = new int[sections];
|
|
+ palettes = new Object[sections];
|
|
+ indexes = new int[sections];
|
|
+ presetValues = new Object[sections][];
|
|
+ }
|
|
+
|
|
+ public ClientboundLevelChunkWithLightPacket getChunkPacket() {
|
|
+ return chunkPacket;
|
|
+ }
|
|
+
|
|
+ public LevelChunk getChunk() {
|
|
+ return chunk;
|
|
+ }
|
|
+
|
|
+ public byte[] getBuffer() {
|
|
+ return buffer;
|
|
+ }
|
|
+
|
|
+ public void setBuffer(byte[] buffer) {
|
|
+ this.buffer = buffer;
|
|
+ }
|
|
+
|
|
+ public int getBits(int chunkSectionIndex) {
|
|
+ return bits[chunkSectionIndex];
|
|
+ }
|
|
+
|
|
+ public void setBits(int chunkSectionIndex, int bits) {
|
|
+ this.bits[chunkSectionIndex] = bits;
|
|
+ }
|
|
+
|
|
+ @SuppressWarnings("unchecked")
|
|
+ public Palette<T> getPalette(int chunkSectionIndex) {
|
|
+ return (Palette<T>) palettes[chunkSectionIndex];
|
|
+ }
|
|
+
|
|
+ public void setPalette(int chunkSectionIndex, Palette<T> palette) {
|
|
+ palettes[chunkSectionIndex] = palette;
|
|
+ }
|
|
+
|
|
+ public int getIndex(int chunkSectionIndex) {
|
|
+ return indexes[chunkSectionIndex];
|
|
+ }
|
|
+
|
|
+ public void setIndex(int chunkSectionIndex, int index) {
|
|
+ indexes[chunkSectionIndex] = index;
|
|
+ }
|
|
+
|
|
+ @SuppressWarnings("unchecked")
|
|
+ public T[] getPresetValues(int chunkSectionIndex) {
|
|
+ return (T[]) presetValues[chunkSectionIndex];
|
|
+ }
|
|
+
|
|
+ public void setPresetValues(int chunkSectionIndex, T[] presetValues) {
|
|
+ this.presetValues[chunkSectionIndex] = presetValues;
|
|
+ }
|
|
+
|
|
+ public boolean isWritten(int chunkSectionIndex) {
|
|
+ return bits[chunkSectionIndex] != 0;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..80a2dfb266ae1221680a7b24fee2f7e2a8330b7d
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java
|
|
@@ -0,0 +1,29 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.chunk.LevelChunk;
|
|
+
|
|
+public final class ChunkPacketInfoAntiXray extends ChunkPacketInfo<BlockState> implements Runnable {
|
|
+
|
|
+ private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray;
|
|
+ private LevelChunk[] nearbyChunks;
|
|
+
|
|
+ public ChunkPacketInfoAntiXray(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) {
|
|
+ super(chunkPacket, chunk);
|
|
+ this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray;
|
|
+ }
|
|
+
|
|
+ public LevelChunk[] getNearbyChunks() {
|
|
+ return nearbyChunks;
|
|
+ }
|
|
+
|
|
+ public void setNearbyChunks(LevelChunk... nearbyChunks) {
|
|
+ this.nearbyChunks = nearbyChunks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ chunkPacketBlockControllerAntiXray.obfuscate(this);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
|
index dba11f277f3703e1ee7f5a62f021d319e4ab18fc..0e75764c108c24b3e2c453f2b4f14e798add0eb4 100644
|
|
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
|
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
|
@@ -33,7 +33,10 @@ public class ClientboundLevelChunkPacketData {
|
|
}
|
|
// Paper end
|
|
|
|
- public ClientboundLevelChunkPacketData(LevelChunk chunk) {
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ @Deprecated public ClientboundLevelChunkPacketData(LevelChunk chunk) { this(chunk, null); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public ClientboundLevelChunkPacketData(LevelChunk chunk, com.destroystokyo.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
|
|
+ // Paper end
|
|
this.heightmaps = new CompoundTag();
|
|
|
|
for(Entry<Heightmap.Types, Heightmap> entry : chunk.getHeightmaps()) {
|
|
@@ -43,7 +46,13 @@ public class ClientboundLevelChunkPacketData {
|
|
}
|
|
|
|
this.buffer = new byte[calculateChunkSize(chunk)];
|
|
- extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk);
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ if (chunkPacketInfo != null) {
|
|
+ chunkPacketInfo.setBuffer(this.buffer);
|
|
+ }
|
|
+
|
|
+ extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk, chunkPacketInfo);
|
|
+ // Paper end
|
|
this.blockEntitiesData = Lists.newArrayList();
|
|
int totalTileEntities = 0; // Paper
|
|
|
|
@@ -103,9 +112,12 @@ public class ClientboundLevelChunkPacketData {
|
|
return byteBuf;
|
|
}
|
|
|
|
- public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) {
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ @Deprecated public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) { ClientboundLevelChunkPacketData.extractChunkData(buf, chunk, null); } // Notice for updates: Please make sure this function isn't used anywhere
|
|
+ public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk, com.destroystokyo.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
|
|
for(LevelChunkSection levelChunkSection : chunk.getSections()) {
|
|
- levelChunkSection.write(buf);
|
|
+ levelChunkSection.write(buf, chunkPacketInfo);
|
|
+ // Paper end
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
|
index 7825d6f0fdcfda6212cff8033ec55fb7db236154..2072aa8710f6e285f7c8f76c63b7bcf85cc11030 100644
|
|
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
|
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
|
@@ -13,13 +13,30 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
|
|
private final int z;
|
|
private final ClientboundLevelChunkPacketData chunkData;
|
|
private final ClientboundLightUpdatePacketData lightData;
|
|
+ // Paper start - Async-Anti-Xray - Ready flag for the connection
|
|
+ private volatile boolean ready;
|
|
|
|
- public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightProvider, @Nullable BitSet skyBits, @Nullable BitSet blockBits, boolean nonEdge) {
|
|
+ @Override
|
|
+ public boolean isReady() {
|
|
+ return this.ready;
|
|
+ }
|
|
+
|
|
+ public void setReady(boolean ready) {
|
|
+ this.ready = ready;
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ @Deprecated public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightProvider, @Nullable BitSet skyBits, @Nullable BitSet blockBits, boolean nonEdge) { this(chunk, lightProvider, skyBits, blockBits, nonEdge, true); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightProvider, @Nullable BitSet skyBits, @Nullable BitSet blockBits, boolean nonEdge, boolean modifyBlocks) {
|
|
+ com.destroystokyo.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo = modifyBlocks ? chunk.getLevel().chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null;
|
|
ChunkPos chunkPos = chunk.getPos();
|
|
this.x = chunkPos.x;
|
|
this.z = chunkPos.z;
|
|
- this.chunkData = new ClientboundLevelChunkPacketData(chunk);
|
|
+ this.chunkData = new ClientboundLevelChunkPacketData(chunk, chunkPacketInfo);
|
|
+ // Paper end
|
|
this.lightData = new ClientboundLightUpdatePacketData(chunkPos, lightProvider, skyBits, blockBits, nonEdge);
|
|
+ chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
|
|
}
|
|
|
|
public ClientboundLevelChunkWithLightPacket(FriendlyByteBuf buf) {
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index fee7c426876ecfc1e6a55afcd4f7c7a503d02902..a3806b29907468f690e386d9147ae18f71f688ef 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -976,7 +976,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
completablefuture1.thenAcceptAsync((either) -> {
|
|
either.ifLeft((chunk) -> {
|
|
this.tickingGenerated.getAndIncrement();
|
|
- MutableObject<ClientboundLevelChunkWithLightPacket> mutableobject = new MutableObject();
|
|
+ MutableObject<java.util.Map<Object, ClientboundLevelChunkWithLightPacket>> mutableobject = new MutableObject<>(); // Paper - Anti-Xray - Bypass
|
|
|
|
this.getPlayers(chunkcoordintpair, false).forEach((entityplayer) -> {
|
|
this.playerLoadedChunk(entityplayer, mutableobject, chunk);
|
|
@@ -1143,7 +1143,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
while (objectiterator.hasNext()) {
|
|
ChunkHolder playerchunk = (ChunkHolder) objectiterator.next();
|
|
ChunkPos chunkcoordintpair = playerchunk.getPos();
|
|
- MutableObject<ClientboundLevelChunkWithLightPacket> mutableobject = new MutableObject();
|
|
+ MutableObject<java.util.Map<Object, ClientboundLevelChunkWithLightPacket>> mutableobject = new MutableObject<>(); // Paper - Anti-Xray - Bypass
|
|
|
|
this.getPlayers(chunkcoordintpair, false).forEach((entityplayer) -> {
|
|
boolean flag = ChunkMap.isChunkInEuclideanRange(chunkcoordintpair, entityplayer, true, k);
|
|
@@ -1156,7 +1156,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
|
|
}
|
|
|
|
- protected void updateChunkTracking(ServerPlayer player, ChunkPos pos, MutableObject<ClientboundLevelChunkWithLightPacket> mutableobject, boolean oldWithinViewDistance, boolean newWithinViewDistance) {
|
|
+ protected void updateChunkTracking(ServerPlayer player, ChunkPos pos, MutableObject<java.util.Map<Object, ClientboundLevelChunkWithLightPacket>> mutableobject, boolean oldWithinViewDistance, boolean newWithinViewDistance) { // Paper - Anti-Xray - Bypass
|
|
if (player.level == this.level) {
|
|
if (newWithinViewDistance && !oldWithinViewDistance) {
|
|
ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos.toLong());
|
|
@@ -1679,12 +1679,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
|
|
}
|
|
|
|
- private void playerLoadedChunk(ServerPlayer player, MutableObject<ClientboundLevelChunkWithLightPacket> cachedDataPacket, LevelChunk chunk) {
|
|
- if (cachedDataPacket.getValue() == null) {
|
|
- cachedDataPacket.setValue(new ClientboundLevelChunkWithLightPacket(chunk, this.lightEngine, (BitSet) null, (BitSet) null, true));
|
|
+ // Paper start - Anti-Xray - Bypass
|
|
+ private void playerLoadedChunk(ServerPlayer player, MutableObject<java.util.Map<Object, ClientboundLevelChunkWithLightPacket>> cachedDataPackets, LevelChunk chunk) {
|
|
+ if (cachedDataPackets.getValue() == null) {
|
|
+ cachedDataPackets.setValue(new java.util.HashMap<>());
|
|
}
|
|
|
|
- player.trackChunk(chunk.getPos(), (Packet) cachedDataPacket.getValue());
|
|
+ Boolean shouldModify = chunk.getLevel().chunkPacketBlockController.shouldModify(player, chunk);
|
|
+ player.trackChunk(chunk.getPos(), cachedDataPackets.getValue().computeIfAbsent(shouldModify, (s) -> {
|
|
+ return new ClientboundLevelChunkWithLightPacket(chunk, this.lightEngine, (BitSet) null, (BitSet) null, true, (Boolean) s);
|
|
+ }));
|
|
+ // Paper end
|
|
DebugPackets.sendPoiPacketsForChunk(this.level, chunk.getPos());
|
|
List<Entity> list = Lists.newArrayList();
|
|
List<Entity> list1 = Lists.newArrayList();
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
index db123ef36fc4cb0206349ac696572714b806454c..d26e42449a1f75898d06aba384178c0ef30d9aa7 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -391,7 +391,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
// Add env and gen to constructor, WorldData -> WorldDataServer
|
|
public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, ServerLevelData iworlddataserver, ResourceKey<Level> resourcekey, DimensionType dimensionmanager, ChunkProgressListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List<CustomSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
|
|
// Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error
|
|
- super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getProfiler, false, flag, i, gen, biomeProvider, env);
|
|
+ super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getProfiler, false, flag, i, gen, biomeProvider, env, executor); // Paper - Async-Anti-Xray - Pass executor
|
|
this.pvpMode = minecraftserver.isPvpAllowed();
|
|
this.convertable = convertable_conversionsession;
|
|
this.uuid = WorldUUID.getUUID(convertable_conversionsession.levelPath.toFile());
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
|
index 44e5ab0b545de41b937c7ce304ce643f78a43734..a85b89a0d525e623e154c8b1bea55470d2072dcb 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
|
@@ -48,7 +48,7 @@ import org.bukkit.event.player.PlayerInteractEvent;
|
|
public class ServerPlayerGameMode {
|
|
|
|
private static final Logger LOGGER = LogManager.getLogger();
|
|
- protected ServerLevel level;
|
|
+ public ServerLevel level; // Paper - Anti-Xray - protected -> public
|
|
protected final ServerPlayer player;
|
|
private GameType gameModeForPlayer;
|
|
@Nullable
|
|
@@ -314,6 +314,8 @@ public class ServerPlayerGameMode {
|
|
}
|
|
|
|
}
|
|
+
|
|
+ this.level.chunkPacketBlockController.onPlayerLeftClickBlock(this, pos, action, direction, worldHeight); // Paper - Anti-Xray
|
|
}
|
|
|
|
public void destroyAndAck(BlockPos pos, ServerboundPlayerActionPacket.Action action, String reason) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
|
index 77e58257dc3fb517aeca8f8436a4279aa133b570..8cde961332f4671bdd361e8f88d0eedd595bf745 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -167,6 +167,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot
|
|
|
|
public final com.destroystokyo.paper.PaperWorldConfig paperConfig; // Paper
|
|
+ public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
|
|
|
|
public final co.aikar.timings.WorldTimingsHandler timings; // Paper
|
|
public static BlockPos lastPhysicsProblem; // Spigot
|
|
@@ -186,7 +187,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
|
|
public abstract ResourceKey<LevelStem> getTypeKey();
|
|
|
|
- protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, final DimensionType dimensionmanager, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env) {
|
|
+ protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, final DimensionType dimensionmanager, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor
|
|
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
|
|
this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), this.spigotConfig); // Paper
|
|
this.generator = gen;
|
|
@@ -262,6 +263,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
this.keepSpawnInMemory = this.paperConfig.keepSpawnInMemory; // Paper
|
|
this.entityLimiter = new org.spigotmc.TickLimiter(spigotConfig.entityMaxTickTime);
|
|
this.tileLimiter = new org.spigotmc.TickLimiter(spigotConfig.tileMaxTickTime);
|
|
+ this.chunkPacketBlockController = this.paperConfig.antiXray ? new com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : com.destroystokyo.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray
|
|
}
|
|
|
|
// Paper start
|
|
@@ -442,6 +444,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
// CraftBukkit end
|
|
|
|
BlockState iblockdata1 = chunk.setBlockState(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag
|
|
+ this.chunkPacketBlockController.onBlockChange(this, pos, state, iblockdata1, flags, maxUpdateDepth); // Paper - Anti-Xray
|
|
|
|
if (iblockdata1 == null) {
|
|
// CraftBukkit start - remove blockstate if failed (or the same)
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
|
|
index 96dc50a24821016e53dfeac344c0e61f110a5a53..0a2f9e93666a5b2123f84e5cf409e4355d3cad86 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
|
|
@@ -110,7 +110,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
|
|
private static void replaceMissingSections(LevelHeightAccessor world, Registry<Biome> biome, LevelChunkSection[] sectionArray) {
|
|
for (int i = 0; i < sectionArray.length; ++i) {
|
|
if (sectionArray[i] == null) {
|
|
- sectionArray[i] = new LevelChunkSection(world.getSectionYFromSectionIndex(i), biome);
|
|
+ sectionArray[i] = new LevelChunkSection(world.getSectionYFromSectionIndex(i), biome, null, world instanceof net.minecraft.world.level.Level ? (net.minecraft.world.level.Level) world : null); // Paper - Anti-Xray - Add parameters
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
index a009122a8ea9dc2b04a178248f20ef01886e832b..0e89cf3fd6edf28b52ec113af31267642fd2b195 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
@@ -93,7 +93,7 @@ public class LevelChunk extends ChunkAccess {
|
|
}
|
|
|
|
public LevelChunk(Level world, ChunkPos pos, UpgradeData upgradeData, LevelChunkTicks<Block> blockTickScheduler, LevelChunkTicks<Fluid> fluidTickScheduler, long inhabitedTime, @Nullable LevelChunkSection[] sectionArrayInitializer, @Nullable LevelChunk.PostLoadProcessor entityLoader, @Nullable BlendingData blendingData) {
|
|
- super(pos, upgradeData, world, world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), inhabitedTime, sectionArrayInitializer, blendingData);
|
|
+ super(pos, upgradeData, world, net.minecraft.server.MinecraftServer.getServer().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), inhabitedTime, sectionArrayInitializer, blendingData); // Paper - Anti-Xray - The world isnt ready yet, use server singleton for registry
|
|
this.tickersInLevel = Maps.newHashMap();
|
|
this.clientLightReady = false;
|
|
this.level = (ServerLevel) world; // CraftBukkit - type
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
index 50b6ecfea7a342be0d21e37ae87777a4b4860026..512f53b24de14ea48eab85a0e725556d92def6e9 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
@@ -33,10 +33,13 @@ public class LevelChunkSection {
|
|
this.recalcBlockCounts();
|
|
}
|
|
|
|
- public LevelChunkSection(int chunkPos, Registry<Biome> biomeRegistry) {
|
|
+ // Paper start - Anti-Xray - Add parameters
|
|
+ @Deprecated public LevelChunkSection(int chunkPos, Registry<Biome> biomeRegistry) { this(chunkPos, biomeRegistry, null, null); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public LevelChunkSection(int chunkPos, Registry<Biome> biomeRegistry, net.minecraft.world.level.ChunkPos pos, net.minecraft.world.level.Level level) {
|
|
+ // Paper end
|
|
this.bottomBlockY = LevelChunkSection.getBottomBlockY(chunkPos);
|
|
- this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
|
- this.biomes = new PalettedContainer<>(biomeRegistry, (Biome) biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
|
+ this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, level == null || level.chunkPacketBlockController == null ? null : level.chunkPacketBlockController.getPresetBlockStates(level, pos, this.bottomBlockY())); // Paper - Anti-Xray - Add preset block states
|
|
+ this.biomes = new PalettedContainer<>(biomeRegistry, (Biome) biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes
|
|
}
|
|
|
|
public static int getBottomBlockY(int chunkPos) {
|
|
@@ -158,10 +161,13 @@ public class LevelChunkSection {
|
|
this.biomes.read(buf);
|
|
}
|
|
|
|
- public void write(FriendlyByteBuf buf) {
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ @Deprecated public void write(FriendlyByteBuf buf) { this.write(buf, null); } // Notice for updates: Please make sure this method isn't used anywhere
|
|
+ public void write(FriendlyByteBuf buf, com.destroystokyo.paper.antixray.ChunkPacketInfo<BlockState> chunkPacketInfo) {
|
|
buf.writeShort(this.nonEmptyBlockCount);
|
|
- this.states.write(buf);
|
|
- this.biomes.write(buf);
|
|
+ this.states.write(buf, chunkPacketInfo, this.bottomBlockY());
|
|
+ this.biomes.write(buf, null, this.bottomBlockY());
|
|
+ // Paper end
|
|
}
|
|
|
|
public int getSerializedSize() {
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
|
index 911bf2b7bbe627f98d21681b0c6d5b8a5170c8a8..cdd357a8dd82cfd2a8abd45c1b7937b409af4b05 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
|
@@ -29,6 +29,7 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
return 0;
|
|
};
|
|
public final IdMap<T> registry;
|
|
+ private final T[] presetValues; // Paper - Anti-Xray - Add preset values
|
|
private volatile PalettedContainer.Data<T> data;
|
|
private final PalettedContainer.Strategy strategy;
|
|
private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer");
|
|
@@ -41,29 +42,65 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
this.threadingDetector.checkAndUnlock();
|
|
}
|
|
|
|
- public static <T> Codec<PalettedContainer<T>> codec(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy provider, T object) {
|
|
+ // Paper start - Anti-Xray - Add preset values
|
|
+ @Deprecated public static <T> Codec<PalettedContainer<T>> codec(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy provider, T object) { return PalettedContainer.codec(idList, entryCodec, provider, object, null); } // Notice for updates: Please make sure this function isn't used anywhere
|
|
+ public static <T> Codec<PalettedContainer<T>> codec(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy provider, T object, T[] presetValues) {
|
|
return RecordCodecBuilder.create((instance) -> {
|
|
return instance.group(entryCodec.mapResult(ExtraCodecs.orElsePartial(object)).listOf().fieldOf("palette").forGetter(PalettedContainer.DiscData::paletteEntries), Codec.LONG_STREAM.optionalFieldOf("data").forGetter(PalettedContainer.DiscData::storage)).apply(instance, PalettedContainer.DiscData::new);
|
|
}).comapFlatMap((serialized) -> {
|
|
- return read(idList, provider, serialized);
|
|
+ return read(idList, provider, serialized, object, presetValues);
|
|
+ // Paper end
|
|
}, (container) -> {
|
|
return container.write(idList, provider);
|
|
});
|
|
}
|
|
|
|
- public PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Configuration<T> dataProvider, BitStorage storage, List<T> paletteEntries) {
|
|
+ // Paper start - Anti-Xray - Add preset values
|
|
+ @Deprecated public PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Configuration<T> dataProvider, BitStorage storage, List<T> paletteEntries) { this(idList, paletteProvider, dataProvider, storage, paletteEntries, null, null); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Configuration<T> dataProvider, BitStorage storage, List<T> paletteEntries, T defaultValue, T[] presetValues) {
|
|
+ this.presetValues = presetValues;
|
|
this.registry = idList;
|
|
this.strategy = paletteProvider;
|
|
this.data = new PalettedContainer.Data<>(dataProvider, storage, dataProvider.factory().create(dataProvider.bits(), idList, this, paletteEntries));
|
|
+
|
|
+ if (presetValues != null && (dataProvider.factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY ? this.data.palette.valueFor(0) != defaultValue : dataProvider.factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY)) {
|
|
+ // In 1.18 Mojang unfortunately removed code that already handled possible resize operations on read from disk for us
|
|
+ // We readd this here but in a smarter way than it was before
|
|
+ int maxSize = 1 << dataProvider.bits();
|
|
+
|
|
+ for (T presetValue : presetValues) {
|
|
+ if (this.data.palette.getSize() >= maxSize) {
|
|
+ java.util.Set<T> allValues = new java.util.HashSet<>(paletteEntries);
|
|
+ allValues.addAll(Arrays.asList(presetValues));
|
|
+ int newBits = Mth.ceillog2(allValues.size());
|
|
+
|
|
+ if (newBits > dataProvider.bits()) {
|
|
+ this.onResize(newBits, null);
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ this.data.palette.idFor(presetValue);
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
}
|
|
|
|
- private PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Data<T> data) {
|
|
+ // Paper start - Anti-Xray - Add preset values
|
|
+ private PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Data<T> data, T[] presetValues) {
|
|
+ this.presetValues = presetValues;
|
|
+ // Paper end
|
|
this.registry = idList;
|
|
this.strategy = paletteProvider;
|
|
this.data = data;
|
|
}
|
|
|
|
- public PalettedContainer(IdMap<T> idList, T object, PalettedContainer.Strategy paletteProvider) {
|
|
+ // Paper start - Anti-Xray - Add preset values
|
|
+ @Deprecated public PalettedContainer(IdMap<T> idList, T object, PalettedContainer.Strategy paletteProvider) { this(idList, object, paletteProvider, null); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public PalettedContainer(IdMap<T> idList, T object, PalettedContainer.Strategy paletteProvider, T[] presetValues) {
|
|
+ this.presetValues = presetValues;
|
|
+ // Paper end
|
|
this.strategy = paletteProvider;
|
|
this.registry = idList;
|
|
this.data = this.createOrReuseData((PalettedContainer.Data<T>)null, 0);
|
|
@@ -78,11 +115,33 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
@Override
|
|
public int onResize(int newBits, T object) {
|
|
PalettedContainer.Data<T> data = this.data;
|
|
+
|
|
+ // Paper start - Anti-Xray - Add preset values
|
|
+ if (this.presetValues != null && object != null && data.configuration().factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY) {
|
|
+ int duplicates = 0;
|
|
+ List<T> presetValues = Arrays.asList(this.presetValues);
|
|
+ duplicates += presetValues.contains(object) ? 1 : 0;
|
|
+ duplicates += presetValues.contains(data.palette.valueFor(0)) ? 1 : 0;
|
|
+ newBits = Mth.ceillog2((1 << this.strategy.calculateBitsForSerialization(this.registry, 1 << newBits)) + presetValues.size() - duplicates);
|
|
+ }
|
|
+
|
|
PalettedContainer.Data<T> data2 = this.createOrReuseData(data, newBits);
|
|
data2.copyFrom(data.palette, data.storage);
|
|
this.data = data2;
|
|
- return data2.palette.idFor(object);
|
|
+ this.addPresetValues();
|
|
+ return object == null ? -1 : data2.palette.idFor(object);
|
|
+ // Paper end
|
|
+ }
|
|
+
|
|
+ // Paper start - Anti-Xray - Add preset values
|
|
+ private void addPresetValues() {
|
|
+ if (this.presetValues != null && this.data.configuration().factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY) {
|
|
+ for (T presetValue : this.presetValues) {
|
|
+ this.data.palette.idFor(presetValue);
|
|
+ }
|
|
+ }
|
|
}
|
|
+ // Paper end
|
|
|
|
public T getAndSet(int x, int y, int z, T value) {
|
|
this.acquire();
|
|
@@ -150,24 +209,34 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
data.palette.read(buf);
|
|
buf.readLongArray(data.storage.getRaw());
|
|
this.data = data;
|
|
+ this.addPresetValues(); // Paper - Anti-Xray - Add preset values (inefficient, but this not used by the server)
|
|
} finally {
|
|
this.release();
|
|
}
|
|
|
|
}
|
|
|
|
- public void write(FriendlyByteBuf buf) {
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ @Deprecated public void write(FriendlyByteBuf buf) { this.write(buf, null, 0); } // Notice for updates: Please make sure this method isn't used anywhere
|
|
+ public void write(FriendlyByteBuf buf, com.destroystokyo.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int bottomBlockY) {
|
|
this.acquire();
|
|
|
|
try {
|
|
- this.data.write(buf);
|
|
+ this.data.write(buf, chunkPacketInfo, bottomBlockY);
|
|
+
|
|
+ if (chunkPacketInfo != null) {
|
|
+ // Bottom block to 0 based chunk section index
|
|
+ int chunkSectionIndex = (bottomBlockY >> 4) - chunkPacketInfo.getChunk().getMinSection();
|
|
+ chunkPacketInfo.setPresetValues(chunkSectionIndex, this.presetValues);
|
|
+ }
|
|
+ // Paper end
|
|
} finally {
|
|
this.release();
|
|
}
|
|
|
|
}
|
|
|
|
- private static <T> DataResult<PalettedContainer<T>> read(IdMap<T> idList, PalettedContainer.Strategy provider, PalettedContainer.DiscData<T> serialized) {
|
|
+ private static <T> DataResult<PalettedContainer<T>> read(IdMap<T> idList, PalettedContainer.Strategy provider, PalettedContainer.DiscData<T> serialized, T defaultValue, T[] presetValues) { // Paper - Anti-Xray - Add preset values
|
|
List<T> list = serialized.paletteEntries();
|
|
int i = provider.size();
|
|
int j = provider.calculateBitsForSerialization(idList, list.size());
|
|
@@ -203,7 +272,7 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
}
|
|
}
|
|
|
|
- return DataResult.success(new PalettedContainer<>(idList, provider, configuration, bitStorage, list));
|
|
+ return DataResult.success(new PalettedContainer<>(idList, provider, configuration, bitStorage, list, defaultValue, presetValues)); // Paper - Anti-Xray - Add preset values
|
|
}
|
|
|
|
private PalettedContainer.DiscData<T> write(IdMap<T> idList, PalettedContainer.Strategy provider) {
|
|
@@ -260,7 +329,7 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
}
|
|
|
|
public PalettedContainer<T> copy() {
|
|
- return new PalettedContainer<>(this.registry, this.strategy, new PalettedContainer.Data<>(this.data.configuration(), this.data.storage().copy(), this.data.palette().copy()));
|
|
+ return new PalettedContainer<>(this.registry, this.strategy, new PalettedContainer.Data<>(this.data.configuration(), this.data.storage().copy(), this.data.palette().copy()), this.presetValues); // Paper - Anti-Xray - Add preset values
|
|
}
|
|
|
|
public void count(PalettedContainer.CountConsumer<T> counter) {
|
|
@@ -304,9 +373,20 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
return 1 + this.palette.getSerializedSize() + FriendlyByteBuf.getVarIntSize(this.storage.getSize()) + this.storage.getRaw().length * 8;
|
|
}
|
|
|
|
- public void write(FriendlyByteBuf buf) {
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ public void write(FriendlyByteBuf buf, com.destroystokyo.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int bottomBlockY) {
|
|
buf.writeByte(this.storage.getBits());
|
|
this.palette.write(buf);
|
|
+
|
|
+ if (chunkPacketInfo != null) {
|
|
+ // Bottom block to 0 based chunk section index
|
|
+ int chunkSectionIndex = (bottomBlockY >> 4) - chunkPacketInfo.getChunk().getMinSection();
|
|
+ chunkPacketInfo.setBits(chunkSectionIndex, this.configuration.bits());
|
|
+ chunkPacketInfo.setPalette(chunkSectionIndex, this.palette);
|
|
+ chunkPacketInfo.setIndex(chunkSectionIndex, buf.writerIndex() + FriendlyByteBuf.getVarIntSize(this.storage.getRaw().length));
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
buf.writeLongArray(this.storage.getRaw());
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
|
index cf86755050632b158576849b786079787db11763..1267e93d1e315d55086a87670fd098db552c3afd 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
|
@@ -67,7 +67,7 @@ import org.apache.logging.log4j.Logger;
|
|
|
|
public class ChunkSerializer {
|
|
|
|
- public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
|
|
+ public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null); // Paper - Anti-Xray - Add preset block states
|
|
private static final Logger LOGGER = LogManager.getLogger();
|
|
private static final String TAG_UPGRADE_DATA = "UpgradeData";
|
|
private static final String BLOCK_TICKS_TAG = "block_ticks";
|
|
@@ -146,16 +146,20 @@ public class ChunkSerializer {
|
|
if (k >= 0 && k < achunksection.length) {
|
|
Logger logger;
|
|
PalettedContainer datapaletteblock;
|
|
+ // Paper start - Anti-Xray - Add preset block states
|
|
+ BlockState[] presetBlockStates = world.chunkPacketBlockController.getPresetBlockStates(world, chunkPos, b0 << 4);
|
|
|
|
if (nbttagcompound1.contains("block_states", 10)) {
|
|
- dataresult = ChunkSerializer.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, nbttagcompound1.getCompound("block_states")).promotePartial((s) -> {
|
|
+ Codec<PalettedContainer<BlockState>> blockStateCodec = presetBlockStates == null ? ChunkSerializer.BLOCK_STATE_CODEC : PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), presetBlockStates);
|
|
+ dataresult = blockStateCodec.parse(NbtOps.INSTANCE, nbttagcompound1.getCompound("block_states")).promotePartial((s) -> {
|
|
ChunkSerializer.logErrors(chunkPos, b0, s);
|
|
});
|
|
logger = ChunkSerializer.LOGGER;
|
|
Objects.requireNonNull(logger);
|
|
datapaletteblock = (PalettedContainer) dataresult.getOrThrow(false, logger::error);
|
|
} else {
|
|
- datapaletteblock = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
|
+ datapaletteblock = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, presetBlockStates);
|
|
+ // Paper end
|
|
}
|
|
|
|
PalettedContainer datapaletteblock1;
|
|
@@ -168,7 +172,7 @@ public class ChunkSerializer {
|
|
Objects.requireNonNull(logger);
|
|
datapaletteblock1 = (PalettedContainer) dataresult.getOrThrow(false, logger::error);
|
|
} else {
|
|
- datapaletteblock1 = new PalettedContainer<>(iregistry, (Biome) iregistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
|
+ datapaletteblock1 = new PalettedContainer<>(iregistry, (Biome) iregistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes
|
|
}
|
|
|
|
LevelChunkSection chunksection = new LevelChunkSection(b0, datapaletteblock, datapaletteblock1);
|
|
@@ -421,7 +425,7 @@ public class ChunkSerializer {
|
|
}
|
|
|
|
private static Codec<PalettedContainer<Biome>> makeBiomeCodec(Registry<Biome> biomeRegistry) {
|
|
- return PalettedContainer.codec(biomeRegistry, biomeRegistry.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, (Biome) biomeRegistry.getOrThrow(Biomes.PLAINS));
|
|
+ return PalettedContainer.codec(biomeRegistry, biomeRegistry.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, (Biome) biomeRegistry.getOrThrow(Biomes.PLAINS), null); // Paper - Anti-Xray - Add preset biomes
|
|
}
|
|
|
|
public static CompoundTag write(ServerLevel world, ChunkAccess chunk) {
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
|
index 7bc1219523eeb0880493e6fb42692f1fdb30c110..d6efa18ba9c032858071f6c87f1bdc0ce2ddb20f 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
|
@@ -52,7 +52,7 @@ public class CraftChunk implements Chunk {
|
|
private final ServerLevel worldServer;
|
|
private final int x;
|
|
private final int z;
|
|
- private static final PalettedContainer<net.minecraft.world.level.block.state.BlockState> emptyBlockIDs = new PalettedContainer<>(net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
|
+ private static final PalettedContainer<net.minecraft.world.level.block.state.BlockState> emptyBlockIDs = new PalettedContainer<>(net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); // Paper - Anti-Xray - Add preset block states
|
|
private static final byte[] emptyLight = new byte[2048];
|
|
|
|
public CraftChunk(net.minecraft.world.level.chunk.LevelChunk chunk) {
|
|
@@ -325,7 +325,7 @@ public class CraftChunk implements Chunk {
|
|
PalettedContainer<Biome>[] biome = (includeBiome || includeBiomeTempRain) ? new PalettedContainer[cs.length] : null;
|
|
|
|
Registry<Biome> iregistry = this.worldServer.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
|
|
- Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(iregistry, iregistry.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getOrThrow(Biomes.PLAINS));
|
|
+ Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(iregistry, iregistry.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getOrThrow(Biomes.PLAINS), null); // Paper - Anti-Xray - Add preset biomes
|
|
|
|
for (int i = 0; i < cs.length; i++) {
|
|
CompoundTag data = new CompoundTag();
|
|
@@ -390,7 +390,7 @@ public class CraftChunk implements Chunk {
|
|
|
|
if (biome != null) {
|
|
Registry<Biome> iregistry = world.getHandle().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
|
|
- biome[i] = new PalettedContainer<>(iregistry, iregistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
|
+ biome[i] = new PalettedContainer<>(iregistry, iregistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index 20214d98ab5449798c3d045b4d1133ff5d939c11..0287df105ee4a2d415a343e1b54b4dd9e369e5d0 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -2201,7 +2201,7 @@ public final class CraftServer implements Server {
|
|
public ChunkGenerator.ChunkData createChunkData(World world) {
|
|
Validate.notNull(world, "World cannot be null");
|
|
ServerLevel handle = ((CraftWorld) world).getHandle();
|
|
- return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY));
|
|
+ return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), world); // Paper - Anti-Xray - Add parameters
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
|
|
index 960405935e395a31c0300773c41413801cf0d290..6f6bf950cd15b34031618782c82824cf0b191ff8 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
|
|
@@ -27,8 +27,13 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData {
|
|
private final Registry<net.minecraft.world.level.biome.Biome> biomes;
|
|
private Set<BlockPos> tiles;
|
|
private final Set<BlockPos> lights = new HashSet<>();
|
|
+ // Paper start - Anti-Xray - Add parameters
|
|
+ private final World world;
|
|
|
|
- public OldCraftChunkData(int minHeight, int maxHeight, Registry<net.minecraft.world.level.biome.Biome> biomes) {
|
|
+ @Deprecated public OldCraftChunkData(int minHeight, int maxHeight, Registry<net.minecraft.world.level.biome.Biome> biomes) { this(minHeight, maxHeight, biomes, null); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public OldCraftChunkData(int minHeight, int maxHeight, Registry<net.minecraft.world.level.biome.Biome> biomes, World world) {
|
|
+ this.world = world;
|
|
+ // Paper end
|
|
this.minHeight = minHeight;
|
|
this.maxHeight = maxHeight;
|
|
this.biomes = biomes;
|
|
@@ -176,7 +181,7 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData {
|
|
int offset = (y - this.minHeight) >> 4;
|
|
LevelChunkSection section = this.sections[offset];
|
|
if (create && section == null) {
|
|
- this.sections[offset] = section = new LevelChunkSection(offset + (this.minHeight >> 4), this.biomes);
|
|
+ this.sections[offset] = section = new LevelChunkSection(offset + (this.minHeight >> 4), this.biomes, null, this.world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) this.world).getHandle() : null); // Paper - Anti-Xray - Add parameters
|
|
}
|
|
return section;
|
|
}
|