From bd2d33ccb1c71f708401054435dd5ad7abed47e8 Mon Sep 17 00:00:00 2001 From: MiniDigger | Martin Date: Wed, 24 Nov 2021 08:37:09 +0100 Subject: [PATCH] hey, look ma, am doing my part! --- .../Add-effect-to-block-break-naturally.patch | 0 ...layerDeathEvent-shouldDropExperience.patch | 0 .../Add-effect-to-block-break-naturally.patch | 0 ...Chunks-from-Hoppers-and-other-things.patch | 0 .../server/Entity-Activation-Range-2.0.patch | 26 +- .../server/Fix-Light-Command.patch | 0 .../Fix-items-not-falling-correctly.patch | 0 ...k-in-stack-not-having-effects-when-d.patch | 0 ...rializing-mismatching-chunk-coordina.patch | 17 +- .../server/Increase-Light-Queue-Size.patch | 2 +- .../server/Lag-compensate-eating.patch | 2 +- ...timise-IEntityAccess-getPlayerByUUID.patch | 0 ...mize-call-to-getFluid-for-explosions.patch | 0 ...layerDeathEvent-shouldDropExperience.patch | 0 ...oading-chunks-checking-hive-position.patch | 0 patches/unapplied/server/Anti-Xray.patch | 1456 ----------------- 16 files changed, 18 insertions(+), 1485 deletions(-) rename patches/{unapplied => }/api/Add-effect-to-block-break-naturally.patch (100%) rename patches/{unapplied => }/api/PlayerDeathEvent-shouldDropExperience.patch (100%) rename patches/{unapplied => }/server/Add-effect-to-block-break-naturally.patch (100%) rename patches/{unapplied => }/server/Don-t-load-Chunks-from-Hoppers-and-other-things.patch (100%) rename patches/{unapplied => }/server/Entity-Activation-Range-2.0.patch (97%) rename patches/{unapplied => }/server/Fix-Light-Command.patch (100%) rename patches/{unapplied => }/server/Fix-items-not-falling-correctly.patch (100%) rename patches/{unapplied => }/server/Fix-last-firework-in-stack-not-having-effects-when-d.patch (100%) rename patches/{unapplied => }/server/Guard-against-serializing-mismatching-chunk-coordina.patch (74%) rename patches/{unapplied => }/server/Increase-Light-Queue-Size.patch (98%) rename patches/{unapplied => }/server/Lag-compensate-eating.patch (97%) rename patches/{unapplied => }/server/Optimise-IEntityAccess-getPlayerByUUID.patch (100%) rename patches/{unapplied => }/server/Optimize-call-to-getFluid-for-explosions.patch (100%) rename patches/{unapplied => }/server/PlayerDeathEvent-shouldDropExperience.patch (100%) rename patches/{unapplied => }/server/Prevent-bees-loading-chunks-checking-hive-position.patch (100%) delete mode 100644 patches/unapplied/server/Anti-Xray.patch diff --git a/patches/unapplied/api/Add-effect-to-block-break-naturally.patch b/patches/api/Add-effect-to-block-break-naturally.patch similarity index 100% rename from patches/unapplied/api/Add-effect-to-block-break-naturally.patch rename to patches/api/Add-effect-to-block-break-naturally.patch diff --git a/patches/unapplied/api/PlayerDeathEvent-shouldDropExperience.patch b/patches/api/PlayerDeathEvent-shouldDropExperience.patch similarity index 100% rename from patches/unapplied/api/PlayerDeathEvent-shouldDropExperience.patch rename to patches/api/PlayerDeathEvent-shouldDropExperience.patch diff --git a/patches/unapplied/server/Add-effect-to-block-break-naturally.patch b/patches/server/Add-effect-to-block-break-naturally.patch similarity index 100% rename from patches/unapplied/server/Add-effect-to-block-break-naturally.patch rename to patches/server/Add-effect-to-block-break-naturally.patch diff --git a/patches/unapplied/server/Don-t-load-Chunks-from-Hoppers-and-other-things.patch b/patches/server/Don-t-load-Chunks-from-Hoppers-and-other-things.patch similarity index 100% rename from patches/unapplied/server/Don-t-load-Chunks-from-Hoppers-and-other-things.patch rename to patches/server/Don-t-load-Chunks-from-Hoppers-and-other-things.patch diff --git a/patches/unapplied/server/Entity-Activation-Range-2.0.patch b/patches/server/Entity-Activation-Range-2.0.patch similarity index 97% rename from patches/unapplied/server/Entity-Activation-Range-2.0.patch rename to patches/server/Entity-Activation-Range-2.0.patch index bc99631130..e758275074 100644 --- a/patches/unapplied/server/Entity-Activation-Range-2.0.patch +++ b/patches/server/Entity-Activation-Range-2.0.patch @@ -33,14 +33,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 import it.unimi.dsi.fastutil.objects.ObjectIterator; import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -@@ -0,0 +0,0 @@ import net.minecraft.world.level.chunk.LevelChunkSection; - import net.minecraft.world.level.chunk.storage.EntityStorage; - import net.minecraft.world.level.dimension.DimensionType; - import net.minecraft.world.level.dimension.end.EndDragonFight; --import net.minecraft.world.level.entity.EntityAccess; - import net.minecraft.world.level.entity.EntityPersistentStorage; - import net.minecraft.world.level.entity.EntityTickList; - import net.minecraft.world.level.entity.EntityTypeTest; @@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel { ++TimingHistory.entityTicks; // Paper - timings // Spigot start @@ -127,7 +119,7 @@ diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/jav index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i public final org.spigotmc.ActivationRange.ActivationType activationType = org.spigotmc.ActivationRange.initializeEntityActivationType(this); public final boolean defaultActivationState; public long activatedTick = Integer.MIN_VALUE; @@ -136,7 +128,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one protected int numCollisions = 0; // Paper public void inactiveTick() { } -@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i } else { this.wasOnFire = this.isOnFire(); if (movementType == MoverType.PISTON) { @@ -145,7 +137,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 movement = this.limitPistonMovement(movement); if (movement.equals(Vec3.ZERO)) { return; -@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i this.stuckSpeedMultiplier = Vec3.ZERO; this.setDeltaMovement(Vec3.ZERO); } @@ -187,14 +179,14 @@ diff --git a/src/main/java/net/minecraft/world/entity/PathfinderMob.java b/src/m index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/entity/PathfinderMob.java +++ b/src/main/java/net/minecraft/world/entity/PathfinderMob.java -@@ -0,0 +0,0 @@ import org.bukkit.event.entity.EntityUnleashEvent; - public abstract class PathfinderMob extends Mob { +@@ -0,0 +0,0 @@ public abstract class PathfinderMob extends Mob { + } public org.bukkit.craftbukkit.entity.CraftCreature getBukkitCreature() { return (org.bukkit.craftbukkit.entity.CraftCreature) super.getBukkitEntity(); } // Paper + public BlockPos movingTarget = null; public BlockPos getMovingTarget() { return movingTarget; } // Paper - protected PathfinderMob(EntityType type, Level world) { - super(type, world); + public float getWalkTargetValue(BlockPos pos) { + return this.getWalkTargetValue(pos, this.level); diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java @@ -296,8 +288,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + protected void customServerAiStep() { mobTick(false); } + protected void mobTick(boolean inactive) { this.level.getProfiler().push("villagerBrain"); -- this.getBrain().tick((ServerLevel) this.level, this); // CraftBukkit - decompile error -+ if (!inactive) this.getBrain().tick((ServerLevel) this.level, this); // CraftBukkit - decompile error // Paper +- this.getBrain().tick((ServerLevel) this.level, this); ++ if (!inactive) this.getBrain().tick((ServerLevel) this.level, this); // Paper this.level.getProfiler().pop(); if (this.assignProfessionWhenSpawned) { this.assignProfessionWhenSpawned = false; diff --git a/patches/unapplied/server/Fix-Light-Command.patch b/patches/server/Fix-Light-Command.patch similarity index 100% rename from patches/unapplied/server/Fix-Light-Command.patch rename to patches/server/Fix-Light-Command.patch diff --git a/patches/unapplied/server/Fix-items-not-falling-correctly.patch b/patches/server/Fix-items-not-falling-correctly.patch similarity index 100% rename from patches/unapplied/server/Fix-items-not-falling-correctly.patch rename to patches/server/Fix-items-not-falling-correctly.patch diff --git a/patches/unapplied/server/Fix-last-firework-in-stack-not-having-effects-when-d.patch b/patches/server/Fix-last-firework-in-stack-not-having-effects-when-d.patch similarity index 100% rename from patches/unapplied/server/Fix-last-firework-in-stack-not-having-effects-when-d.patch rename to patches/server/Fix-last-firework-in-stack-not-having-effects-when-d.patch diff --git a/patches/unapplied/server/Guard-against-serializing-mismatching-chunk-coordina.patch b/patches/server/Guard-against-serializing-mismatching-chunk-coordina.patch similarity index 74% rename from patches/unapplied/server/Guard-against-serializing-mismatching-chunk-coordina.patch rename to patches/server/Guard-against-serializing-mismatching-chunk-coordina.patch index f9dcb477ab..938ca9fd0d 100644 --- a/patches/unapplied/server/Guard-against-serializing-mismatching-chunk-coordina.patch +++ b/patches/server/Guard-against-serializing-mismatching-chunk-coordina.patch @@ -16,24 +16,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper start - guard against serializing mismatching coordinates + // TODO Note: This needs to be re-checked each update + public static ChunkPos getChunkCoordinate(CompoundTag chunkData) { -+ CompoundTag levelData = chunkData.getCompound("Level"); -+ return new ChunkPos(levelData.getInt("xPos"), levelData.getInt("zPos")); ++ return new ChunkPos(chunkData.getInt("xPos"), chunkData.getInt("zPos")); + } + // Paper end // Paper start public static final class InProgressChunkHolder { @@ -0,0 +0,0 @@ public class ChunkSerializer { + public static InProgressChunkHolder loadChunk(ServerLevel world, PoiManager poiStorage, ChunkPos chunkPos, CompoundTag nbt, boolean distinguish) { + java.util.ArrayDeque tasksToExecuteOnMain = new java.util.ArrayDeque<>(); // Paper end - ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator(); - BiomeSource worldchunkmanager = chunkgenerator.getBiomeSource(); -- CompoundTag nbttagcompound1 = nbt.getCompound("Level"); -- ChunkPos chunkcoordintpair1 = new ChunkPos(nbttagcompound1.getInt("xPos"), nbttagcompound1.getInt("zPos")); -+ CompoundTag nbttagcompound1 = nbt.getCompound("Level"); // Paper - diff on change, see ChunkSerializer#getChunkCoordinate -+ ChunkPos chunkcoordintpair1 = new ChunkPos(nbttagcompound1.getInt("xPos"), nbttagcompound1.getInt("zPos")); // Paper - diff on change, see ChunkSerializer#getChunkCoordinate +- ChunkPos chunkcoordintpair1 = new ChunkPos(nbt.getInt("xPos"), nbt.getInt("zPos")); ++ ChunkPos chunkcoordintpair1 = new ChunkPos(nbt.getInt("xPos"), nbt.getInt("zPos")); // Paper - diff on change, see ChunkSerializer#getChunkCoordinate - if (!Objects.equals(pos, chunkcoordintpair1)) { - ChunkSerializer.LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", pos, pos, chunkcoordintpair1); + if (!Objects.equals(chunkPos, chunkcoordintpair1)) { + ChunkSerializer.LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", chunkPos, chunkPos, chunkcoordintpair1); diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java diff --git a/patches/unapplied/server/Increase-Light-Queue-Size.patch b/patches/server/Increase-Light-Queue-Size.patch similarity index 98% rename from patches/unapplied/server/Increase-Light-Queue-Size.patch rename to patches/server/Increase-Light-Queue-Size.patch index 608baa8752..df18e4afe4 100644 --- a/patches/unapplied/server/Increase-Light-Queue-Size.patch +++ b/patches/server/Increase-Light-Queue-Size.patch @@ -39,5 +39,5 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 - chunkproviderserver.getLightEngine().setTaskPerBatch(5); + chunkproviderserver.getLightEngine().setTaskPerBatch(worldserver.paperConfig.lightQueueSize); // Paper - increase light queue size // CraftBukkit start - // this.updateSpawnFlags(); + // this.updateMobSpawningFlags(); worldserver.setSpawnSettings(this.isSpawningMonsters(), this.isSpawningAnimals()); diff --git a/patches/unapplied/server/Lag-compensate-eating.patch b/patches/server/Lag-compensate-eating.patch similarity index 97% rename from patches/unapplied/server/Lag-compensate-eating.patch rename to patches/server/Lag-compensate-eating.patch index 062a9220e2..ce8ce880bb 100644 --- a/patches/unapplied/server/Lag-compensate-eating.patch +++ b/patches/server/Lag-compensate-eating.patch @@ -48,7 +48,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper end if (!this.level.isClientSide) { this.setLivingEntityFlag(1, true); - this.setLivingEntityFlag(2, enumhand == InteractionHand.OFF_HAND); + this.setLivingEntityFlag(2, hand == InteractionHand.OFF_HAND); @@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity { } } else if (!this.isUsingItem() && !this.useItem.isEmpty()) { diff --git a/patches/unapplied/server/Optimise-IEntityAccess-getPlayerByUUID.patch b/patches/server/Optimise-IEntityAccess-getPlayerByUUID.patch similarity index 100% rename from patches/unapplied/server/Optimise-IEntityAccess-getPlayerByUUID.patch rename to patches/server/Optimise-IEntityAccess-getPlayerByUUID.patch diff --git a/patches/unapplied/server/Optimize-call-to-getFluid-for-explosions.patch b/patches/server/Optimize-call-to-getFluid-for-explosions.patch similarity index 100% rename from patches/unapplied/server/Optimize-call-to-getFluid-for-explosions.patch rename to patches/server/Optimize-call-to-getFluid-for-explosions.patch diff --git a/patches/unapplied/server/PlayerDeathEvent-shouldDropExperience.patch b/patches/server/PlayerDeathEvent-shouldDropExperience.patch similarity index 100% rename from patches/unapplied/server/PlayerDeathEvent-shouldDropExperience.patch rename to patches/server/PlayerDeathEvent-shouldDropExperience.patch diff --git a/patches/unapplied/server/Prevent-bees-loading-chunks-checking-hive-position.patch b/patches/server/Prevent-bees-loading-chunks-checking-hive-position.patch similarity index 100% rename from patches/unapplied/server/Prevent-bees-loading-chunks-checking-hive-position.patch rename to patches/server/Prevent-bees-loading-chunks-checking-hive-position.patch diff --git a/patches/unapplied/server/Anti-Xray.patch b/patches/unapplied/server/Anti-Xray.patch deleted file mode 100644 index affabf4b10..0000000000 --- a/patches/unapplied/server/Anti-Xray.patch +++ /dev/null @@ -1,1456 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: stonar96 -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -@@ -0,0 +0,0 @@ - package com.destroystokyo.paper; - -+import java.util.Arrays; - import java.util.List; - - 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; -@@ -0,0 +0,0 @@ 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 hiddenBlocks; -+ public List 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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java -@@ -0,0 +0,0 @@ -+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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java -@@ -0,0 +0,0 @@ -+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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java -@@ -0,0 +0,0 @@ -+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 getChunkPacketInfo(ClientboundLevelChunkPacket chunkPacket, LevelChunk chunk) { -+ return null; -+ } -+ -+ public void modifyBlocks(ClientboundLevelChunkPacket chunkPacket, ChunkPacketInfo 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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -0,0 +0,0 @@ -+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 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 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) constructor doesn't specify that the insertion order is the predictable iteration order of the specified Collection, although it is in the implementation -+ Set presetBlockStateSet = new LinkedHashSet<>(); -+ // Therefore addAll(Collection) 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 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 presetBlockStateBits = ThreadLocal.withInitial(() -> new int[getPresetBlockStatesFullLength()]); -+ private static final ThreadLocal solid = ThreadLocal.withInitial(() -> new boolean[Block.BLOCK_STATE_REGISTRY.size()]); -+ private static final ThreadLocal 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 current = ThreadLocal.withInitial(() -> new boolean[16][16]); -+ private static final ThreadLocal next = ThreadLocal.withInitial(() -> new boolean[16][16]); -+ private static final ThreadLocal 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 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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java -@@ -0,0 +0,0 @@ -+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 { -+ -+ 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 getPalette(int chunkSectionIndex) { -+ return (Palette) palettes[chunkSectionIndex]; -+ } -+ -+ public void setPalette(int chunkSectionIndex, Palette 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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java -@@ -0,0 +0,0 @@ -+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 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacket.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacket.java -@@ -0,0 +0,0 @@ public class ClientboundLevelChunkPacket implements Packet chunkPacketInfo = modifyBlocks ? chunk.level.chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null; -+ // Paper end - ChunkPos chunkPos = chunk.getPos(); - this.x = chunkPos.x; - this.z = chunkPos.z; -@@ -0,0 +0,0 @@ public class ClientboundLevelChunkPacket implements Packet chunkPacketInfo) { -+ // Paper end - BitSet bitSet = new BitSet(); - LevelChunkSection[] levelChunkSections = chunk.getSections(); - int i = 0; -@@ -0,0 +0,0 @@ public class ClientboundLevelChunkPacket implements 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -0,0 +0,0 @@ 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 resourcekey, DimensionType dimensionmanager, ChunkProgressListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -@@ -0,0 +0,0 @@ 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 -@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -0,0 +0,0 @@ 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 -@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - return this.typeKey; - } - -- protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, final DimensionType dimensionmanager, Supplier 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 resourcekey, final DimensionType dimensionmanager, Supplier 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; -@@ -0,0 +0,0 @@ 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 -@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java -@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -0,0 +0,0 @@ public class LevelChunkSection { - private short tickingFluidCount; - final PalettedContainer 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) { -@@ -0,0 +0,0 @@ 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 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java -+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize { - private final Function reader; - private final Function writer; - private final T defaultValue; -+ private final T[] presetValues; // Paper - Anti-Xray - Add preset values - protected BitStorage storage; - private Palette palette; - private int bits; -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize { - this.lock.release(); - } - -- public PalettedContainer(Palette fallbackPalette, IdMapper idList, Function elementDeserializer, Function elementSerializer, T defaultElement) { -+ // Paper start - Anti-Xray - Add preset values -+ @Deprecated public PalettedContainer(Palette fallbackPalette, IdMapper idList, Function elementDeserializer, Function 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 fallbackPalette, IdMapper idList, Function elementDeserializer, Function 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; -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize { - BitStorage bitStorage = this.storage; - Palette 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)); -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize { - - 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 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(); -@@ -0,0 +0,0 @@ public class PalettedContainer implements PaletteResize { - 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 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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 -@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -@@ -0,0 +0,0 @@ public class CraftChunk implements Chunk { - private final ServerLevel worldServer; - private final int x; - private final int z; -- private static final PalettedContainer emptyBlockIDs = new LevelChunkSection(0).getStates(); -+ private static final PalettedContainer 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) { -@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java -+++ b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java -@@ -0,0 +0,0 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData { - private final LevelChunkSection[] sections; - private Set tiles; - private final Set 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) { -@@ -0,0 +0,0 @@ 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; - }