mirror of
https://github.com/PaperMC/Paper.git
synced 2024-08-30 16:49:42 +02:00
This PR contains all of Tuinity's patches. Very notable ones are: - Highly optimised collisions - Optimised entity lookups by bounding box (Mojang made regressions in 1.17, this brings it back to 1.16) - Starlight https://github.com/PaperMC/Starlight - Rewritten dataconverter system https://github.com/PaperMC/DataConverter - Random block ticking optimisation (wrongly dropped from Paper 1.17) - Chunk ticking optimisations - Anything else I've forgotten in the 60 or so patches If you are a previous Tuinity user, your config will not migrate. You must do it yourself. The config options have simply been moved into paper.yml, so it will be an easy migration. However, please note that the chunk loading options in tuinity.yml are NOT compatible with the options in paper.yml. * Port tuinity, initial patchset * Update gradle to 7.2 jmp said it fixes rebuildpatches not working for me. it fucking better * Completely clean apply * Remove tuinity config, add per player api patch * Remove paper reobf mappings patch * Properly update gradlew * Force clean rebuild * Mark fixups Comments and ATs still need to be done * grep -r "Tuinity" * Fixup * Ensure gameprofile lastaccess is written only under the state lock * update URL for dataconverter * Only clean rebuild tuinity patches might fix merge conflicts * Use UTF-8 for gradlew * Clean rb patches again * Convert block ids used as item ids Neither the converters of pre 1.13 nor DFU handled these cases, as by the time they were written the game at the time didn't consider these ids valid - they would be air. Because of this, some worlds have logspam since only DataConverter (not DFU or legacy converters) will warn when an invalid id has been seen. While quite a few do need to now be considered as air, quite a lot do not. So it makes sense to add conversion for these items, instead of simply suppressing or ignoring the logs. I've now added id -> string conversion for all block ids that could be used as items that existed in the game before 1.7.10 (I have no interest in tracking down the exact version block ids stopped working) that were on https://minecraft-ids.grahamedgecombe.com/ Items that did not directly convert to new items will be instead converted to air: stems, wheat crops, piston head, tripwire wire block * Fix LightPopulated parsing in V1466 The DFU code was checking if the number existed, not if it didn't exist. I misread the original code. * Always parse protochunk light sources unless it is marked as non-lit Chunks not marked as lit will always go through the light engine, so they should always have their block sources parsed. * Update custom names to JSON for players Missed this fix from CB, as it was inside the DataFixers class. I decided to double check all of the CB changes again: DataFixers.java was the only area I missed, as I had inspected all datafixer diffs and implemented them all into DataConverter. I also checked Bootstrap.java again, and re-evaluated their changes. I had previously done this, but determined that they were all bad. The changes to make standing_sign block map to oak_sign block in V1450 is bad, because that's not the item id V1450 accepts. Only in 1.14 did oak_sign even exist, and as expected there is a converter to rename all existing sign items/blocks. The fix to register the portal block under id 1440 is useless, as the flattenning logic will default to the lowest registered id - which is the exact blockstate that CB registers into 1440. So it just doesn't do anything. The extra item ids in the id -> string converter are already added, but I found this from EMC originally. The change for the spawn egg id 23 -> Arrow is just wrong, that id DOES correspond to TippedArrow, NOT Arrow. As expected, the spawn egg already has a dedicated mapping for Arrow, which is id 10 - which was Arrow's entity id. I also ported a fix for the cooked_fished id update. This doesn't really matter since there is already a dataconverter to fix this, but the game didn't accept cooked_fished at the time. So I see no harm. * Review all converters and walkers - Refactor V99 to have helper methods for defining entity/tile entity types - Automatically namespace all ids that should be namespaced. While vanilla never saved non-namespaced data for things that are namespaced, plugins/users might have. - Synchronised the identity ensure map in HelperBlockFlatteningV1450 - Code style consistency - Add missing log warning in V102 for ITEM_NAME type conversion - Use getBoolean instead of getByte - Use ConverterAbstractEntityRename for V143 TippedArrow -> Arrow rename, as it will affect ENTITY_NAME type - Always set isVillager to false in V502 for Zombie - Register V808's converter under subversion 1 like DFU - Register a breakpoint for V1.17.1. In the future, all final versions of major releases will have a breakpoint so that the work required to determine if a converter needs a breakpoint is minimal - Validate that a dataconverter is only registered for a version that is registered - ConverterFlattenTileEntity is actually ConverterFlattenEntity It even registered the converters under TILE_ENTITY, instead of ENTITY. - Fix id comparison in V1492 STRUCTURE_FEATURE renamer - Use ConverterAbstractStatsRename for V1510 stats renamer At the time I had written that class, the abstract renamer didn't exist. - Ensure OwnerUUID is at least set to empty string in V1904 if the ocelot is converted to a cat (this is likely so that it retains a collar) - Use generic read/write for Records in V1946 Records is actually a list, not a map. So reading map was invalid. * Always set light to zero when propagating decrease This fixes an almost infinite loop where light values would be spam queued on a very small subset on blocks. This also likely fixes the memory issues people were seeing. * re-organize patches * Apply and fix conflicts * Revert some patches getChunkAt retains chunks so that plugins don't spam loads revert mc-4 fix will remain unless issues pop up * Shuffle iterated chunks if per player is not enabled Can help with some mob spawning stacking up at locations * Make per player default, migrate all configs * Adjust comments in fixups * Rework config for player chunk loader Old config is not compatible. Move all configs to be under `settings` in paper.yml The player chunk loader has been modified to less aggressively load chunks, but to send chunks at higher rates compared to tuinity. There are new config entries to tune this behavior. * Add back old constructor to CompressionEncoder/Decoder (fixes Tuinity #358) * Raise chunk loading default limits * Reduce worldgen thread workers for lower core count cpus * Raise limits for chunk loading config Also place it under `chunk-loading` * Disable max chunk send rate by default * Fix conflicts and rebuild patches * Drop default send rate again Appears to be still causing problems for no known reason * Raise chunk send limits to 100 per player While a low limit fixes ping issues for some people, most people do not suffer from this issue and thus should not suffer from an extremely slow load-in rate. * Rebase part 1 Autosquash the fixups * Move not implemented up * Fixup mc-dev fixes Missed this one * Rebase per player viewdistance api into the original api patch * Remove old light engine patch part 1 The prioritisation must be kept from it, so that part has been rebased into the priority patch. Part 2 will deal with rebasing all of the patches _after_ * Rebase remaining patches for old light patch removal * Remove other mid tick patch * Remove Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch Replaced by `Do not copy visible chunks` * Revert AT for Vec3i setX/Y/Z The class is immutable. set should not be exposed * Remove old IntegerUtil class * Replace old CraftChunk#getEntities patch * Remove import for SWMRNibbleArray in ChunkAccess * Finished merge checklist * Remove ensureTickThread impl in urgency patch Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com> Co-authored-by: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
1454 lines
75 KiB
Diff
1454 lines
75 KiB
Diff
From 0000000000000000000000000000000000000000 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 a91a7d8f56a068b18d50a8b987b71510b0a19d5b..9e16c9e6b6e84d84028821f2ade103dbced66470 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
@@ -1,7 +1,9 @@
|
|
package com.destroystokyo.paper;
|
|
|
|
+import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.configuration.file.YamlConfiguration;
|
|
import org.spigotmc.SpigotWorldConfig;
|
|
@@ -484,5 +486,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..280ece653cdda74e9c8fab4e9e5b3a952901cb01
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java
|
|
@@ -0,0 +1,46 @@
|
|
+package com.destroystokyo.paper.antixray;
|
|
+
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.core.Direction;
|
|
+import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket;
|
|
+import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.server.level.ServerPlayerGameMode;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.chunk.ChunkAccess;
|
|
+import net.minecraft.world.level.chunk.LevelChunk;
|
|
+import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
+
|
|
+public class ChunkPacketBlockController {
|
|
+
|
|
+ public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController();
|
|
+
|
|
+ protected ChunkPacketBlockController() {
|
|
+
|
|
+ }
|
|
+
|
|
+ public BlockState[] getPresetBlockStates(Level level, ChunkAccess chunk, LevelChunkSection chunkSection, boolean initializeBlocks) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public boolean shouldModify(ServerPlayer player, LevelChunk chunk) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public ChunkPacketInfo<BlockState> getChunkPacketInfo(ClientboundLevelChunkPacket chunkPacket, LevelChunk chunk) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public void modifyBlocks(ClientboundLevelChunkPacket 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..58052a5f773b5882acd90bc0214a36e8a512817c
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
|
@@ -0,0 +1,635 @@
|
|
+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.ClientboundLevelChunkPacket;
|
|
+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 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 = {LevelChunk.EMPTY_SECTION, LevelChunk.EMPTY_SECTION, LevelChunk.EMPTY_SECTION, LevelChunk.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[]{LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.STONE.defaultBlockState())};
|
|
+ presetBlockStateBitsNetherrackGlobal = new int[]{LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.NETHERRACK.defaultBlockState())};
|
|
+ presetBlockStateBitsEndStoneGlobal = new int[]{LevelChunkSection.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] = LevelChunkSection.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[LevelChunkSection.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 = LevelChunkSection.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, ChunkAccess chunk, LevelChunkSection chunkSection, boolean initializeBlocks) {
|
|
+ // Return the block states to be added to the paletted containers so that they can be used for obfuscation
|
|
+ if (chunkSection.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(ClientboundLevelChunkPacket 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(ClientboundLevelChunkPacket 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.level;
|
|
+ ((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[][]> nextNext = ThreadLocal.withInitial(() -> new boolean[16][16]);
|
|
+
|
|
+ public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) {
|
|
+ int[] presetBlockStateBits = this.presetBlockStateBits.get();
|
|
+ boolean[] solid = ChunkPacketBlockControllerAntiXray.solid.get();
|
|
+ boolean[] obfuscate = ChunkPacketBlockControllerAntiXray.obfuscate.get();
|
|
+ boolean[][] current = ChunkPacketBlockControllerAntiXray.current.get();
|
|
+ boolean[][] next = ChunkPacketBlockControllerAntiXray.next.get();
|
|
+ boolean[][] nextNext = ChunkPacketBlockControllerAntiXray.nextNext.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.level;
|
|
+ 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) == LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE) {
|
|
+ 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++) {
|
|
+ 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]) == LevelChunk.EMPTY_SECTION;
|
|
+
|
|
+ for (int z = 0; z < 16; z++) {
|
|
+ for (int x = 0; x < 16; x++) {
|
|
+ current[z][x] = true;
|
|
+ next[z][x] = skipFirstLayer || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(belowChunkSection.getBlockState(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 ? LevelChunk.EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex];
|
|
+ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? LevelChunk.EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex];
|
|
+ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? LevelChunk.EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex];
|
|
+ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? LevelChunk.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]) != LevelChunk.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 (!solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(aboveChunkSection.getBlockState(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] || nearbyChunkSections[2] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[2].getBlockState(0, y, 15))] || nearbyChunkSections[0] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[0].getBlockState(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] || nearbyChunkSections[2] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[2].getBlockState(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] || nearbyChunkSections[2] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[2].getBlockState(15, y, 15))] || nearbyChunkSections[1] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[1].getBlockState(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] || nearbyChunkSections[0] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[0].getBlockState(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] || nearbyChunkSections[1] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[1].getBlockState(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] || nearbyChunkSections[3] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[3].getBlockState(0, y, 0))] || nearbyChunkSections[0] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[0].getBlockState(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] || nearbyChunkSections[3] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[3].getBlockState(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] || nearbyChunkSections[3] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[3].getBlockState(15, y, 0))] || nearbyChunkSections[1] == LevelChunk.EMPTY_SECTION || !solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(nearbyChunkSections[1].getBlockState(0, y, 15))]) {
|
|
+ bitStorageWriter.skip();
|
|
+ } else {
|
|
+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!obfuscate[bits]) {
|
|
+ next[15][15] = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private boolean[] readPalette(Palette<BlockState> palette, boolean[] temp, boolean[] global) {
|
|
+ if (palette == LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE) {
|
|
+ return global;
|
|
+ }
|
|
+
|
|
+ BlockState blockState;
|
|
+
|
|
+ for (int i = 0; (blockState = palette.valueFor(i)) != null; i++) {
|
|
+ temp[i] = global[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)];
|
|
+ }
|
|
+
|
|
+ return temp;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onBlockChange(Level level, BlockPos blockPos, BlockState newBlockState, BlockState oldBlockState, int flags, int maxUpdateDepth) {
|
|
+ if (oldBlockState != null && solidGlobal[LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE.idFor(oldBlockState)] && !solidGlobal[LevelChunkSection.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[LevelChunkSection.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..7bc2d4daffa8e9e71c3bf496d2cf1a2b7f3c6a4b
|
|
--- /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.ClientboundLevelChunkPacket;
|
|
+import net.minecraft.world.level.chunk.LevelChunk;
|
|
+import net.minecraft.world.level.chunk.Palette;
|
|
+
|
|
+public class ChunkPacketInfo<T> {
|
|
+
|
|
+ private final ClientboundLevelChunkPacket 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(ClientboundLevelChunkPacket 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 ClientboundLevelChunkPacket 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..02324a59ac21db5349fe2a74248b2c6f92fa8233
|
|
--- /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.ClientboundLevelChunkPacket;
|
|
+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(ClientboundLevelChunkPacket 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/ClientboundLevelChunkPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacket.java
|
|
index c28879f32b004f36ff746ea2274f91ddd9501e71..60d72e488bc77cd913328be400ca374a873b4561 100644
|
|
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacket.java
|
|
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacket.java
|
|
@@ -37,7 +37,24 @@ public class ClientboundLevelChunkPacket implements Packet<ClientGamePacketListe
|
|
}
|
|
// Paper end
|
|
|
|
- public ClientboundLevelChunkPacket(LevelChunk chunk) {
|
|
+ // Paper start - Async-Anti-Xray - Ready flag for the connection
|
|
+ private volatile boolean ready;
|
|
+
|
|
+ @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 ClientboundLevelChunkPacket(LevelChunk chunk) { this(chunk, true); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public ClientboundLevelChunkPacket(LevelChunk chunk, boolean modifyBlocks) {
|
|
+ com.destroystokyo.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo = modifyBlocks ? chunk.level.chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null;
|
|
+ // Paper end
|
|
ChunkPos chunkPos = chunk.getPos();
|
|
this.x = chunkPos.x;
|
|
this.z = chunkPos.z;
|
|
@@ -51,7 +68,14 @@ public class ClientboundLevelChunkPacket implements Packet<ClientGamePacketListe
|
|
|
|
this.biomes = chunk.getBiomes().writeBiomes();
|
|
this.buffer = new byte[this.calculateChunkSize(chunk)];
|
|
- this.availableSections = this.extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk);
|
|
+
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ if (chunkPacketInfo != null) {
|
|
+ chunkPacketInfo.setBuffer(this.buffer);
|
|
+ }
|
|
+
|
|
+ this.availableSections = this.extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk, chunkPacketInfo);
|
|
+ // Paper end
|
|
this.blockEntitiesTags = Lists.newArrayList();
|
|
int totalTileEntities = 0; // Paper
|
|
|
|
@@ -71,6 +95,7 @@ public class ClientboundLevelChunkPacket implements Packet<ClientGamePacketListe
|
|
this.blockEntitiesTags.add(compoundTag);
|
|
}
|
|
|
|
+ chunk.level.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
|
|
}
|
|
|
|
public ClientboundLevelChunkPacket(FriendlyByteBuf buf) {
|
|
@@ -120,7 +145,10 @@ public class ClientboundLevelChunkPacket implements Packet<ClientGamePacketListe
|
|
return byteBuf;
|
|
}
|
|
|
|
- public BitSet extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) {
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ @Deprecated public BitSet extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) { return extractChunkData(buf, chunk, null); } // Notice for updates: Please make sure this method isn't used anywhere
|
|
+ public BitSet extractChunkData(FriendlyByteBuf buf, LevelChunk chunk, com.destroystokyo.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
|
|
+ // Paper end
|
|
BitSet bitSet = new BitSet();
|
|
LevelChunkSection[] levelChunkSections = chunk.getSections();
|
|
int i = 0;
|
|
@@ -129,7 +157,7 @@ public class ClientboundLevelChunkPacket implements Packet<ClientGamePacketListe
|
|
LevelChunkSection levelChunkSection = levelChunkSections[i];
|
|
if (levelChunkSection != LevelChunk.EMPTY_SECTION && !levelChunkSection.isEmpty()) {
|
|
bitSet.set(i);
|
|
- levelChunkSection.write(buf);
|
|
+ levelChunkSection.write(buf, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index 63d52f6f0c0097654ddab05fea4bd809dc612a9b..292fb6d71ca8bd63ad04d87d04f13b7f4f6f98fc 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -1534,7 +1534,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
|
|
public void playerLoadedChunk(ServerPlayer player, Packet<?>[] packets, LevelChunk chunk) {
|
|
if (packets[0] == null) {
|
|
- packets[0] = new ClientboundLevelChunkPacket(chunk);
|
|
+ packets[0] = new ClientboundLevelChunkPacket(chunk, chunk.level.chunkPacketBlockController.shouldModify(player, chunk)); // Paper - Ani-Xray - Bypass
|
|
packets[1] = new ClientboundLightUpdatePacket(chunk.getPos(), this.lightEngine, (BitSet) null, (BitSet) null, true);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
index 8a1df5b26a6ba0e2fa13a9974cf41384a8e6d4fe..0df81cbddf2e7f164861b95cf572f9d6d3f031ca 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -378,7 +378,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 - 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 8659df9d2e9d8b3b0cd992efc9a1f3b2d958e9c8..3f7919f4e8f5a5b79bf4e541ea5f4ce482965fa1 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
|
@@ -49,7 +49,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
|
|
@@ -315,6 +315,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 fac722d2f5bbd86cbeacc7da4bb45155257ae97f..d050d60900a6829a9409ef944d7e4fceb5248dc1 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -165,6 +165,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 {
|
|
return this.typeKey;
|
|
}
|
|
|
|
- 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 - 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;
|
|
@@ -259,6 +260,9 @@ 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
|
|
@@ -449,6 +453,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
// CraftBukkit end
|
|
|
|
BlockState iblockdata1 = chunk.setType(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 149ac5ec368b53a9a5e9208bd49a3c9453625d9c..12d11a249c759e99568a76c791cc0d65adfcfe94 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
|
|
@@ -74,7 +74,7 @@ public interface ChunkAccess extends BlockGetter, FeatureAccess {
|
|
default LevelChunkSection getOrCreateSection(int yIndex) {
|
|
LevelChunkSection[] levelChunkSections = this.getSections();
|
|
if (levelChunkSections[yIndex] == LevelChunk.EMPTY_SECTION) {
|
|
- levelChunkSections[yIndex] = new LevelChunkSection(this.getSectionYFromSectionIndex(yIndex));
|
|
+ levelChunkSections[yIndex] = new LevelChunkSection(this.getSectionYFromSectionIndex(yIndex), this, this.getLevel(), true); // Paper - Anti-Xray - Add parameters
|
|
}
|
|
|
|
return levelChunkSections[yIndex];
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java
|
|
index 69c2454533e6f21c70792b555ec02c6bc6d169b3..f9e0e109497d685a9d88d2fa8892287b9fa97443 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java
|
|
@@ -86,7 +86,7 @@ public class EmptyLevelChunk extends LevelChunk {
|
|
private static final Biome[] EMPTY_BIOMES = new Biome[0];
|
|
|
|
public EmptyChunkBiomeContainer(Level world) {
|
|
- super(world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), world, EMPTY_BIOMES);
|
|
+ super(net.minecraft.server.MinecraftServer.getServer().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), world, EMPTY_BIOMES); // Paper - Anti-Xray - The world isnt ready yet, use server singleton for registry
|
|
}
|
|
|
|
@Override
|
|
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 8141935e2ee58bbb58c6b5cfdef5a9a88d7658ec..515e28eea8cbab261320352ee0db9b877807f3ed 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
@@ -456,7 +456,7 @@ public class LevelChunk implements ChunkAccess {
|
|
return null;
|
|
}
|
|
|
|
- chunksection = new LevelChunkSection(SectionPos.blockToSectionCoord(i));
|
|
+ chunksection = new LevelChunkSection(SectionPos.blockToSectionCoord(i), this, this.level, true); // Paper - Anti-Xray - Add parameters
|
|
this.sections[j] = chunksection;
|
|
}
|
|
|
|
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 5fd66020a937b641e2a060cf38df731a43f3bf55..c9fefeef19bd46ade51b23eadb5eef3a88024ea1 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
@@ -20,16 +20,25 @@ public class LevelChunkSection {
|
|
private short tickingFluidCount;
|
|
final PalettedContainer<BlockState> states; // Paper - package-private
|
|
|
|
- public LevelChunkSection(int yOffset) {
|
|
- this(yOffset, (short)0, (short)0, (short)0);
|
|
+ // Paper start - Anti-Xray - Add parameters
|
|
+ @Deprecated public LevelChunkSection(int yOffset) { this(yOffset, null, null, true); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ public LevelChunkSection(int yOffset, ChunkAccess chunk, net.minecraft.world.level.Level level, boolean initializeBlocks) {
|
|
+ this(yOffset, (short) 0, (short) 0, (short) 0, chunk, level, initializeBlocks);
|
|
+ // Paper end
|
|
}
|
|
|
|
- public LevelChunkSection(int yOffset, short nonEmptyBlockCount, short randomTickableBlockCount, short nonEmptyFluidCount) {
|
|
+ // Paper start - Anti-Xray - Add parameters
|
|
+ @Deprecated public LevelChunkSection(int yOffset, short nonEmptyBlockCount, short randomTickableBlockCount, short nonEmptyFluidCount) { // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ this(yOffset, nonEmptyBlockCount, randomTickableBlockCount, nonEmptyFluidCount, null, null, true);
|
|
+ }
|
|
+ public LevelChunkSection(int yOffset, short nonEmptyBlockCount, short randomTickableBlockCount, short nonEmptyFluidCount, ChunkAccess chunk, net.minecraft.world.level.Level level, boolean initializeBlocks) {
|
|
+ // Paper end
|
|
this.bottomBlockY = getBottomBlockY(yOffset);
|
|
this.nonEmptyBlockCount = nonEmptyBlockCount;
|
|
this.tickingBlockCount = randomTickableBlockCount;
|
|
this.tickingFluidCount = nonEmptyFluidCount;
|
|
- this.states = new PalettedContainer<>(GLOBAL_BLOCKSTATE_PALETTE, Block.BLOCK_STATE_REGISTRY, NbtUtils::readBlockState, NbtUtils::writeBlockState, Blocks.AIR.defaultBlockState());
|
|
+ this.states = new PalettedContainer<>(GLOBAL_BLOCKSTATE_PALETTE, Block.BLOCK_STATE_REGISTRY, NbtUtils::readBlockState, NbtUtils::writeBlockState, Blocks.AIR.defaultBlockState(),
|
|
+ level == null ? null : level.chunkPacketBlockController.getPresetBlockStates(level, chunk, this, initializeBlocks), initializeBlocks); // Paper - Anti-Xray - Add preset block states
|
|
}
|
|
|
|
public static int getBottomBlockY(int chunkPos) {
|
|
@@ -147,9 +156,12 @@ public class LevelChunkSection {
|
|
this.states.read(buf);
|
|
}
|
|
|
|
- public void write(FriendlyByteBuf buf) {
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ @Deprecated public void write(FriendlyByteBuf buf) { 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) {
|
|
+ // Paper end
|
|
buf.writeShort(this.nonEmptyBlockCount);
|
|
- this.states.write(buf);
|
|
+ this.states.write(buf, chunkPacketInfo, this.bottomBlockY); // Paper - Anti-Xray - Add chunk packet info
|
|
}
|
|
|
|
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 bb8fd88aebb550edec8c679622a02a595cbc6694..ac51089aae57a5f1d2411367ff177e058702894c 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
|
@@ -28,6 +28,7 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
private final Function<CompoundTag, T> reader;
|
|
private final Function<T, CompoundTag> writer;
|
|
private final T defaultValue;
|
|
+ private final T[] presetValues; // Paper - Anti-Xray - Add preset values
|
|
protected BitStorage storage;
|
|
private Palette<T> palette;
|
|
private int bits;
|
|
@@ -48,14 +49,46 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
this.lock.release();
|
|
}
|
|
|
|
- public PalettedContainer(Palette<T> fallbackPalette, IdMapper<T> idList, Function<CompoundTag, T> elementDeserializer, Function<T, CompoundTag> elementSerializer, T defaultElement) {
|
|
+ // Paper start - Anti-Xray - Add preset values
|
|
+ @Deprecated public PalettedContainer(Palette<T> fallbackPalette, IdMapper<T> idList, Function<CompoundTag, T> elementDeserializer, Function<T, CompoundTag> elementSerializer, T defaultElement) { // Notice for updates: Please make sure this constructor isn't used anywhere
|
|
+ this(fallbackPalette, idList, elementDeserializer, elementSerializer, defaultElement, null, true);
|
|
+ }
|
|
+ public PalettedContainer(Palette<T> fallbackPalette, IdMapper<T> idList, Function<CompoundTag, T> elementDeserializer, Function<T, CompoundTag> elementSerializer, T defaultElement, T[] presetValues, boolean initialize) {
|
|
+ // Paper end
|
|
this.globalPalette = fallbackPalette;
|
|
this.registry = idList;
|
|
this.reader = elementDeserializer;
|
|
this.writer = elementSerializer;
|
|
this.defaultValue = defaultElement;
|
|
- this.setBits(4);
|
|
+ // Paper start - Anti-Xray - Add preset values
|
|
+ this.presetValues = presetValues;
|
|
+
|
|
+ if (initialize) {
|
|
+ if (presetValues == null) {
|
|
+ // Default
|
|
+ this.setBits(4);
|
|
+ } else {
|
|
+ // Count the number of required bits
|
|
+ // Preset values: presetValues.length - 1
|
|
+ // Air: + 1
|
|
+ // Extra: + 15
|
|
+ // Air and extra correspond to the default behavior this.setBits(4)
|
|
+ this.setBits(32 - Integer.numberOfLeadingZeros(presetValues.length + 15));
|
|
+ this.addPresetValues();
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+ }
|
|
+
|
|
+ // Paper start - Anti-Xray - Add preset values
|
|
+ private void addPresetValues() {
|
|
+ if (this.presetValues != null && this.palette != this.globalPalette) {
|
|
+ for (T presetValue : this.presetValues) {
|
|
+ this.palette.idFor(presetValue);
|
|
+ }
|
|
+ }
|
|
}
|
|
+ // Paper end
|
|
|
|
private static int getIndex(int x, int y, int z) {
|
|
return y << 8 | z << 4 | x;
|
|
@@ -84,6 +117,7 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
BitStorage bitStorage = this.storage;
|
|
Palette<T> palette = this.palette;
|
|
this.setBits(newSize);
|
|
+ this.addPresetValues(); // Paper - Anti-Xray - Add preset values
|
|
|
|
for(int i = 0; i < bitStorage.getSize(); ++i) {
|
|
T object = palette.valueFor(bitStorage.get(i));
|
|
@@ -153,17 +187,38 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
|
|
this.palette.read(buf);
|
|
buf.readLongArray(this.storage.getRaw());
|
|
+ // Paper start - Anti-Xray - Add preset values
|
|
+ // If there are many preset values this may require several resize operations
|
|
+ // This can be avoided by calculating the required bits in advance, as it is done in #read(ListTag, long[])
|
|
+ // However, this method is only used by the client, so it does not matter
|
|
+ this.addPresetValues();
|
|
+ // Paper end
|
|
} finally {
|
|
this.release();
|
|
}
|
|
|
|
}
|
|
|
|
- public void write(FriendlyByteBuf buf) {
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ @Deprecated public void write(FriendlyByteBuf buf) { 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) {
|
|
+ // Paper end
|
|
try {
|
|
this.acquire();
|
|
buf.writeByte(this.bits);
|
|
this.palette.write(buf);
|
|
+
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ if (chunkPacketInfo != null) {
|
|
+ // Bottom block to 0 based chunk section index
|
|
+ int chunkSectionIndex = (bottomBlockY >> 4) - chunkPacketInfo.getChunk().getMinSection();
|
|
+ chunkPacketInfo.setBits(chunkSectionIndex, this.bits);
|
|
+ chunkPacketInfo.setPalette(chunkSectionIndex, this.palette);
|
|
+ chunkPacketInfo.setIndex(chunkSectionIndex, buf.writerIndex() + FriendlyByteBuf.getVarIntSize(this.storage.getRaw().length));
|
|
+ chunkPacketInfo.setPresetValues(chunkSectionIndex, this.presetValues);
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
buf.writeLongArray(this.storage.getRaw());
|
|
} finally {
|
|
this.release();
|
|
@@ -174,12 +229,14 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
|
public void read(ListTag paletteNbt, long[] data) {
|
|
try {
|
|
this.acquire();
|
|
- int i = Math.max(4, Mth.ceillog2(paletteNbt.size()));
|
|
- if (i != this.bits) {
|
|
+ // Paper - Anti-Xray - TODO: Should this.presetValues.length just be added here (faster) or should the contents be compared to calculate the size (less RAM)?
|
|
+ int i = Math.max(4, Mth.ceillog2(paletteNbt.size() + (this.presetValues == null ? 0 : this.presetValues.length))); // Paper - Anti-Xray - Calculate the size with preset values
|
|
+ if (true || i != this.bits) { // Paper - Anti-Xray - Not initialized yet
|
|
this.setBits(i);
|
|
}
|
|
|
|
this.palette.read(paletteNbt);
|
|
+ this.addPresetValues(); // Paper - Anti-Xray - Add preset values
|
|
int j = data.length * 64 / 4096;
|
|
if (this.palette == this.globalPalette) {
|
|
Palette<T> palette = new HashMapPalette<>(this.registry, i, this.dummyPaletteResize, this.reader, this.writer);
|
|
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 670e4f65680ca36fba1c84cb334c470ea8fa9b60..79f2b3942a3ccccd8fe8719db12de458212e8659 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
|
|
@@ -134,7 +134,7 @@ public class ChunkSerializer {
|
|
byte b0 = nbttagcompound2.getByte("Y");
|
|
|
|
if (nbttagcompound2.contains("Palette", 9) && nbttagcompound2.contains("BlockStates", 12)) {
|
|
- LevelChunkSection chunksection = new LevelChunkSection(b0);
|
|
+ LevelChunkSection chunksection = new LevelChunkSection(b0, null, world, false); // Paper - Anti-Xray - Add parameters and don't initialize because it's done in the line below internally
|
|
|
|
chunksection.getStates().read(nbttagcompound2.getList("Palette", 10), nbttagcompound2.getLongArray("BlockStates"));
|
|
chunksection.recalcBlockCounts();
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
|
index 17c2f2dca587b350dc3e48f01dc62025a12d83c0..1ba393e2b47cca45bfa8e4ff4ef2438f273fd467 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
|
@@ -46,7 +46,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 LevelChunkSection(0).getStates();
|
|
+ private static final PalettedContainer<net.minecraft.world.level.block.state.BlockState> emptyBlockIDs = new LevelChunkSection(0, null, null, true).getStates(); // Paper - Anti-Xray - Add parameters
|
|
private static final byte[] emptyLight = new byte[2048];
|
|
|
|
public CraftChunk(net.minecraft.world.level.chunk.LevelChunk chunk) {
|
|
@@ -275,7 +275,7 @@ public class CraftChunk implements Chunk {
|
|
CompoundTag data = new CompoundTag();
|
|
cs[i].getStates().write(data, "Palette", "BlockStates");
|
|
|
|
- PalettedContainer blockids = new PalettedContainer<>(LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE, net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, NbtUtils::readBlockState, NbtUtils::writeBlockState, Blocks.AIR.defaultBlockState()); // TODO: snapshot whole ChunkSection
|
|
+ PalettedContainer blockids = new PalettedContainer<>(LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE, net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, NbtUtils::readBlockState, NbtUtils::writeBlockState, Blocks.AIR.defaultBlockState(), null, false); // TODO: snapshot whole ChunkSection // Paper - Anti-Xray - Add no preset block states and don't initialize because it's done in the line below internally
|
|
blockids.read(data.getList("Palette", CraftMagicNumbers.NBT.TAG_COMPOUND), data.getLongArray("BlockStates"));
|
|
|
|
sectionBlockIDs[i] = blockids;
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
|
|
index 7bfd803d11976dba9ace79edc76bb083a2eccf38..a48c659c02c6c33a8efdac6daf9c9a0708f05071 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
|
|
@@ -25,9 +25,11 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData {
|
|
private final LevelChunkSection[] sections;
|
|
private Set<BlockPos> tiles;
|
|
private final Set<BlockPos> lights = new HashSet<>();
|
|
+ private World world; // Paper - Anti-Xray - Add parameters
|
|
|
|
public OldCraftChunkData(World world) {
|
|
this(world.getMinHeight(), world.getMaxHeight());
|
|
+ this.world = world; // Paper - Anti-Xray - Add parameters
|
|
}
|
|
|
|
/* pp for tests */ OldCraftChunkData(int minHeight, int maxHeight) {
|
|
@@ -177,7 +179,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.sections[offset] = section = new LevelChunkSection(offset + (this.minHeight >> 4), null, world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) this.world).getHandle() : null, true); // Paper - Anti-Xray - Add parameters
|
|
}
|
|
return section;
|
|
}
|