From bd232d80dacf2daae04f90143c4ae4ea8a7ba2aa Mon Sep 17 00:00:00 2001 From: stonar96 Date: Sun, 26 Aug 2018 04:37:58 +0200 Subject: [PATCH] 1.13: Readd Anti-Xray (#1366) * Decompile fixes for ProtoChunk and DataPaletteBlock * Do changes * final change * more changes * First Anti-Xray build * Fixes and cleanups * Cleanups and renamings * Cleanups renamings and move OBFHELPERs to 0004-MC-Utils.patch * Implement obfuscation for newly generated chunks * Implement chunk edge mode * Implement block updates * Cleanups * Rebuild patches --- Spigot-Server-Patches/0003-MC-Dev-fixes.patch | 110 +- Spigot-Server-Patches/0004-MC-Utils.patch | 208 ++- ...le-Explicit-Network-Manager-Flushing.patch | 8 +- ...ient-rendering-skulls-from-same-user.patch | 8 +- Spigot-Server-Patches/0348-Anti-Xray.patch | 1581 +++++++++++++++++ 5 files changed, 1874 insertions(+), 41 deletions(-) create mode 100644 Spigot-Server-Patches/0348-Anti-Xray.patch diff --git a/Spigot-Server-Patches/0003-MC-Dev-fixes.patch b/Spigot-Server-Patches/0003-MC-Dev-fixes.patch index c7656f4342..8a9bdc41a8 100644 --- a/Spigot-Server-Patches/0003-MC-Dev-fixes.patch +++ b/Spigot-Server-Patches/0003-MC-Dev-fixes.patch @@ -1,11 +1,11 @@ -From 3d58bb7e7759377a0a59a8cf388372f8918dea2f Mon Sep 17 00:00:00 2001 +From 6691919def0c0a2eb8baccb043701888ab8b2044 Mon Sep 17 00:00:00 2001 From: Aikar Date: Wed, 30 Mar 2016 19:36:20 -0400 Subject: [PATCH] MC Dev fixes diff --git a/src/main/java/net/minecraft/server/BlockPosition.java b/src/main/java/net/minecraft/server/BlockPosition.java -index 002da2a191..9f3aa24590 100644 +index 002da2a19..9f3aa2459 100644 --- a/src/main/java/net/minecraft/server/BlockPosition.java +++ b/src/main/java/net/minecraft/server/BlockPosition.java @@ -150,7 +150,7 @@ public class BlockPosition extends BaseBlockPosition { @@ -59,7 +59,7 @@ index 002da2a191..9f3aa24590 100644 } diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java -index 650ef14758..35aea4829f 100644 +index 650ef1475..35aea4829 100644 --- a/src/main/java/net/minecraft/server/ChunkSection.java +++ b/src/main/java/net/minecraft/server/ChunkSection.java @@ -15,7 +15,7 @@ public class ChunkSection { @@ -71,8 +71,47 @@ index 650ef14758..35aea4829f 100644 this.emittedLight = new NibbleArray(); if (flag) { this.skyLight = new NibbleArray(); +diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java +index 20b1bc1b4..030ec8704 100644 +--- a/src/main/java/net/minecraft/server/DataPaletteBlock.java ++++ b/src/main/java/net/minecraft/server/DataPaletteBlock.java +@@ -75,14 +75,14 @@ public class DataPaletteBlock implements DataPaletteExpandable { + public int onResize(int i, T t0) { + this.b(); + DataBits databits = this.a; +- DataPalette datapalette = this.h; ++ DataPalette datapalette = this.h; // Paper - decompile fix + + this.b(i); + + int j; + + for (j = 0; j < databits.b(); ++j) { +- Object object = datapalette.a(databits.a(j)); ++ T object = datapalette.a(databits.a(j)); // Paper - decompile fix + + if (object != null) { + this.setBlockIndex(j, object); +@@ -111,7 +111,7 @@ public class DataPaletteBlock implements DataPaletteExpandable { + } + + protected T a(int i) { +- Object object = this.h.a(this.a.a(i)); ++ T object = this.h.a(this.a.a(i)); // Paper - decompile fix + + return object == null ? this.g : object; + } +@@ -138,7 +138,7 @@ public class DataPaletteBlock implements DataPaletteExpandable { + int j = along.length * 64 / 4096; + + if (this.h == this.b) { +- DataPaletteHash datapalettehash = new DataPaletteHash(this.d, i, this.c, this.e, this.f); ++ DataPaletteHash datapalettehash = new DataPaletteHash<>(this.d, i, this.c, this.e, this.f); // Paper - decompile fix + + datapalettehash.a(nbttaglist); + DataBits databits = new DataBits(i, 4096, along); diff --git a/src/main/java/net/minecraft/server/DedicatedPlayerList.java b/src/main/java/net/minecraft/server/DedicatedPlayerList.java -index 4997255c79..a5545bd4c4 100644 +index 4997255c7..a5545bd4c 100644 --- a/src/main/java/net/minecraft/server/DedicatedPlayerList.java +++ b/src/main/java/net/minecraft/server/DedicatedPlayerList.java @@ -135,8 +135,4 @@ public class DedicatedPlayerList extends PlayerList { @@ -85,7 +124,7 @@ index 4997255c79..a5545bd4c4 100644 - } } diff --git a/src/main/java/net/minecraft/server/DefinedStructure.java b/src/main/java/net/minecraft/server/DefinedStructure.java -index a661789c1e..785a1a2184 100644 +index a661789c1..785a1a218 100644 --- a/src/main/java/net/minecraft/server/DefinedStructure.java +++ b/src/main/java/net/minecraft/server/DefinedStructure.java @@ -88,7 +88,7 @@ public class DefinedStructure { @@ -140,7 +179,7 @@ index a661789c1e..785a1a2184 100644 public Iterator iterator() { diff --git a/src/main/java/net/minecraft/server/EnumDirection.java b/src/main/java/net/minecraft/server/EnumDirection.java -index 188d49d820..854ad49b6c 100644 +index 188d49d82..854ad49b6 100644 --- a/src/main/java/net/minecraft/server/EnumDirection.java +++ b/src/main/java/net/minecraft/server/EnumDirection.java @@ -255,7 +255,7 @@ public enum EnumDirection implements INamable { @@ -204,7 +243,7 @@ index 188d49d820..854ad49b6c 100644 } diff --git a/src/main/java/net/minecraft/server/LocaleLanguage.java b/src/main/java/net/minecraft/server/LocaleLanguage.java -index 8f06c58485..4361b2cee5 100644 +index 8f06c5848..4361b2cee 100644 --- a/src/main/java/net/minecraft/server/LocaleLanguage.java +++ b/src/main/java/net/minecraft/server/LocaleLanguage.java @@ -34,7 +34,7 @@ public class LocaleLanguage { @@ -217,7 +256,7 @@ index 8f06c58485..4361b2cee5 100644 this.e = SystemUtils.b(); diff --git a/src/main/java/net/minecraft/server/MathHelper.java b/src/main/java/net/minecraft/server/MathHelper.java -index 86585a13ff..f52b9c6f0a 100644 +index 86585a13f..f52b9c6f0 100644 --- a/src/main/java/net/minecraft/server/MathHelper.java +++ b/src/main/java/net/minecraft/server/MathHelper.java @@ -8,7 +8,7 @@ import java.util.function.IntPredicate; @@ -229,8 +268,57 @@ index 86585a13ff..f52b9c6f0a 100644 for (int i = 0; i < afloat.length; ++i) { afloat[i] = (float) Math.sin((double) i * 3.141592653589793D * 2.0D / 65536.0D); } +diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java +index 824e68b50..584a86c11 100644 +--- a/src/main/java/net/minecraft/server/ProtoChunk.java ++++ b/src/main/java/net/minecraft/server/ProtoChunk.java +@@ -65,7 +65,7 @@ public class ProtoChunk implements IChunkAccess { + this.b = chunkcoordintpair; + this.p = chunkconverter; + Predicate predicate = (block) -> { +- return block == null || block.getBlockData().isAir(); ++ return block == null || ((Block) block).getBlockData().isAir(); // Paper - decompile fix + }; + RegistryBlocks registryblocks = Block.REGISTRY; + +@@ -325,8 +325,8 @@ public class ProtoChunk implements IChunkAccess { + } + + private HeightMap c(HeightMap.Type heightmap_type) { +- return (HeightMap) this.f.computeIfAbsent(heightmap_type, (heightmap_type) -> { +- HeightMap heightmap = new HeightMap(this, heightmap_type); ++ return (HeightMap) this.f.computeIfAbsent(heightmap_type, (heightmap_type1) -> { // Paper - decompile fix ++ HeightMap heightmap = new HeightMap(this, heightmap_type1); // Paper - decompile fix + + heightmap.a(); + return heightmap; +@@ -372,13 +372,13 @@ public class ProtoChunk implements IChunkAccess { + + @Nullable + public LongSet b(String s) { +- return (LongSet) this.o.computeIfAbsent(s, (s) -> { ++ return (LongSet) this.o.computeIfAbsent(s, (s1) -> { // Paper - decompile fix + return new LongOpenHashSet(); + }); + } + + public void a(String s, long i) { +- ((LongSet) this.o.computeIfAbsent(s, (s) -> { ++ ((LongSet) this.o.computeIfAbsent(s, (s1) -> { // Paper - decompile fix + return new LongOpenHashSet(); + })).add(i); + this.c = true; +@@ -493,7 +493,7 @@ public class ProtoChunk implements IChunkAccess { + } + + public BitSet a(WorldGenStage.Features worldgenstage_features) { +- return (BitSet) this.t.computeIfAbsent(worldgenstage_features, (worldgenstage_features) -> { ++ return (BitSet) this.t.computeIfAbsent(worldgenstage_features, (worldgenstage_features1) -> { // Paper - decompile fix + return new BitSet(65536); + }); + } diff --git a/src/main/java/net/minecraft/server/RegistryID.java b/src/main/java/net/minecraft/server/RegistryID.java -index 3b8f6ec167..bde5714dd6 100644 +index 3b8f6ec16..bde5714dd 100644 --- a/src/main/java/net/minecraft/server/RegistryID.java +++ b/src/main/java/net/minecraft/server/RegistryID.java @@ -6,7 +6,7 @@ import java.util.Arrays; @@ -271,7 +359,7 @@ index 3b8f6ec167..bde5714dd6 100644 this.f = 0; diff --git a/src/main/java/net/minecraft/server/ShapeDetector.java b/src/main/java/net/minecraft/server/ShapeDetector.java -index 43596cb2d5..3faf74a22f 100644 +index 43596cb2d..3faf74a22 100644 --- a/src/main/java/net/minecraft/server/ShapeDetector.java +++ b/src/main/java/net/minecraft/server/ShapeDetector.java @@ -171,7 +171,7 @@ public class ShapeDetector { @@ -284,7 +372,7 @@ index 43596cb2d5..3faf74a22f 100644 } } diff --git a/src/main/java/net/minecraft/server/VoxelShape.java b/src/main/java/net/minecraft/server/VoxelShape.java -index 4b5463cca2..53c9f21887 100644 +index 4b5463cca..53c9f2188 100644 --- a/src/main/java/net/minecraft/server/VoxelShape.java +++ b/src/main/java/net/minecraft/server/VoxelShape.java @@ -67,7 +67,7 @@ public abstract class VoxelShape { diff --git a/Spigot-Server-Patches/0004-MC-Utils.patch b/Spigot-Server-Patches/0004-MC-Utils.patch index 953549e2ba..536c19ade9 100644 --- a/Spigot-Server-Patches/0004-MC-Utils.patch +++ b/Spigot-Server-Patches/0004-MC-Utils.patch @@ -1,11 +1,11 @@ -From 42457b83f3c35c1e566f635b53505bc0c53f1eaf Mon Sep 17 00:00:00 2001 +From b78498d654658ee40447dbcbef775b255505987f Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 28 Mar 2016 20:55:47 -0400 Subject: [PATCH] MC Utils diff --git a/src/main/java/net/minecraft/server/BaseBlockPosition.java b/src/main/java/net/minecraft/server/BaseBlockPosition.java -index c3e990bdff..e2a7b4be2c 100644 +index c3e990bd..e2a7b4be 100644 --- a/src/main/java/net/minecraft/server/BaseBlockPosition.java +++ b/src/main/java/net/minecraft/server/BaseBlockPosition.java @@ -93,7 +93,7 @@ public class BaseBlockPosition implements Comparable { @@ -18,7 +18,7 @@ index c3e990bdff..e2a7b4be2c 100644 } } diff --git a/src/main/java/net/minecraft/server/BlockPosition.java b/src/main/java/net/minecraft/server/BlockPosition.java -index 9f3aa24590..7dbea90902 100644 +index 9f3aa245..7dbea909 100644 --- a/src/main/java/net/minecraft/server/BlockPosition.java +++ b/src/main/java/net/minecraft/server/BlockPosition.java @@ -10,7 +10,6 @@ import org.apache.logging.log4j.Logger; @@ -54,7 +54,7 @@ index 9f3aa24590..7dbea90902 100644 return this.c(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2)); } diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index 03c96de98b..2d06202da7 100644 +index 03c96de9..2d06202d 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -28,7 +28,7 @@ import com.google.common.collect.Lists; // CraftBukkit @@ -75,7 +75,7 @@ index 03c96de98b..2d06202da7 100644 public TileEntity a(BlockPosition blockposition, Chunk.EnumTileEntityState chunk_enumtileentitystate) { // CraftBukkit start diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java -index 00a530c51c..2947d9ff6a 100644 +index 00a530c5..2947d9ff 100644 --- a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java +++ b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java @@ -24,6 +24,8 @@ public class ChunkCoordIntPair { @@ -87,8 +87,90 @@ index 00a530c51c..2947d9ff6a 100644 public static long a(int i, int j) { return (long) i & 4294967295L | ((long) j & 4294967295L) << 32; } +diff --git a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java +index 5d9118da..8b50ec53 100644 +--- a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java ++++ b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java +@@ -15,7 +15,7 @@ import org.apache.logging.log4j.Logger; + public class ChunkTaskScheduler extends Scheduler { + + private static final Logger b = LogManager.getLogger(); +- private final World c; ++ private final World c; private final World getWorld() { return this.c; } // Paper - OBFHELPER + private final ChunkGenerator d; + private final IChunkLoader e; + private final IAsyncTaskHandler f; +diff --git a/src/main/java/net/minecraft/server/DataBits.java b/src/main/java/net/minecraft/server/DataBits.java +index d24ec3d5..9e83610f 100644 +--- a/src/main/java/net/minecraft/server/DataBits.java ++++ b/src/main/java/net/minecraft/server/DataBits.java +@@ -60,6 +60,7 @@ public class DataBits { + } + } + ++ public long[] getDataBits() { return this.a(); } // Paper - OBFHELPER + public long[] a() { + return this.a; + } +diff --git a/src/main/java/net/minecraft/server/DataPalette.java b/src/main/java/net/minecraft/server/DataPalette.java +index dae40b9c..0be0b0ef 100644 +--- a/src/main/java/net/minecraft/server/DataPalette.java ++++ b/src/main/java/net/minecraft/server/DataPalette.java +@@ -4,8 +4,10 @@ import javax.annotation.Nullable; + + public interface DataPalette { + ++ default int getDataBits(T object) { return this.a(object); } // Paper - OBFHELPER + int a(T t0); + ++ @Nullable default T getObject(int dataBits) { return this.a(dataBits); } // Paper - OBFHELPER + @Nullable + T a(int i); + +diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java +index 030ec870..575f9b55 100644 +--- a/src/main/java/net/minecraft/server/DataPaletteBlock.java ++++ b/src/main/java/net/minecraft/server/DataPaletteBlock.java +@@ -9,7 +9,7 @@ import java.util.stream.Collectors; + + public class DataPaletteBlock implements DataPaletteExpandable { + +- private final DataPalette b; ++ private final DataPalette b; private final DataPalette getDataPaletteGlobal() { return this.b; } // Paper - OBFHELPER + private final DataPaletteExpandable c = (i, object) -> { + return 0; + }; +@@ -17,9 +17,9 @@ public class DataPaletteBlock implements DataPaletteExpandable { + private final Function e; + private final Function f; + private final T g; +- protected DataBits a; +- private DataPalette h; +- private int i; ++ protected DataBits a; protected DataBits getDataBits() { return this.a; } // Paper - OBFHELPER ++ private DataPalette h; private DataPalette getDataPalette() { return this.h; } // Paper - OBFHELPER ++ private int i; private int getBitsPerObject() { return this.i; } // Paper - OBFHELPER + private final ReentrantLock j = new ReentrantLock(); + + private void b() { +@@ -54,6 +54,7 @@ public class DataPaletteBlock implements DataPaletteExpandable { + return j << 8 | k << 4 | i; + } + ++ private void initialize(int bitsPerObject) { this.b(bitsPerObject); } // Paper - OBFHELPER + private void b(int i) { + if (i != this.i) { + this.i = i; +@@ -116,6 +117,7 @@ public class DataPaletteBlock implements DataPaletteExpandable { + return object == null ? this.g : object; + } + ++ public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } // Paper - OBFHELPER + public void b(PacketDataSerializer packetdataserializer) { + this.b(); + packetdataserializer.writeByte(this.i); diff --git a/src/main/java/net/minecraft/server/EntityCreature.java b/src/main/java/net/minecraft/server/EntityCreature.java -index b347567699..ceed50d8de 100644 +index b3475676..ceed50d8 100644 --- a/src/main/java/net/minecraft/server/EntityCreature.java +++ b/src/main/java/net/minecraft/server/EntityCreature.java @@ -6,6 +6,7 @@ import org.bukkit.event.entity.EntityUnleashEvent; @@ -100,7 +182,7 @@ index b347567699..ceed50d8de 100644 private float b; diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 514c951516..5b33997529 100644 +index 514c9515..5b339975 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -122,6 +122,7 @@ public abstract class EntityLiving extends Entity { @@ -112,7 +194,7 @@ index 514c951516..5b33997529 100644 @Override public float getBukkitYaw() { diff --git a/src/main/java/net/minecraft/server/EntityMonster.java b/src/main/java/net/minecraft/server/EntityMonster.java -index 494ce0bc06..04c3756ea3 100644 +index 494ce0bc..04c3756e 100644 --- a/src/main/java/net/minecraft/server/EntityMonster.java +++ b/src/main/java/net/minecraft/server/EntityMonster.java @@ -2,6 +2,7 @@ package net.minecraft.server; @@ -124,7 +206,7 @@ index 494ce0bc06..04c3756ea3 100644 super(entitytypes, world); this.b_ = 5; diff --git a/src/main/java/net/minecraft/server/EntityTypes.java b/src/main/java/net/minecraft/server/EntityTypes.java -index ca2a14d7ac..9a513b4e3a 100644 +index ca2a14d7..9a513b4e 100644 --- a/src/main/java/net/minecraft/server/EntityTypes.java +++ b/src/main/java/net/minecraft/server/EntityTypes.java @@ -3,6 +3,7 @@ package net.minecraft.server; @@ -167,7 +249,7 @@ index ca2a14d7ac..9a513b4e3a 100644 private boolean c = true; private boolean d = true; diff --git a/src/main/java/net/minecraft/server/ItemStack.java b/src/main/java/net/minecraft/server/ItemStack.java -index 359979cc1c..0c54eaa529 100644 +index 359979cc..0c54eaa5 100644 --- a/src/main/java/net/minecraft/server/ItemStack.java +++ b/src/main/java/net/minecraft/server/ItemStack.java @@ -26,6 +26,7 @@ import org.bukkit.TreeType; @@ -197,7 +279,7 @@ index 359979cc1c..0c54eaa529 100644 this.tag = nbttagcompound; } diff --git a/src/main/java/net/minecraft/server/LotoSelectorEntry.java b/src/main/java/net/minecraft/server/LotoSelectorEntry.java -index a540167d6d..add618866b 100644 +index a540167d..add61886 100644 --- a/src/main/java/net/minecraft/server/LotoSelectorEntry.java +++ b/src/main/java/net/minecraft/server/LotoSelectorEntry.java @@ -85,11 +85,11 @@ public abstract class LotoSelectorEntry { @@ -216,7 +298,7 @@ index a540167d6d..add618866b 100644 } diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java new file mode 100644 -index 0000000000..70cdc3f102 +index 00000000..70cdc3f1 --- /dev/null +++ b/src/main/java/net/minecraft/server/MCUtil.java @@ -0,0 +1,292 @@ @@ -513,7 +595,7 @@ index 0000000000..70cdc3f102 + } +} diff --git a/src/main/java/net/minecraft/server/NBTBase.java b/src/main/java/net/minecraft/server/NBTBase.java -index 8170a84280..e21e60b003 100644 +index 8170a842..e21e60b0 100644 --- a/src/main/java/net/minecraft/server/NBTBase.java +++ b/src/main/java/net/minecraft/server/NBTBase.java @@ -20,7 +20,7 @@ public interface NBTBase { @@ -535,7 +617,7 @@ index 8170a84280..e21e60b003 100644 case 0: return "TAG_End"; diff --git a/src/main/java/net/minecraft/server/NBTList.java b/src/main/java/net/minecraft/server/NBTList.java -index 1a81d8e5f6..057c2077a0 100644 +index 1a81d8e5..057c2077 100644 --- a/src/main/java/net/minecraft/server/NBTList.java +++ b/src/main/java/net/minecraft/server/NBTList.java @@ -13,7 +13,7 @@ public abstract class NBTList extends AbstractList impleme @@ -561,7 +643,7 @@ index 1a81d8e5f6..057c2077a0 100644 + public abstract NBTList clone(); // Paper - decompile fix } diff --git a/src/main/java/net/minecraft/server/NBTTagByteArray.java b/src/main/java/net/minecraft/server/NBTTagByteArray.java -index 11ffa6c342..3ff3a29835 100644 +index 11ffa6c3..3ff3a298 100644 --- a/src/main/java/net/minecraft/server/NBTTagByteArray.java +++ b/src/main/java/net/minecraft/server/NBTTagByteArray.java @@ -66,7 +66,8 @@ public class NBTTagByteArray extends NBTList { @@ -575,7 +657,7 @@ index 11ffa6c342..3ff3a29835 100644 System.arraycopy(this.data, 0, abyte, 0, this.data.length); diff --git a/src/main/java/net/minecraft/server/NBTTagCompound.java b/src/main/java/net/minecraft/server/NBTTagCompound.java -index 7fc9b5ff32..e658816c24 100644 +index 7fc9b5ff..e658816c 100644 --- a/src/main/java/net/minecraft/server/NBTTagCompound.java +++ b/src/main/java/net/minecraft/server/NBTTagCompound.java @@ -24,7 +24,7 @@ public class NBTTagCompound implements NBTBase { @@ -611,7 +693,7 @@ index 7fc9b5ff32..e658816c24 100644 - } } diff --git a/src/main/java/net/minecraft/server/NBTTagIntArray.java b/src/main/java/net/minecraft/server/NBTTagIntArray.java -index f5c9b97d5c..d121ad4f7a 100644 +index f5c9b97d..d121ad4f 100644 --- a/src/main/java/net/minecraft/server/NBTTagIntArray.java +++ b/src/main/java/net/minecraft/server/NBTTagIntArray.java @@ -132,7 +132,7 @@ public class NBTTagIntArray extends NBTList { @@ -624,7 +706,7 @@ index f5c9b97d5c..d121ad4f7a 100644 } } diff --git a/src/main/java/net/minecraft/server/NBTTagList.java b/src/main/java/net/minecraft/server/NBTTagList.java -index b3c944d701..a8280acf94 100644 +index b3c944d7..a8280acf 100644 --- a/src/main/java/net/minecraft/server/NBTTagList.java +++ b/src/main/java/net/minecraft/server/NBTTagList.java @@ -14,7 +14,7 @@ import org.apache.logging.log4j.Logger; @@ -661,8 +743,82 @@ index b3c944d701..a8280acf94 100644 - return this.add((NBTBase) object); - } } +diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java +index 0afaea81..26da8972 100644 +--- a/src/main/java/net/minecraft/server/NetworkManager.java ++++ b/src/main/java/net/minecraft/server/NetworkManager.java +@@ -44,7 +44,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build()); + }); + private final EnumProtocolDirection h; +- private final Queue i = Queues.newConcurrentLinkedQueue(); ++ private final Queue i = Queues.newConcurrentLinkedQueue(); private final Queue getPacketQueue() { return this.i; } // Paper - OBFHELPER + private final ReentrantReadWriteLock j = new ReentrantReadWriteLock(); + public Channel channel; + // Spigot Start // PAIL +@@ -168,6 +168,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + + } + ++ private void dispatchPacket(Packet packet, @Nullable GenericFutureListener> genericFutureListener) { this.b(packet, genericFutureListener); } // Paper - OBFHELPER + private void b(Packet packet, @Nullable GenericFutureListener> genericfuturelistener) { + EnumProtocol enumprotocol = EnumProtocol.a(packet); + EnumProtocol enumprotocol1 = (EnumProtocol) this.channel.attr(NetworkManager.c).get(); +@@ -208,6 +209,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + + } + ++ private void sendPacketQueue() { this.o(); } // Paper - OBFHELPER + private void o() { + if (this.channel != null && this.channel.isOpen()) { + this.j.readLock().lock(); +@@ -338,9 +340,9 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + + static class QueuedPacket { + +- private final Packet a; ++ private final Packet a; private final Packet getPacket() { return this.a; } // Paper - OBFHELPER + @Nullable +- private final GenericFutureListener> b; ++ private final GenericFutureListener> b; private final GenericFutureListener> getGenericFutureListener() { return this.b; } // Paper - OBFHELPER + + public QueuedPacket(Packet packet, @Nullable GenericFutureListener> genericfuturelistener) { + this.a = packet; +diff --git a/src/main/java/net/minecraft/server/PacketDataSerializer.java b/src/main/java/net/minecraft/server/PacketDataSerializer.java +index d04afceb..a63a5811 100644 +--- a/src/main/java/net/minecraft/server/PacketDataSerializer.java ++++ b/src/main/java/net/minecraft/server/PacketDataSerializer.java +@@ -33,6 +33,7 @@ public class PacketDataSerializer extends ByteBuf { + this.a = bytebuf; + } + ++ public static int countBytes(int i) { return PacketDataSerializer.a(i); } // Paper - OBFHELPER + public static int a(int i) { + for (int j = 1; j < 5; ++j) { + if ((i & -1 << j * 7) == 0) { +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java +index 1aa36156..ae95fdf9 100644 +--- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java ++++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java +@@ -13,7 +13,7 @@ public class PacketPlayOutMapChunk implements Packet { + private int a; + private int b; + private int c; +- private byte[] d; ++ private byte[] d; private byte[] getData() { return this.d; } // Paper - OBFHELPER + private List e; + private boolean f; + +@@ -97,6 +97,7 @@ public class PacketPlayOutMapChunk implements Packet { + return bytebuf; + } + ++ public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } // Paper - OBFHELPER + public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i) { + int j = 0; + ChunkSection[] achunksection = chunk.getSections(); diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index 0618962e75..42e0630e60 100644 +index 0618962e..42e0630e 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -66,9 +66,9 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable { @@ -679,7 +835,7 @@ index 0618962e75..42e0630e60 100644 private volatile int chatThrottle; private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle"); diff --git a/src/main/java/net/minecraft/server/RegistryBlockID.java b/src/main/java/net/minecraft/server/RegistryBlockID.java -index ef332d6517..7cc7eb7735 100644 +index ef332d65..a804a67e 100644 --- a/src/main/java/net/minecraft/server/RegistryBlockID.java +++ b/src/main/java/net/minecraft/server/RegistryBlockID.java @@ -8,7 +8,7 @@ import java.util.Iterator; @@ -700,8 +856,16 @@ index ef332d6517..7cc7eb7735 100644 } this.c.set(i, t0); +@@ -56,6 +56,7 @@ public class RegistryBlockID implements Registry { + return Iterators.filter(this.c.iterator(), Predicates.notNull()); + } + ++ public int size() { return this.a(); } // Paper - OBFHELPER + public int a() { + return this.b.size(); + } diff --git a/src/main/java/net/minecraft/server/ServerPing.java b/src/main/java/net/minecraft/server/ServerPing.java -index 2179664a0c..d7e1ecc031 100644 +index 2179664a..d7e1ecc0 100644 --- a/src/main/java/net/minecraft/server/ServerPing.java +++ b/src/main/java/net/minecraft/server/ServerPing.java @@ -102,11 +102,11 @@ public class ServerPing { @@ -747,5 +911,5 @@ index 2179664a0c..d7e1ecc031 100644 } } -- -2.18.0 +2.14.1.windows.1 diff --git a/Spigot-Server-Patches/0241-Disable-Explicit-Network-Manager-Flushing.patch b/Spigot-Server-Patches/0241-Disable-Explicit-Network-Manager-Flushing.patch index cbea1821cc..c822b032ad 100644 --- a/Spigot-Server-Patches/0241-Disable-Explicit-Network-Manager-Flushing.patch +++ b/Spigot-Server-Patches/0241-Disable-Explicit-Network-Manager-Flushing.patch @@ -1,4 +1,4 @@ -From 1e1413f50f5a67afcf8a7a2d53f85af1b349aa32 Mon Sep 17 00:00:00 2001 +From 1c884ab552ea64dad0dd97cecd57f6eb71c5074e Mon Sep 17 00:00:00 2001 From: Aikar Date: Sun, 11 Mar 2018 14:13:33 -0400 Subject: [PATCH] Disable Explicit Network Manager Flushing @@ -12,7 +12,7 @@ flushing on the netty event loop, so it won't do the flush on the main thread. Renable flushing by passing -Dpaper.explicit-flush=true diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java -index 61f9eb3e64..2272f1239b 100644 +index 4f3f88ff..f8facddb 100644 --- a/src/main/java/net/minecraft/server/NetworkManager.java +++ b/src/main/java/net/minecraft/server/NetworkManager.java @@ -66,6 +66,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { @@ -23,7 +23,7 @@ index 61f9eb3e64..2272f1239b 100644 // Paper end public NetworkManager(EnumProtocolDirection enumprotocoldirection) { -@@ -236,7 +237,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { +@@ -238,7 +239,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { } if (this.channel != null) { @@ -33,5 +33,5 @@ index 61f9eb3e64..2272f1239b 100644 if (this.u++ % 20 == 0) { -- -2.18.0 +2.14.1.windows.1 diff --git a/Spigot-Server-Patches/0332-Fix-client-rendering-skulls-from-same-user.patch b/Spigot-Server-Patches/0332-Fix-client-rendering-skulls-from-same-user.patch index 182049b296..8d3f16844a 100644 --- a/Spigot-Server-Patches/0332-Fix-client-rendering-skulls-from-same-user.patch +++ b/Spigot-Server-Patches/0332-Fix-client-rendering-skulls-from-same-user.patch @@ -28,7 +28,7 @@ diff --git a/src/main/java/net/minecraft/server/PacketDataSerializer.java b/src/ index d04afceb7..b0e8ffc4b 100644 --- a/src/main/java/net/minecraft/server/PacketDataSerializer.java +++ b/src/main/java/net/minecraft/server/PacketDataSerializer.java -@@ -251,6 +251,15 @@ public class PacketDataSerializer extends ByteBuf { +@@ -252,6 +252,15 @@ public class PacketDataSerializer extends ByteBuf { CraftItemStack.setItemMeta(itemstack, CraftItemStack.getItemMeta(itemstack)); // Spigot end nbttagcompound = itemstack.getTag(); @@ -44,7 +44,7 @@ index d04afceb7..b0e8ffc4b 100644 } this.a(nbttagcompound); -@@ -271,6 +280,16 @@ public class PacketDataSerializer extends ByteBuf { +@@ -272,6 +281,16 @@ public class PacketDataSerializer extends ByteBuf { itemstack.setTag(this.j()); // CraftBukkit start if (itemstack.getTag() != null) { @@ -73,7 +73,7 @@ index 1aa36156a..c5f0b8530 100644 this.e.add(nbttagcompound); } -@@ -121,7 +122,7 @@ public class PacketPlayOutMapChunk implements Packet { +@@ -122,7 +123,7 @@ public class PacketPlayOutMapChunk implements Packet { BiomeBase[] abiomebase = chunk.getBiomeIndex(); for (l = 0; l < abiomebase.length; ++l) { @@ -126,5 +126,5 @@ index 85fd8dab6..16a5537ce 100644 public NBTTagCompound aa_() { -- -2.18.0 +2.14.1.windows.1 diff --git a/Spigot-Server-Patches/0348-Anti-Xray.patch b/Spigot-Server-Patches/0348-Anti-Xray.patch new file mode 100644 index 0000000000..a196e4b85f --- /dev/null +++ b/Spigot-Server-Patches/0348-Anti-Xray.patch @@ -0,0 +1,1581 @@ +From af00f9dcbb93cca7e2f193107d3feb7082b4657d 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 a87258bbe..a395406d5 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +@@ -1,7 +1,10 @@ + package com.destroystokyo.paper; + ++import java.util.Arrays; + import java.util.List; + ++import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.ChunkEdgeMode; ++import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode; + import net.minecraft.server.MinecraftServer; + import org.bukkit.Bukkit; + import org.bukkit.configuration.file.YamlConfiguration; +@@ -498,4 +501,27 @@ public class PaperWorldConfig { + this.armorStandTick = this.getBoolean("armor-stands-tick", this.armorStandTick); + log("ArmorStand ticking is " + (this.armorStandTick ? "enabled" : "disabled") + " by default"); + } ++ ++ public boolean antiXray; ++ public boolean asynchronous; ++ public EngineMode engineMode; ++ public ChunkEdgeMode chunkEdgeMode; ++ public int maxChunkSectionIndex; ++ public int updateRadius; ++ public List hiddenBlocks; ++ public List replacementBlocks; ++ private void antiXray() { ++ antiXray = getBoolean("anti-xray.enabled", false); ++ asynchronous = true; ++ engineMode = EngineMode.getById(getInt("anti-xray.engine-mode", EngineMode.HIDE.getId())); ++ engineMode = engineMode == null ? EngineMode.HIDE : engineMode; ++ chunkEdgeMode = ChunkEdgeMode.getById(getInt("anti-xray.chunk-edge-mode", ChunkEdgeMode.DEFAULT.getId())); ++ chunkEdgeMode = chunkEdgeMode == null ? ChunkEdgeMode.DEFAULT : chunkEdgeMode; ++ maxChunkSectionIndex = getInt("anti-xray.max-chunk-section-index", 3); ++ maxChunkSectionIndex = maxChunkSectionIndex > 15 ? 15 : maxChunkSectionIndex; ++ updateRadius = getInt("anti-xray.update-radius", 2); ++ hiddenBlocks = getList("anti-xray.hidden-blocks", Arrays.asList((Object) "gold_ore", "iron_ore", "coal_ore", "lapis_ore", "mossy_cobblestone", "obsidian", "chest", "diamond_ore", "redstone_ore", "lit_redstone_ore", "clay", "emerald_ore", "ender_chest")); ++ replacementBlocks = getList("anti-xray.replacement-blocks", Arrays.asList((Object) "stone", "planks")); ++ log("Anti-Xray: " + (antiXray ? "enabled" : "disabled") + " / Engine Mode: " + engineMode.getDescription() + " / Chunk Edge Mode: " + chunkEdgeMode.getDescription() + " / Up to " + ((maxChunkSectionIndex + 1) * 16) + " blocks / Update Radius: " + updateRadius); ++ } + } +diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java +new file mode 100644 +index 000000000..1ba8477bf +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java +@@ -0,0 +1,45 @@ ++package com.destroystokyo.paper.antixray; ++ ++import net.minecraft.server.BlockPosition; ++import net.minecraft.server.Chunk; ++import net.minecraft.server.ChunkSection; ++import net.minecraft.server.EnumDirection; ++import net.minecraft.server.IBlockData; ++import net.minecraft.server.IChunkAccess; ++import net.minecraft.server.IWorldReader; ++import net.minecraft.server.PacketPlayOutMapChunk; ++import net.minecraft.server.PlayerInteractManager; ++import net.minecraft.server.World; ++ ++public class ChunkPacketBlockController { ++ ++ public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController(); ++ ++ protected ChunkPacketBlockController() { ++ ++ } ++ ++ public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, boolean initializeBlocks) { ++ return null; ++ } ++ ++ public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { ++ return true; ++ } ++ ++ public ChunkPacketInfo getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { ++ return null; ++ } ++ ++ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { ++ packetPlayOutMapChunk.setReady(true); ++ } ++ ++ public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) { ++ ++ } ++ ++ public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) { ++ ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java +new file mode 100644 +index 000000000..f44eaaccc +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java +@@ -0,0 +1,670 @@ ++package com.destroystokyo.paper.antixray; ++ ++import java.util.HashSet; ++import java.util.Set; ++import java.util.concurrent.ExecutorService; ++import java.util.concurrent.Executors; ++ ++import org.bukkit.World.Environment; ++ ++import com.destroystokyo.paper.PaperWorldConfig; ++ ++import net.minecraft.server.Block; ++import net.minecraft.server.BlockPosition; ++import net.minecraft.server.Blocks; ++import net.minecraft.server.Chunk; ++import net.minecraft.server.ChunkSection; ++import net.minecraft.server.DataPalette; ++import net.minecraft.server.EnumDirection; ++import net.minecraft.server.GeneratorAccess; ++import net.minecraft.server.IBlockData; ++import net.minecraft.server.IChunkAccess; ++import net.minecraft.server.IWorldReader; ++import net.minecraft.server.PacketPlayOutMapChunk; ++import net.minecraft.server.PlayerInteractManager; ++import net.minecraft.server.World; ++ ++public class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController { ++ ++ private static ExecutorService executorServiceInstance = null; ++ private final ExecutorService executorService; ++ private final boolean asynchronous; ++ private final EngineMode engineMode; ++ private final ChunkEdgeMode chunkEdgeMode; ++ private final int maxChunkSectionIndex; ++ private final int updateRadius; ++ private final IBlockData[] predefinedBlockData; ++ private final IBlockData[] predefinedBlockDataStone; ++ private final IBlockData[] predefinedBlockDataNetherrack; ++ private final IBlockData[] predefinedBlockDataEndStone; ++ private final int[] predefinedBlockDataBitsGlobal; ++ private final int[] predefinedBlockDataBitsStoneGlobal; ++ private final int[] predefinedBlockDataBitsNetherrackGlobal; ++ private final int[] predefinedBlockDataBitsEndStoneGlobal; ++ private final boolean[] solidGlobal = new boolean[Block.REGISTRY_ID.size()]; ++ private final boolean[] obfuscateGlobal = new boolean[Block.REGISTRY_ID.size()]; ++ private final ChunkSection[] emptyNearbyChunkSections = {Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION}; ++ private final int maxBlockYUpdatePosition; ++ ++ public ChunkPacketBlockControllerAntiXray(PaperWorldConfig paperWorldConfig) { ++ asynchronous = paperWorldConfig.asynchronous; ++ engineMode = paperWorldConfig.engineMode; ++ chunkEdgeMode = paperWorldConfig.chunkEdgeMode; ++ maxChunkSectionIndex = paperWorldConfig.maxChunkSectionIndex; ++ updateRadius = paperWorldConfig.updateRadius; ++ ++ if (asynchronous) { ++ executorService = getExecutorServiceInstance(); ++ } else { ++ executorService = null; ++ } ++ ++ if (engineMode == EngineMode.HIDE) { ++ predefinedBlockData = null; ++ predefinedBlockDataStone = new IBlockData[] {Blocks.STONE.getBlockData()}; ++ predefinedBlockDataNetherrack = new IBlockData[] {Blocks.NETHERRACK.getBlockData()}; ++ predefinedBlockDataEndStone = new IBlockData[] {Blocks.END_STONE.getBlockData()}; ++ predefinedBlockDataBitsGlobal = null; ++ predefinedBlockDataBitsStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getDataBits(Blocks.STONE.getBlockData())}; ++ predefinedBlockDataBitsNetherrackGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getDataBits(Blocks.NETHERRACK.getBlockData())}; ++ predefinedBlockDataBitsEndStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getDataBits(Blocks.END_STONE.getBlockData())}; ++ } else { ++ Set predefinedBlockDataSet = new HashSet(); ++ ++ for (Object id : paperWorldConfig.hiddenBlocks) { ++ Block block = Block.getByName(String.valueOf(id)); ++ ++ if (block != null && !block.isTileEntity()) { ++ predefinedBlockDataSet.add(block.getBlockData()); ++ } ++ } ++ ++ predefinedBlockData = predefinedBlockDataSet.size() == 0 ? new IBlockData[] {Blocks.DIAMOND_ORE.getBlockData()} : predefinedBlockDataSet.toArray(new IBlockData[predefinedBlockDataSet.size()]); ++ predefinedBlockDataStone = null; ++ predefinedBlockDataNetherrack = null; ++ predefinedBlockDataEndStone = null; ++ predefinedBlockDataBitsGlobal = new int[predefinedBlockData.length]; ++ ++ for (int i = 0; i < predefinedBlockData.length; i++) { ++ predefinedBlockDataBitsGlobal[i] = ChunkSection.GLOBAL_PALETTE.getDataBits(predefinedBlockData[i]); ++ } ++ ++ predefinedBlockDataBitsStoneGlobal = null; ++ predefinedBlockDataBitsNetherrackGlobal = null; ++ predefinedBlockDataBitsEndStoneGlobal = null; ++ } ++ ++ for (Object id : (engineMode == EngineMode.HIDE) ? paperWorldConfig.hiddenBlocks : paperWorldConfig.replacementBlocks) { ++ Block block = Block.getByName(String.valueOf(id)); ++ ++ if (block != null) { ++ obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(block.getBlockData())] = true; ++ } ++ } ++ ++ for (int i = 0; i < solidGlobal.length; i++) { ++ IBlockData blockData = ChunkSection.GLOBAL_PALETTE.getObject(i); ++ ++ if (blockData != null) { ++ solidGlobal[i] = blockData.getBlock().isOccluding(blockData) && blockData.getBlock() != Blocks.SPAWNER && blockData.getBlock() != Blocks.BARRIER; ++ } ++ } ++ ++ this.maxBlockYUpdatePosition = (maxChunkSectionIndex + 1) * 16 + updateRadius - 1; ++ } ++ ++ private static ExecutorService getExecutorServiceInstance() { ++ if (executorServiceInstance == null) { ++ executorServiceInstance = Executors.newSingleThreadExecutor(); ++ } ++ ++ return executorServiceInstance; ++ } ++ ++ @Override ++ public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, boolean initializeBlocks) { ++ //Return the block data which should be added to the data palettes so that they can be used for the obfuscation ++ if (chunkSection.getYPosition() >> 4 <= maxChunkSectionIndex) { ++ switch (engineMode) { ++ case HIDE: ++ if (world instanceof GeneratorAccess) { ++ switch (((GeneratorAccess) world).getMinecraftWorld().getWorld().getEnvironment()) { ++ case NETHER: ++ return predefinedBlockDataNetherrack; ++ case THE_END: ++ return predefinedBlockDataEndStone; ++ default: ++ return predefinedBlockDataStone; ++ } ++ } ++ ++ return null; ++ default: ++ return predefinedBlockData; ++ } ++ } ++ ++ return null; ++ } ++ ++ @Override ++ public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { ++ //Load nearby chunks if necessary ++ if (chunkEdgeMode == ChunkEdgeMode.WAIT && !force) { ++ if (chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1) == null) { ++ //Don't create the chunk packet now, wait until nearby chunks are loaded and create it later ++ return false; ++ } ++ } else if (chunkEdgeMode == ChunkEdgeMode.LOAD || chunkEdgeMode == ChunkEdgeMode.WAIT) { ++ chunk.world.getChunkAt(chunk.locX - 1, chunk.locZ); ++ chunk.world.getChunkAt(chunk.locX + 1, chunk.locZ); ++ chunk.world.getChunkAt(chunk.locX, chunk.locZ - 1); ++ chunk.world.getChunkAt(chunk.locX, chunk.locZ + 1); ++ } ++ ++ //Create the chunk packet now ++ return true; ++ } ++ ++ @Override ++ public ChunkPacketInfoAntiXray getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { ++ //Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later ++ ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this); ++ chunkPacketInfoAntiXray.setNearbyChunks(chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1)); ++ return chunkPacketInfoAntiXray; ++ } ++ ++ @Override ++ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { ++ if (asynchronous) { ++ executorService.submit((ChunkPacketInfoAntiXray) chunkPacketInfo); ++ } else { ++ obfuscate((ChunkPacketInfoAntiXray) chunkPacketInfo); ++ } ++ } ++ ++ //Actually these fields should be variables inside the obfuscate method but in sync mode or with SingleThreadExecutor in async mode it's okay ++ private int[] predefinedBlockDataBits; ++ private final boolean[] solid = new boolean[Block.REGISTRY_ID.size()]; ++ private final boolean[] obfuscate = new boolean[Block.REGISTRY_ID.size()]; ++ //These boolean arrays represent chunk layers, true means don't obfuscate, false means obfuscate ++ private boolean[][] current = new boolean[16][16]; ++ private boolean[][] next = new boolean[16][16]; ++ private boolean[][] nextNext = new boolean[16][16]; ++ private final DataBitsReader dataBitsReader = new DataBitsReader(); ++ private final DataBitsWriter dataBitsWriter = new DataBitsWriter(); ++ private final ChunkSection[] nearbyChunkSections = new ChunkSection[4]; ++ ++ public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) { ++ boolean[] solidTemp = null; ++ boolean[] obfuscateTemp = null; ++ dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData()); ++ dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData()); ++ int counter = 0; ++ ++ for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) { ++ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) { ++ int[] predefinedBlockDataBitsTemp; ++ ++ if (chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex) == ChunkSection.GLOBAL_PALETTE) { ++ predefinedBlockDataBitsTemp = engineMode == EngineMode.HIDE ? chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.NETHER ? predefinedBlockDataBitsNetherrackGlobal : chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.THE_END ? predefinedBlockDataBitsEndStoneGlobal : predefinedBlockDataBitsStoneGlobal : predefinedBlockDataBitsGlobal; ++ } else { ++ predefinedBlockDataBitsTemp = predefinedBlockDataBits == null ? predefinedBlockDataBits = engineMode == EngineMode.HIDE ? new int[1] : new int[predefinedBlockData.length] : predefinedBlockDataBits; ++ ++ for (int i = 0; i < predefinedBlockDataBitsTemp.length; i++) { ++ predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).getDataBits(chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex)[i]); ++ } ++ } ++ ++ dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getDataBitsIndex(chunkSectionIndex)); ++ ++ //Check if the chunk section below was not obfuscated ++ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex - 1) == null) { ++ //If so, initialize some stuff ++ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); ++ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getDataBitsIndex(chunkSectionIndex)); ++ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), solid, solidGlobal); ++ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), obfuscate, obfuscateGlobal); ++ //Read the blocks of the upper layer of the chunk section below if it exists ++ ChunkSection belowChunkSection = null; ++ boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex - 1]) == Chunk.EMPTY_CHUNK_SECTION; ++ ++ for (int z = 0; z < 16; z++) { ++ for (int x = 0; x < 16; x++) { ++ current[z][x] = true; ++ next[z][x] = skipFirstLayer || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(belowChunkSection.getType(x, 15, z))]; ++ } ++ } ++ ++ //Abuse the obfuscateLayer method to read the blocks of the first layer of the current chunk section ++ dataBitsWriter.setBitsPerObject(0); ++ obfuscateLayer(-1, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, emptyNearbyChunkSections, counter); ++ } ++ ++ dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); ++ nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex]; ++ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex]; ++ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex]; ++ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex]; ++ ++ //Obfuscate all layers of the current chunk section except the upper one ++ for (int y = 0; y < 15; y++) { ++ boolean[][] temp = current; ++ current = next; ++ next = nextNext; ++ nextNext = temp; ++ counter = obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); ++ } ++ ++ //Check if the chunk section above doesn't need obfuscation ++ if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex + 1) == null) { ++ //If so, obfuscate the upper layer of the current chunk section by reading blocks of the first layer from the chunk section above if it exists ++ ChunkSection aboveChunkSection; ++ ++ if (chunkSectionIndex != 15 && (aboveChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex + 1]) != Chunk.EMPTY_CHUNK_SECTION) { ++ boolean[][] temp = current; ++ current = next; ++ next = nextNext; ++ nextNext = temp; ++ ++ for (int z = 0; z < 16; z++) { ++ for (int x = 0; x < 16; x++) { ++ if (!solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(aboveChunkSection.getType(x, 0, z))]) { ++ current[z][x] = true; ++ } ++ } ++ } ++ ++ //There is nothing to read anymore ++ dataBitsReader.setBitsPerObject(0); ++ solid[0] = true; ++ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); ++ } ++ } else { ++ //If not, initialize the reader and other stuff for the chunk section above to obfuscate the upper layer of the current chunk section ++ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex + 1)); ++ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getDataBitsIndex(chunkSectionIndex + 1)); ++ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), solid, solidGlobal); ++ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal); ++ boolean[][] temp = current; ++ current = next; ++ next = nextNext; ++ nextNext = temp; ++ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); ++ } ++ ++ dataBitsWriter.finish(); ++ } ++ } ++ ++ chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true); ++ } ++ ++ private int obfuscateLayer(int y, DataBitsReader dataBitsReader, DataBitsWriter dataBitsWriter, boolean[] solid, boolean[] obfuscate, int[] predefinedBlockDataBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, ChunkSection[] nearbyChunkSections, int counter) { ++ //First block of first line ++ int dataBits = dataBitsReader.read(); ++ ++ if (nextNext[0][0] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[0][1] = true; ++ next[1][0] = true; ++ } else { ++ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[2].getType(0, y, 15))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[0].getType(15, y, 0))] || current[0][0]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[0][0] = true; ++ } ++ ++ //First line ++ for (int x = 1; x < 15; x++) { ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[0][x] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[0][x - 1] = true; ++ next[0][x + 1] = true; ++ next[1][x] = true; ++ } else { ++ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[2].getType(x, y, 15))] || current[0][x]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[0][x] = true; ++ } ++ } ++ ++ //Last block of first line ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[0][15] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[0][14] = true; ++ next[1][15] = true; ++ } else { ++ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[2].getType(15, y, 15))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[1].getType(0, y, 0))] || current[0][15]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[0][15] = true; ++ } ++ ++ //All inner lines ++ for (int z = 1; z < 15; z++) { ++ //First block ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[z][0] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[z][1] = true; ++ next[z - 1][0] = true; ++ next[z + 1][0] = true; ++ } else { ++ if (nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[0].getType(15, y, z))] || current[z][0]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[z][0] = true; ++ } ++ ++ //All inner blocks ++ for (int x = 1; x < 15; x++) { ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[z][x] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[z][x - 1] = true; ++ next[z][x + 1] = true; ++ next[z - 1][x] = true; ++ next[z + 1][x] = true; ++ } else { ++ if (current[z][x]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[z][x] = true; ++ } ++ } ++ ++ //Last block ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[z][15] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[z][14] = true; ++ next[z - 1][15] = true; ++ next[z + 1][15] = true; ++ } else { ++ if (nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[1].getType(0, y, z))] || current[z][15]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[z][15] = true; ++ } ++ } ++ ++ //First block of last line ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[15][0] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[15][1] = true; ++ next[14][0] = true; ++ } else { ++ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[3].getType(0, y, 0))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[0].getType(15, y, 15))] || current[15][0]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[15][0] = true; ++ } ++ ++ //Last line ++ for (int x = 1; x < 15; x++) { ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[15][x] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[15][x - 1] = true; ++ next[15][x + 1] = true; ++ next[14][x] = true; ++ } else { ++ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[3].getType(x, y, 0))] || current[15][x]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[15][x] = true; ++ } ++ } ++ ++ //Last block of last line ++ dataBits = dataBitsReader.read(); ++ ++ if (nextNext[15][15] = !solid[dataBits]) { ++ dataBitsWriter.skip(); ++ next[15][14] = true; ++ next[14][15] = true; ++ } else { ++ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[3].getType(15, y, 0))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(nearbyChunkSections[1].getType(0, y, 15))] || current[15][15]) { ++ dataBitsWriter.skip(); ++ } else { ++ if (counter >= predefinedBlockDataBits.length) { ++ counter = 0; ++ } ++ ++ dataBitsWriter.write(predefinedBlockDataBits[counter++]); ++ } ++ } ++ ++ if (!obfuscate[dataBits]) { ++ next[15][15] = true; ++ } ++ ++ return counter; ++ } ++ ++ private boolean[] readDataPalette(DataPalette dataPalette, boolean[] temp, boolean[] global) { ++ if (dataPalette == ChunkSection.GLOBAL_PALETTE) { ++ return global; ++ } ++ ++ IBlockData blockData; ++ ++ for (int i = 0; (blockData = dataPalette.getObject(i)) != null; i++) { ++ temp[i] = global[ChunkSection.GLOBAL_PALETTE.getDataBits(blockData)]; ++ } ++ ++ return temp; ++ } ++ ++ @Override ++ public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) { ++ if (oldBlockData != null && solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(oldBlockData)] && !solidGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(newBlockData)] && blockPosition.getY() <= maxBlockYUpdatePosition) { ++ updateNearbyBlocks(world, blockPosition); ++ } ++ } ++ ++ @Override ++ public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) { ++ if (blockPosition.getY() <= maxBlockYUpdatePosition) { ++ updateNearbyBlocks(playerInteractManager.world, blockPosition); ++ } ++ } ++ ++ private void updateNearbyBlocks(World world, BlockPosition blockPosition) { ++ if (updateRadius >= 2) { ++ BlockPosition temp = blockPosition.west(); ++ updateBlock(world, temp); ++ updateBlock(world, temp.west()); ++ updateBlock(world, temp.down()); ++ updateBlock(world, temp.up()); ++ updateBlock(world, temp.north()); ++ updateBlock(world, temp.south()); ++ updateBlock(world, temp = blockPosition.east()); ++ updateBlock(world, temp.east()); ++ updateBlock(world, temp.down()); ++ updateBlock(world, temp.up()); ++ updateBlock(world, temp.north()); ++ updateBlock(world, temp.south()); ++ updateBlock(world, temp = blockPosition.down()); ++ updateBlock(world, temp.down()); ++ updateBlock(world, temp.north()); ++ updateBlock(world, temp.south()); ++ updateBlock(world, temp = blockPosition.up()); ++ updateBlock(world, temp.up()); ++ updateBlock(world, temp.north()); ++ updateBlock(world, temp.south()); ++ updateBlock(world, temp = blockPosition.north()); ++ updateBlock(world, temp.north()); ++ updateBlock(world, temp = blockPosition.south()); ++ updateBlock(world, temp.south()); ++ } else if (updateRadius == 1) { ++ updateBlock(world, blockPosition.west()); ++ updateBlock(world, blockPosition.east()); ++ updateBlock(world, blockPosition.down()); ++ updateBlock(world, blockPosition.up()); ++ updateBlock(world, blockPosition.north()); ++ updateBlock(world, blockPosition.south()); ++ } else { ++ //Do nothing if updateRadius <= 0 (test mode) ++ } ++ } ++ ++ private void updateBlock(World world, BlockPosition blockPosition) { ++ if (world.isLoaded(blockPosition)) { ++ IBlockData blockData = world.getType(blockPosition); ++ ++ if (obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getDataBits(blockData)]) { ++ world.notify(blockPosition, blockData, blockData, 3); ++ } ++ } ++ } ++ ++ public enum EngineMode { ++ ++ HIDE(1, "hide ores"), ++ OBFUSCATE(2, "obfuscate"); ++ ++ private final int id; ++ private final String description; ++ ++ EngineMode(int id, String description) { ++ this.id = id; ++ this.description = description; ++ } ++ ++ public static EngineMode getById(int id) { ++ for (EngineMode engineMode : values()) { ++ if (engineMode.id == id) { ++ return engineMode; ++ } ++ } ++ ++ return null; ++ } ++ ++ public int getId() { ++ return id; ++ } ++ ++ public String getDescription() { ++ return description; ++ } ++ } ++ ++ public enum ChunkEdgeMode { ++ ++ DEFAULT(1, "default"), ++ WAIT(2, "wait until nearby chunks are loaded"), ++ LOAD(3, "load nearby chunks"); ++ ++ private final int id; ++ private final String description; ++ ++ ChunkEdgeMode(int id, String description) { ++ this.id = id; ++ this.description = description; ++ } ++ ++ public static ChunkEdgeMode getById(int id) { ++ for (ChunkEdgeMode chunkEdgeMode : values()) { ++ if (chunkEdgeMode.id == id) { ++ return chunkEdgeMode; ++ } ++ } ++ ++ return null; ++ } ++ ++ public int getId() { ++ return id; ++ } ++ ++ public String getDescription() { ++ return description; ++ } ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java +new file mode 100644 +index 000000000..41618994b +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java +@@ -0,0 +1,81 @@ ++package com.destroystokyo.paper.antixray; ++ ++import net.minecraft.server.Chunk; ++import net.minecraft.server.DataPalette; ++import net.minecraft.server.PacketPlayOutMapChunk; ++ ++public class ChunkPacketInfo { ++ ++ private final PacketPlayOutMapChunk packetPlayOutMapChunk; ++ private final Chunk chunk; ++ private final int chunkSectionSelector; ++ private byte[] data; ++ private final int[] bitsPerObject = new int[16]; ++ private final Object[] dataPalettes = new Object[16]; ++ private final int[] dataBitsIndexes = new int[16]; ++ private final Object[][] predefinedObjects = new Object[16][]; ++ ++ public ChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { ++ this.packetPlayOutMapChunk = packetPlayOutMapChunk; ++ this.chunk = chunk; ++ this.chunkSectionSelector = chunkSectionSelector; ++ } ++ ++ public PacketPlayOutMapChunk getPacketPlayOutMapChunk() { ++ return packetPlayOutMapChunk; ++ } ++ ++ public Chunk getChunk() { ++ return chunk; ++ } ++ ++ public int getChunkSectionSelector() { ++ return chunkSectionSelector; ++ } ++ ++ public byte[] getData() { ++ return data; ++ } ++ ++ public void setData(byte[] data) { ++ this.data = data; ++ } ++ ++ public int getBitsPerObject(int chunkSectionIndex) { ++ return bitsPerObject[chunkSectionIndex]; ++ } ++ ++ public void setBitsPerObject(int chunkSectionIndex, int bitsPerObject) { ++ this.bitsPerObject[chunkSectionIndex] = bitsPerObject; ++ } ++ ++ @SuppressWarnings("unchecked") ++ public DataPalette getDataPalette(int chunkSectionIndex) { ++ return (DataPalette) dataPalettes[chunkSectionIndex]; ++ } ++ ++ public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) { ++ dataPalettes[chunkSectionIndex] = dataPalette; ++ } ++ ++ public int getDataBitsIndex(int chunkSectionIndex) { ++ return dataBitsIndexes[chunkSectionIndex]; ++ } ++ ++ public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) { ++ dataBitsIndexes[chunkSectionIndex] = dataBitsIndex; ++ } ++ ++ @SuppressWarnings("unchecked") ++ public T[] getPredefinedObjects(int chunkSectionIndex) { ++ return (T[]) predefinedObjects[chunkSectionIndex]; ++ } ++ ++ public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) { ++ this.predefinedObjects[chunkSectionIndex] = predefinedObjects; ++ } ++ ++ public boolean isWritten(int chunkSectionIndex) { ++ return bitsPerObject[chunkSectionIndex] != 0; ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java +new file mode 100644 +index 000000000..e255a45fa +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java +@@ -0,0 +1,29 @@ ++package com.destroystokyo.paper.antixray; ++ ++import net.minecraft.server.Chunk; ++import net.minecraft.server.IBlockData; ++import net.minecraft.server.PacketPlayOutMapChunk; ++ ++public class ChunkPacketInfoAntiXray extends ChunkPacketInfo implements Runnable { ++ ++ private Chunk[] nearbyChunks; ++ private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray; ++ ++ public ChunkPacketInfoAntiXray(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) { ++ super(packetPlayOutMapChunk, chunk, chunkSectionSelector); ++ this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray; ++ } ++ ++ public Chunk[] getNearbyChunks() { ++ return nearbyChunks; ++ } ++ ++ public void setNearbyChunks(Chunk... nearbyChunks) { ++ this.nearbyChunks = nearbyChunks; ++ } ++ ++ @Override ++ public void run() { ++ chunkPacketBlockControllerAntiXray.obfuscate(this); ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java +new file mode 100644 +index 000000000..cc586827a +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java +@@ -0,0 +1,56 @@ ++package com.destroystokyo.paper.antixray; ++ ++public class DataBitsReader { ++ ++ private byte[] dataBits; ++ private int bitsPerObject; ++ private int mask; ++ private int longInDataBitsIndex; ++ private int bitInLongIndex; ++ private long current; ++ ++ public void setDataBits(byte[] dataBits) { ++ this.dataBits = dataBits; ++ } ++ ++ public void setBitsPerObject(int bitsPerObject) { ++ this.bitsPerObject = bitsPerObject; ++ mask = (1 << bitsPerObject) - 1; ++ } ++ ++ public void setIndex(int index) { ++ this.longInDataBitsIndex = index; ++ bitInLongIndex = 0; ++ init(); ++ } ++ ++ private void init() { ++ if (dataBits.length > longInDataBitsIndex + 7) { ++ current = ((((long) dataBits[longInDataBitsIndex]) << 56) ++ | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48) ++ | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40) ++ | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32) ++ | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24) ++ | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16) ++ | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8) ++ | (((long) dataBits[longInDataBitsIndex + 7] & 0xff))); ++ } ++ } ++ ++ public int read() { ++ int value = (int) (current >>> bitInLongIndex) & mask; ++ bitInLongIndex += bitsPerObject; ++ ++ if (bitInLongIndex > 63) { ++ bitInLongIndex -= 64; ++ longInDataBitsIndex += 8; ++ init(); ++ ++ if (bitInLongIndex > 0) { ++ value |= current << bitsPerObject - bitInLongIndex & mask; ++ } ++ } ++ ++ return value; ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java +new file mode 100644 +index 000000000..37093419c +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java +@@ -0,0 +1,84 @@ ++package com.destroystokyo.paper.antixray; ++ ++public class DataBitsWriter { ++ ++ private byte[] dataBits; ++ private int bitsPerObject; ++ private long mask; ++ private int longInDataBitsIndex; ++ private int bitInLongIndex; ++ private long current; ++ private boolean dirty; ++ ++ public void setDataBits(byte[] dataBits) { ++ this.dataBits = dataBits; ++ } ++ ++ public void setBitsPerObject(int bitsPerObject) { ++ this.bitsPerObject = bitsPerObject; ++ mask = (1 << bitsPerObject) - 1; ++ } ++ ++ public void setIndex(int index) { ++ this.longInDataBitsIndex = index; ++ bitInLongIndex = 0; ++ init(); ++ } ++ ++ private void init() { ++ if (dataBits.length > longInDataBitsIndex + 7) { ++ current = ((((long) dataBits[longInDataBitsIndex]) << 56) ++ | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48) ++ | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40) ++ | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32) ++ | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24) ++ | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16) ++ | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8) ++ | (((long) dataBits[longInDataBitsIndex + 7] & 0xff))); ++ } ++ ++ dirty = false; ++ } ++ ++ public void finish() { ++ if (dirty && dataBits.length > longInDataBitsIndex + 7) { ++ dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff); ++ dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff); ++ dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff); ++ dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff); ++ dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff); ++ dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff); ++ dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff); ++ dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff); ++ } ++ } ++ ++ public void write(int value) { ++ current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex; ++ dirty = true; ++ bitInLongIndex += bitsPerObject; ++ ++ if (bitInLongIndex > 63) { ++ finish(); ++ bitInLongIndex -= 64; ++ longInDataBitsIndex += 8; ++ init(); ++ ++ if (bitInLongIndex > 0) { ++ current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex; ++ dirty = true; ++ } ++ } ++ } ++ ++ public void skip() { ++ bitInLongIndex += bitsPerObject; ++ ++ if (bitInLongIndex > 63) { ++ finish(); ++ bitInLongIndex -= 64; ++ longInDataBitsIndex += 8; ++ init(); ++ } ++ } ++} +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index 8b8083093..358934455 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -537,7 +537,7 @@ public class Chunk implements IChunkAccess { + return null; + } + +- chunksection = new ChunkSection(j >> 4 << 4, this.world.worldProvider.g()); ++ chunksection = new ChunkSection(j >> 4 << 4, this.world.worldProvider.g(), this, this.world, true); // Paper - Anti-Xray + this.sections[j >> 4] = chunksection; + flag1 = j >= l; + } +@@ -637,7 +637,7 @@ public class Chunk implements IChunkAccess { + return; + } + +- chunksection = new ChunkSection(i1 << 4, flag); ++ chunksection = new ChunkSection(i1 << 4, flag, this, this.world, true); // Paper - Anti-Xray + this.sections[i1] = chunksection; + this.initLighting(); + } +diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +index 54840851b..0f1423c9a 100644 +--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java ++++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +@@ -852,7 +852,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + } + + ChunkConverter chunkconverter = nbttagcompound.hasKeyOfType("UpgradeData", 10) ? new ChunkConverter(nbttagcompound.getCompound("UpgradeData")) : ChunkConverter.a; +- ProtoChunk protochunk = new ProtoChunk(i, j, chunkconverter); ++ ProtoChunk protochunk = new ProtoChunk(i, j, chunkconverter, generatoraccess); // Paper - Anti-Xray + + protochunk.a(abiomebase); + protochunk.b(nbttagcompound.getLong("InhabitedTime")); +@@ -958,7 +958,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); + byte b0 = nbttagcompound.getByte("Y"); +- ChunkSection chunksection = new ChunkSection(b0 << 4, flag1); ++ ChunkSection chunksection = new ChunkSection(b0 << 4, flag1, null, iworldreader, false); // Paper - Anti-Xray + + chunksection.getBlocks().a(nbttagcompound, "Palette", "BlockStates"); + chunksection.a(new NibbleArray(nbttagcompound.getByteArray("BlockLight"))); +diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java +index 233cbb6d6..8c116b74c 100644 +--- a/src/main/java/net/minecraft/server/ChunkSection.java ++++ b/src/main/java/net/minecraft/server/ChunkSection.java +@@ -13,9 +13,15 @@ public class ChunkSection { + private NibbleArray emittedLight; + private NibbleArray skyLight; + ++ // Paper start - Anti-Xray - Support default constructor + public ChunkSection(int i, boolean flag) { ++ this(i, flag, null, null, true); ++ } ++ // Paper end ++ ++ public ChunkSection(int i, boolean flag, IChunkAccess chunk, IWorldReader world, boolean initializeBlocks) { // Paper - Anti-Xray + this.yPos = i; +- this.blockIds = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData()); // Paper - Decompile error ++ this.blockIds = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData(), world instanceof GeneratorAccess ? ((GeneratorAccess) world).getMinecraftWorld().chunkPacketBlockController.getPredefinedBlockData(world, chunk, this, flag, initializeBlocks) : null, initializeBlocks); // Paper - Decompile error // Paper - Anti-Xray - Add predefined block data + this.emittedLight = new NibbleArray(); + if (flag) { + this.skyLight = new NibbleArray(); +diff --git a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java +index 668829c5b..7c414ed8a 100644 +--- a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java ++++ b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java +@@ -43,7 +43,7 @@ public class ChunkTaskScheduler extends Scheduler implements DataPaletteExpandable { + private final Function e; + private final Function f; + private final T g; ++ private final T[] predefinedObjects; // Paper - Anti-Xray - Add predefined objects + protected DataBits a; protected DataBits getDataBits() { return this.a; } // Paper - OBFHELPER + private DataPalette h; private DataPalette getDataPalette() { return this.h; } // Paper - OBFHELPER + private int i; private int getBitsPerObject() { return this.i; } // Paper - OBFHELPER +@@ -41,13 +43,44 @@ public class DataPaletteBlock implements DataPaletteExpandable { + this.j.unlock(); + } + ++ // Paper start - Anti-Xray - Support default constructor + public DataPaletteBlock(DataPalette datapalette, RegistryBlockID registryblockid, Function function, Function function1, T t0) { ++ this(datapalette, registryblockid, function, function1, t0, null, true); ++ } ++ // Paper end ++ ++ public DataPaletteBlock(DataPalette datapalette, RegistryBlockID registryblockid, Function function, Function function1, T t0, T[] predefinedObjects, boolean initialize) { // Paper - Anti-Xray - Add predefined objects + this.b = datapalette; + this.d = registryblockid; + this.e = function; + this.f = function1; + this.g = t0; +- this.b(4); ++ // Paper start - Anti-Xray - Add predefined objects ++ this.predefinedObjects = predefinedObjects; ++ ++ if (initialize) { ++ if (predefinedObjects == null) { ++ // Default ++ this.initialize(4); ++ } else { ++ // TODO: MathHelper.d(int i) can be used here instead (see DataPaletteBlock#a(NBTTagCompound nbttagcompound, String s, String s1)) but I don't understand the implementation ++ // Count the bits of the maximum array index to initialize a data palette with enough space from the beginning ++ // The length of the array is used because air is also added to the data palette from the beginning ++ // Start with at least 4 ++ int maxIndex = predefinedObjects.length >> 4; ++ int bitCount = 4; ++ ++ while (maxIndex != 0) { ++ maxIndex >>= 1; ++ bitCount++; ++ } ++ ++ // Initialize with at least 15 free indixes ++ this.initialize((1 << bitCount) - predefinedObjects.length < 16 ? bitCount + 1 : bitCount); ++ this.addPredefinedObjects(); ++ } ++ } ++ // Paper end + } + + private static int b(int i, int j, int k) { +@@ -73,12 +106,23 @@ public class DataPaletteBlock implements DataPaletteExpandable { + } + } + ++ // Paper start - Anti-Xray - Add predefined objects ++ private void addPredefinedObjects() { ++ if (this.predefinedObjects != null && this.getDataPalette() != this.getDataPaletteGlobal()) { ++ for (int i = 0; i < this.predefinedObjects.length; i++) { ++ this.h.a(this.predefinedObjects[i]); ++ } ++ } ++ } ++ // Paper end ++ + public int onResize(int i, T t0) { + this.b(); + DataBits databits = this.a; + DataPalette datapalette = this.h; // Paper - decompile fix + + this.b(i); ++ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects + + int j; + +@@ -117,11 +161,28 @@ public class DataPaletteBlock implements DataPaletteExpandable { + return object == null ? this.g : object; + } + +- public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } // Paper - OBFHELPER ++ // Paper start - Anti-Xray - Support default methods ++ public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } + public void b(PacketDataSerializer packetdataserializer) { ++ this.b(packetdataserializer, null, 0); ++ } ++ // Paper end ++ ++ public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer, ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { this.b(packetDataSerializer, chunkPacketInfo, chunkSectionIndex); } // Paper - OBFHELPER // Paper - Anti-Xray - Add chunk packet info ++ public void b(PacketDataSerializer packetdataserializer, ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { // Paper - Anti-Xray - Add chunk packet info + this.b(); + packetdataserializer.writeByte(this.i); + this.h.b(packetdataserializer); ++ ++ // Paper start - Anti-Xray - Add chunk packet info ++ if (chunkPacketInfo != null) { ++ chunkPacketInfo.setBitsPerObject(chunkSectionIndex, this.getBitsPerObject()); ++ chunkPacketInfo.setDataPalette(chunkSectionIndex, this.getDataPalette()); ++ chunkPacketInfo.setDataBitsIndex(chunkSectionIndex, packetdataserializer.writerIndex() + PacketDataSerializer.countBytes(this.getDataBits().getDataBits().length)); ++ chunkPacketInfo.setPredefinedObjects(chunkSectionIndex, this.predefinedObjects); ++ } ++ // Paper end ++ + packetdataserializer.a(this.a.a()); + this.c(); + } +@@ -129,13 +190,15 @@ public class DataPaletteBlock implements DataPaletteExpandable { + public void a(NBTTagCompound nbttagcompound, String s, String s1) { + this.b(); + NBTTagList nbttaglist = nbttagcompound.getList(s, 10); +- int i = Math.max(4, MathHelper.d(nbttaglist.size())); ++ // Paper - Anti-Xray - TODO: Should this.predefinedObjects.length just be added here (faster) or should the contents be compared to calculate the size (less RAM)? ++ int i = Math.max(4, MathHelper.d(nbttaglist.size() + (this.predefinedObjects == null ? 0 : this.predefinedObjects.length))); // Paper - Anti-Xray - Calculate the size with predefined objects + +- if (i != this.i) { ++ if (true || i != this.i) { // Paper - Anti-Xray - Not initialized yet + this.b(i); + } + + this.h.a(nbttaglist); ++ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects + long[] along = nbttagcompound.o(s1); + int j = along.length * 64 / 4096; + +diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java +index f8facddb4..b2afec5e4 100644 +--- a/src/main/java/net/minecraft/server/NetworkManager.java ++++ b/src/main/java/net/minecraft/server/NetworkManager.java +@@ -158,8 +158,8 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + } + + public void sendPacket(Packet packet, @Nullable GenericFutureListener> genericfuturelistener) { +- if (this.isConnected()) { +- this.o(); ++ if (this.isConnected() && this.sendPacketQueue() && !(packet instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) packet).isReady())) { // Paper - Async-Anti-Xray - Add chunk packets which are not ready or all packets if the packet queue contains chunk packets which are not ready to the packet queue and send the packets later in the right order ++ //this.o(); // Paper - Async-Anti-Xray - Move to if statement (this.sendPacketQueue()) + this.b(packet, genericfuturelistener); + } else { + this.j.writeLock().lock(); +@@ -214,23 +214,38 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + + } + +- private void sendPacketQueue() { this.o(); } // Paper - OBFHELPER +- private void o() { ++ // Paper start - Async-Anti-Xray - Stop dispatching further packets and return false if the peeked packet is a chunk packet which is not ready ++ private boolean sendPacketQueue() { return this.o(); } // OBFHELPER // void -> boolean ++ private boolean o() { // void -> boolean + if (this.channel != null && this.channel.isOpen()) { +- this.j.readLock().lock(); ++ if (this.i.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all ++ return true; ++ } ++ ++ this.j.writeLock().lock(); // readLock -> writeLock (because of race condition between peek and poll) + + try { + while (!this.i.isEmpty()) { +- NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.i.poll(); +- +- this.b(networkmanager_queuedpacket.a, networkmanager_queuedpacket.b); ++ NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.getPacketQueue().peek(); // poll -> peek ++ ++ if (networkmanager_queuedpacket != null) { // Fix NPE (Spigot bug caused by handleDisconnection()) ++ if (networkmanager_queuedpacket.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) networkmanager_queuedpacket.getPacket()).isReady()) { // Check if the peeked packet is a chunk packet which is not ready ++ return false; // Return false if the peeked packet is a chunk packet which is not ready ++ } else { ++ this.getPacketQueue().poll(); // poll here ++ this.dispatchPacket(networkmanager_queuedpacket.getPacket(), networkmanager_queuedpacket.getGenericFutureListener()); // dispatch the packet ++ } ++ } + } + } finally { +- this.j.readLock().unlock(); ++ this.j.writeLock().unlock(); // readLock -> writeLock (because of race condition between peek and poll) + } + + } ++ ++ return true; // Return true if all packets were dispatched + } ++ // Paper end + + public void a() { + this.o(); +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java +index 9787f5b06..d666685c5 100644 +--- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java ++++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java +@@ -1,5 +1,6 @@ + package net.minecraft.server; + ++import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray + import com.google.common.collect.Lists; + import io.netty.buffer.ByteBuf; + import io.netty.buffer.Unpooled; +@@ -16,17 +17,30 @@ public class PacketPlayOutMapChunk implements Packet { + private byte[] d; private byte[] getData() { return this.d; } // Paper - OBFHELPER + private List e; + private boolean f; ++ private volatile boolean ready = false; // Paper - Async-Anti-Xray - Ready flag for the network manager + +- public PacketPlayOutMapChunk() {} ++ // Paper start - Async-Anti-Xray - Set the ready flag to true ++ public PacketPlayOutMapChunk() { ++ this.ready = true; ++ } ++ // Paper end + + public PacketPlayOutMapChunk(Chunk chunk, int i) { ++ ChunkPacketInfo chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info + this.a = chunk.locX; + this.b = chunk.locZ; + this.f = i == '\uffff'; + boolean flag = chunk.getWorld().worldProvider.g(); + + this.d = new byte[this.a(chunk, flag, i)]; +- this.c = this.a(new PacketDataSerializer(this.h()), chunk, flag, i); ++ ++ // Paper start - Anti-Xray - Add chunk packet info ++ if (chunkPacketInfo != null) { ++ chunkPacketInfo.setData(this.getData()); ++ } ++ // Paper end ++ ++ this.c = this.writeChunk(new PacketDataSerializer(this.h()), chunk, flag, i, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info + this.e = Lists.newArrayList(); + Iterator iterator = chunk.getTileEntities().entrySet().iterator(); + +@@ -44,7 +58,18 @@ public class PacketPlayOutMapChunk implements Packet { + } + } + ++ chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks ++ } ++ ++ // Paper start - Async-Anti-Xray - Getter and Setter for the ready flag ++ public boolean isReady() { ++ return this.ready; ++ } ++ ++ public void setReady(boolean ready) { ++ this.ready = ready; + } ++ // Paper end + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.readInt(); +@@ -98,8 +123,15 @@ public class PacketPlayOutMapChunk implements Packet { + return bytebuf; + } + +- public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } // Paper - OBFHELPER ++ // Paper start - Anti-Xray - Support default methods ++ public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } + public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i) { ++ return this.a(packetdataserializer, chunk, flag, i, null); ++ } ++ // Paper end ++ ++ public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector, ChunkPacketInfo chunkPacketInfo) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector, chunkPacketInfo); } // Paper - OBFHELPER // Paper - Anti-Xray - Add chunk packet info ++ public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i, ChunkPacketInfo chunkPacketInfo) { // Paper - Anti-Xray - Add chunk packet info + int j = 0; + ChunkSection[] achunksection = chunk.getSections(); + int k = 0; +@@ -111,7 +143,7 @@ public class PacketPlayOutMapChunk implements Packet { + + if (chunksection != Chunk.a && (!this.f() || !chunksection.a()) && (i & 1 << k) != 0) { + j |= 1 << k; +- chunksection.getBlocks().b(packetdataserializer); ++ chunksection.getBlocks().writeDataPaletteBlock(packetdataserializer, chunkPacketInfo, k); // Paper - Anti-Xray - Add chunk packet info + packetdataserializer.writeBytes(chunksection.getEmittedLightArray().asBytes()); + if (flag) { + packetdataserializer.writeBytes(chunksection.getSkyLightArray().asBytes()); +diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java +index fcd9f5491..346367b68 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunk.java ++++ b/src/main/java/net/minecraft/server/PlayerChunk.java +@@ -110,6 +110,8 @@ public class PlayerChunk { + return false; + } else if (!this.chunk.isReady()) { + return false; ++ } else if (!this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', false)) { // Paper - Anti-Xray - Load nearby chunks if necessary ++ return false; // Paper - Anti-Xray - Wait and try again later + } else { + this.dirtyCount = 0; + this.h = 0; +@@ -130,6 +132,7 @@ public class PlayerChunk { + + public void sendChunk(EntityPlayer entityplayer) { + if (this.done) { ++ this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', true); // Paper - Anti-Xray - Load nearby chunks if necessary + entityplayer.playerConnection.sendPacket(new PacketPlayOutMapChunk(this.chunk, '\uffff')); + this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk); + } +@@ -194,6 +197,9 @@ public class PlayerChunk { + this.a(this.playerChunkMap.getWorld().getTileEntity(blockposition)); + } + } else if (this.dirtyCount == 64) { ++ // Paper - Anti-Xray - Loading chunks here could cause a ConcurrentModificationException #1104 ++ // Paper - Anti-Xray - TODO: Check if this is still the case for 1.13 ++ //this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, this.h, true); // Paper - Anti-Xray - Load nearby chunks if necessary + this.a((Packet) (new PacketPlayOutMapChunk(this.chunk, this.h))); + } else { + this.a((Packet) (new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, this.chunk))); +diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java +index 4a439ef4c..063f2b920 100644 +--- a/src/main/java/net/minecraft/server/PlayerInteractManager.java ++++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java +@@ -206,6 +206,8 @@ public class PlayerInteractManager { + } + + } ++ ++ this.world.chunkPacketBlockController.onPlayerLeftClickBlock(this, blockposition, enumdirection); // Paper - Anti-Xray + } + + public void a(BlockPosition blockposition) { +diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java +index 584a86c11..62a7d3c23 100644 +--- a/src/main/java/net/minecraft/server/ProtoChunk.java ++++ b/src/main/java/net/minecraft/server/ProtoChunk.java +@@ -44,12 +44,24 @@ public class ProtoChunk implements IChunkAccess { + private long s; + private final Map t; + private boolean u; ++ private final GeneratorAccess world; // Paper - Anti-Xray + ++ // Paper start - Anti-Xray - Support default constructors + public ProtoChunk(int i, int j, ChunkConverter chunkconverter) { +- this(new ChunkCoordIntPair(i, j), chunkconverter); ++ this(i, j, chunkconverter, null); + } + + public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter) { ++ this(chunkcoordintpair, chunkconverter, null); ++ } ++ // Paper end ++ ++ public ProtoChunk(int i, int j, ChunkConverter chunkconverter, GeneratorAccess world) { // Paper - Anti-Xray ++ this(new ChunkCoordIntPair(i, j), chunkconverter, world); // Paper - Anti-Xray ++ } ++ ++ public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, GeneratorAccess world) { // Paper - Anti-Xray ++ this.world = world; // Paper - Anti-Xray + this.d = new AtomicInteger(); + this.f = Maps.newEnumMap(HeightMap.Type.class); + this.g = ChunkStatus.EMPTY; +@@ -152,7 +164,7 @@ public class ProtoChunk implements IChunkAccess { + return iblockdata; + } + +- this.j[j >> 4] = new ChunkSection(j >> 4 << 4, this.x()); ++ this.j[j >> 4] = new ChunkSection(j >> 4 << 4, this.x(), this, this.world, true); // Paper - Anti-Xray + } + + IBlockData iblockdata1 = this.j[j >> 4].getType(i & 15, j & 15, k & 15); +@@ -406,7 +418,7 @@ public class ProtoChunk implements IChunkAccess { + return; + } + +- this.j[i1] = new ChunkSection(i1 << 4, this.x()); ++ this.j[i1] = new ChunkSection(i1 << 4, this.x(), this, this.world, true); // Paper - Anti-Xray + } + + if (enumskyblock == EnumSkyBlock.SKY) { +diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java +index 16d0c0d45..2f85a0fa0 100644 +--- a/src/main/java/net/minecraft/server/World.java ++++ b/src/main/java/net/minecraft/server/World.java +@@ -1,6 +1,8 @@ + package net.minecraft.server; + + import co.aikar.timings.Timings; ++import com.destroystokyo.paper.antixray.ChunkPacketBlockController; // Paper - Anti-Xray ++import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray; // Paper - Anti-Xray + import com.destroystokyo.paper.event.server.ServerExceptionEvent; + import com.destroystokyo.paper.exception.ServerInternalException; + import com.google.common.base.MoreObjects; +@@ -140,6 +142,7 @@ public abstract class World implements GeneratorAccess, IIBlockAccess, AutoClose + public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot + + public final com.destroystokyo.paper.PaperWorldConfig paperConfig; // Paper ++ public final ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray + + public final co.aikar.timings.WorldTimingsHandler timings; // Paper + public boolean guardEntityList; // Spigot // Paper - public +@@ -165,6 +168,7 @@ public abstract class World implements GeneratorAccess, IIBlockAccess, AutoClose + protected World(IDataManager idatamanager, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag, ChunkGenerator gen, org.bukkit.World.Environment env) { + this.spigotConfig = new org.spigotmc.SpigotWorldConfig( worlddata.getName() ); // Spigot + this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(worlddata.getName(), this.spigotConfig); // Paper ++ this.chunkPacketBlockController = this.paperConfig.antiXray ? new ChunkPacketBlockControllerAntiXray(this.paperConfig) : ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray + this.generator = gen; + this.world = new CraftWorld((WorldServer) this, gen, env); + this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit +@@ -384,6 +388,7 @@ public abstract class World implements GeneratorAccess, IIBlockAccess, AutoClose + // CraftBukkit end + + IBlockData iblockdata1 = chunk.a(blockposition, iblockdata, (i & 64) != 0); ++ this.chunkPacketBlockController.onBlockChange(this, blockposition, iblockdata, iblockdata1, i); // Paper - Anti-Xray + + if (iblockdata1 == null) { + // CraftBukkit start - remove blockstate if failed +diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java +index c26f0ed16..f6915d32a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java ++++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java +@@ -17,9 +17,11 @@ import org.bukkit.material.MaterialData; + public final class CraftChunkData implements ChunkGenerator.ChunkData { + private final int maxHeight; + private final ChunkSection[] sections; ++ private World world; // Paper - Anti-Xray + + public CraftChunkData(World world) { + this(world.getMaxHeight()); ++ this.world = world; // Paper - Anti-Xray + } + + /* pp for tests */ CraftChunkData(int maxHeight) { +@@ -145,7 +147,7 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { + private ChunkSection getChunkSection(int y, boolean create) { + ChunkSection section = sections[y >> 4]; + if (create && section == null) { +- sections[y >> 4] = section = new ChunkSection(y, create); ++ sections[y >> 4] = section = new ChunkSection(y, create, null, world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) world).getHandle() : null, true); // Paper - Anti-Xray + } + return section; + } +-- +2.18.0.windows.1 +