Files
paper-mc/paper-server/patches/features/0004-Anti-Xray.patch
Spottedleaf 38c1ddb52a Add and use FeatureHooks.getAllEntities
The ServerLevel#getAllEntities function only returns entities which
are accessible. FeatureHooks#getAllEntities will return all
entities, whether or not they are accessible.

Use the new hook in the EntityCommand, which allows server admins
to inspect entities in unloaded chunks.

Use the hook as well for ticking the EntityScheduler. This fixes
an issue whether unloaded entities did not have their scheduler ticked.
2025-06-24 04:55:58 -07:00

609 lines
39 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: stonar96 <minecraft.stonar96@gmail.com>
Date: Thu, 25 Nov 2021 13:27:51 +0100
Subject: [PATCH] Anti-Xray
diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java
index 811a8e5141f2061a185b53b63d951646141c0c7d..33d3eb510c5844e72bbc382bd24641aae080962d 100644
--- a/io/papermc/paper/FeatureHooks.java
+++ b/io/papermc/paper/FeatureHooks.java
@@ -45,20 +45,25 @@ public final class FeatureHooks {
}
public static LevelChunkSection createSection(final Registry<Biome> biomeRegistry, final Level level, final ChunkPos chunkPos, final int chunkSection) {
- return new LevelChunkSection(biomeRegistry);
+ return new LevelChunkSection(biomeRegistry, level, chunkPos, chunkSection); // Paper - Anti-Xray - Add parameters
}
public static void sendChunkRefreshPackets(final List<ServerPlayer> playersInRange, final LevelChunk chunk) {
- final ClientboundLevelChunkWithLightPacket refreshPacket = new ClientboundLevelChunkWithLightPacket(chunk, chunk.level.getLightEngine(), null, null);
+ // Paper start - Anti-Xray
+ final Map<Object, ClientboundLevelChunkWithLightPacket> refreshPackets = new HashMap<>();
for (final ServerPlayer player : playersInRange) {
if (player.connection == null) continue;
- player.connection.send(refreshPacket);
+ final Boolean shouldModify = chunk.getLevel().chunkPacketBlockController.shouldModify(player, chunk);
+ player.connection.send(refreshPackets.computeIfAbsent(shouldModify, s -> { // Use connection to prevent creating firing event
+ return new ClientboundLevelChunkWithLightPacket(chunk, chunk.level.getLightEngine(), null, null, (Boolean) s);
+ }));
}
+ // Paper end - Anti-Xray
}
public static PalettedContainer<BlockState> emptyPalettedBlockContainer() {
- return new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
+ return new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); // Paper - Anti-Xray - Add preset block states
}
public static Set<Long> getSentChunkKeys(final ServerPlayer player) {
diff --git a/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java b/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
index c9086bca5cbb780fd586f667e31a8fe1400ae58a..f828d07018d9a17aaa0142aac67ebed58dd84c3e 100644
--- a/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
@@ -70,8 +70,10 @@ public record ClientboundChunksBiomesPacket(List<ClientboundChunksBiomesPacket.C
}
public static void extractChunkData(FriendlyByteBuf buffer, LevelChunk chunk) {
+ int chunkSectionIndex = 0; // Paper - Anti-Xray
for (LevelChunkSection levelChunkSection : chunk.getSections()) {
- levelChunkSection.getBiomes().write(buffer);
+ levelChunkSection.getBiomes().write(buffer, null, chunkSectionIndex); // Paper - Anti-Xray
+ chunkSectionIndex++; // Paper - Anti-Xray
}
if (buffer.writerIndex() != buffer.capacity()) {
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
index de234f220ba09ad9b5e0c8215b49d20ca51d0ac7..83c4b454472714de6ebf99cd4e50867920d07509 100644
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
@@ -33,13 +33,23 @@ public class ClientboundLevelChunkPacketData {
private final byte[] buffer;
private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
+ // Paper start - Anti-Xray - Add chunk packet info
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public ClientboundLevelChunkPacketData(LevelChunk levelChunk) {
+ this(levelChunk, null);
+ }
+ public ClientboundLevelChunkPacketData(LevelChunk levelChunk, io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
+ // Paper end
this.heightmaps = levelChunk.getHeightmaps()
.stream()
.filter(entry1 -> entry1.getKey().sendToClient())
.collect(Collectors.toMap(Entry::getKey, entry1 -> (long[])entry1.getValue().getRawData().clone()));
this.buffer = new byte[calculateChunkSize(levelChunk)];
- extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk);
+ // Paper start - Anti-Xray - Add chunk packet info
+ if (chunkPacketInfo != null) {
+ chunkPacketInfo.setBuffer(this.buffer);
+ }
+ extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk, chunkPacketInfo);
this.blockEntitiesData = Lists.newArrayList();
for (Entry<BlockPos, BlockEntity> entry : levelChunk.getBlockEntities().entrySet()) {
@@ -82,9 +92,17 @@ public class ClientboundLevelChunkPacketData {
return byteBuf;
}
+ // Paper start - Anti-Xray - Add chunk packet info
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public static void extractChunkData(FriendlyByteBuf buffer, LevelChunk chunk) {
+ ClientboundLevelChunkPacketData.extractChunkData(buffer, chunk, null);
+ }
+ public static void extractChunkData(FriendlyByteBuf buffer, LevelChunk chunk, io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
+ int chunkSectionIndex = 0;
for (LevelChunkSection levelChunkSection : chunk.getSections()) {
- levelChunkSection.write(buffer);
+ levelChunkSection.write(buffer, chunkPacketInfo, chunkSectionIndex);
+ chunkSectionIndex++;
+ // Paper end - Anti-Xray - Add chunk packet info
}
if (buffer.writerIndex() != buffer.capacity()) {
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
index 3a384175f8e7f204234bbaf3081bdc20c47a0d4b..5699bc15eba92e22433a20cb8326b59f2ebd3036 100644
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
@@ -18,18 +18,31 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
private final int z;
private final ClientboundLevelChunkPacketData chunkData;
private final ClientboundLightUpdatePacketData lightData;
- // Paper start - Anti-Xray
+ // Paper start - Async-Anti-Xray - Ready flag for the connection, add chunk packet info
+ private volatile boolean ready;
+
+ @Override
+ public boolean isReady() {
+ return this.ready;
+ }
+
public void setReady(final boolean ready) {
- // Empty hook, updated by feature patch
+ this.ready = ready;
}
- // Paper end - Anti-Xray
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight) {
+ this(chunk, lightEngine, skyLight, blockLight, true);
+ }
+ public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight, boolean modifyBlocks) {
+ // Paper end - Anti-Xray
ChunkPos pos = chunk.getPos();
this.x = pos.x;
this.z = pos.z;
- this.chunkData = new ClientboundLevelChunkPacketData(chunk);
+ io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo = modifyBlocks ? chunk.getLevel().chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null; // Paper - Ant-Xray
+ this.chunkData = new ClientboundLevelChunkPacketData(chunk, chunkPacketInfo); // Paper - Anti-Xray
this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight);
+ chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
}
private ClientboundLevelChunkWithLightPacket(RegistryFriendlyByteBuf buffer) {
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index e6f958b6128213fb9577406c6495148dccac40ca..49008b4cbaead8a66a93d2b0d4b50b335a6c3eed 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -346,7 +346,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
org.bukkit.generator.BiomeProvider biomeProvider // CraftBukkit
) {
// CraftBukkit start
- super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules()))); // Paper - create paper world configs
+ super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor
this.pvpMode = server.isPvpAllowed();
this.levelStorageAccess = levelStorageAccess;
this.uuid = org.bukkit.craftbukkit.util.WorldUUID.getOrCreate(levelStorageAccess.levelDirectory.path().toFile());
diff --git a/net/minecraft/server/level/ServerPlayerGameMode.java b/net/minecraft/server/level/ServerPlayerGameMode.java
index b29ecfcc07304c1ee3d77cd431db0889ebc7d6b1..6734756d7a51e635a50a47577f9e6b6f8111db51 100644
--- a/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -296,6 +296,7 @@ public class ServerPlayerGameMode {
org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDamageAbortEvent(this.player, pos, this.player.getInventory().getSelectedItem()); // CraftBukkit
}
}
+ this.level.chunkPacketBlockController.onPlayerLeftClickBlock(this, pos, action, face, maxBuildHeight, sequence); // Paper - Anti-Xray
}
public void destroyAndAck(BlockPos pos, int sequence, String message) {
diff --git a/net/minecraft/server/network/PlayerChunkSender.java b/net/minecraft/server/network/PlayerChunkSender.java
index 8cd7363704d4532c926dbf4778eb4dfec5fdf8bc..0376a10ee0544b13e8fd629a7b13f78811e57a30 100644
--- a/net/minecraft/server/network/PlayerChunkSender.java
+++ b/net/minecraft/server/network/PlayerChunkSender.java
@@ -78,8 +78,11 @@ public class PlayerChunkSender {
}
}
- private static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) {
- packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null));
+ // Paper start - Anti-Xray
+ public static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) {
+ final boolean shouldModify = level.chunkPacketBlockController.shouldModify(packetListener.player, chunk);
+ packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify));
+ // Paper end - Anti-Xray
// Paper start - PlayerChunkLoadEvent
if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) {
new io.papermc.paper.event.packet.PlayerChunkLoadEvent(new org.bukkit.craftbukkit.CraftChunk(chunk), packetListener.getPlayer().getBukkitEntity()).callEvent();
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index b595fc3bd844eaf2a56f70f166523ee6254386b6..891d3e13057c6034c59a78e594935c433921de04 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -408,7 +408,7 @@ public abstract class PlayerList {
.getOrThrow(net.minecraft.world.level.biome.Biomes.PLAINS);
player.connection.send(new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket(
new net.minecraft.world.level.chunk.EmptyLevelChunk(serverLevel, player.chunkPosition(), plains),
- serverLevel.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null)
+ serverLevel.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null, true) // Paper - Anti-Xray
);
}
// Paper end - Send empty chunk
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 980eaba27ce2616c1573a4760cf4acc2dd251190..1eb8cb187d33510a4e99d229e721a2e7db4012ad 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -132,6 +132,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
private long subTickCount;
// CraftBukkit start
+ public final io.papermc.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
private final CraftWorld world;
public boolean pvpMode;
public @Nullable org.bukkit.generator.ChunkGenerator generator;
@@ -201,7 +202,8 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
@Nullable org.bukkit.generator.BiomeProvider biomeProvider, // Paper
org.bukkit.World.Environment environment, // Paper
java.util.function.Function<org.spigotmc.SpigotWorldConfig, // Spigot - create per world config
- io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator // Paper - create paper world config
+ io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator, // Paper - create paper world config
+ java.util.concurrent.Executor executor // Paper - Anti-Xray
) {
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot
this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
@@ -278,6 +280,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
public void onBorderSetDamageSafeZOne(WorldBorder border, double safeZoneRadius) {}
});
// CraftBukkit end
+ this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new io.papermc.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : io.papermc.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray
}
// Paper start - Cancel hit for vanished players
@@ -483,6 +486,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
snapshot.setFlags(flags); // Paper - always set the flag of the most recent call to mitigate issues with multiple update at the same pos with different flags
}
BlockState blockState = chunkAt.setBlockState(pos, state, flags);
+ this.chunkPacketBlockController.onBlockChange(this, pos, state, blockState, flags, recursionLeft); // Paper - Anti-Xray
// CraftBukkit end
if (blockState == null) {
// CraftBukkit start - remove blockstate if failed (or the same)
diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java
index f98ee299f5900b0a66c447d7970bd16a7ced1086..d1ae452f0aa96c36afe8b7ecddd3e06b06165878 100644
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -115,14 +115,14 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh
}
}
- replaceMissingSections(biomeRegistry, this.sections);
+ this.replaceMissingSections(biomeRegistry, this.sections); // Paper - Anti-Xray - make it a non-static method
this.biomeRegistry = biomeRegistry; // CraftBukkit
}
- private static void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sections) {
+ private void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sections) { // Paper - Anti-Xray - make it a non-static method
for (int i = 0; i < sections.length; i++) {
if (sections[i] == null) {
- sections[i] = new LevelChunkSection(biomeRegistry);
+ sections[i] = new LevelChunkSection(biomeRegistry, this.levelHeightAccessor instanceof net.minecraft.world.level.Level ? (net.minecraft.world.level.Level) this.levelHeightAccessor : null, this.chunkPos, this.levelHeightAccessor.getSectionYFromSectionIndex(i)); // Paper - Anti-Xray - Add parameters
}
}
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 1e60b295f8fa672ecee96a71fd1fea14339b7054..6f19586b4a39541c0fb895a18a0a4fd9b5da504c 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -112,7 +112,7 @@ public class LevelChunk extends ChunkAccess {
@Nullable LevelChunk.PostLoadProcessor postLoad,
@Nullable BlendingData blendingData
) {
- super(pos, data, level, level.registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData);
+ super(pos, data, level, net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry
this.level = (ServerLevel) level; // CraftBukkit - type
this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>();
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
index 2399a0f8839c0925a9923bae87362c2c9a217197..1aa4e39431a93a7789b74a2e3476a3e47605e2cc 100644
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -38,9 +38,15 @@ public class LevelChunkSection {
this.recalcBlockCounts();
}
+ // Paper start - Anti-Xray - Add parameters
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public LevelChunkSection(Registry<Biome> biomeRegistry) {
- this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
- this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
+ this(biomeRegistry, null, null, 0);
+ }
+ public LevelChunkSection(Registry<Biome> biomeRegistry, net.minecraft.world.level.Level level, net.minecraft.world.level.ChunkPos chunkPos, int chunkSectionY) {
+ // Paper end - Anti-Xray
+ this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, level == null || level.chunkPacketBlockController == null ? null : level.chunkPacketBlockController.getPresetBlockStates(level, chunkPos, chunkSectionY)); // Paper - Anti-Xray - Add preset block states
+ this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes
}
public BlockState getBlockState(int x, int y, int z) {
@@ -168,10 +174,16 @@ public class LevelChunkSection {
this.biomes = palettedContainer;
}
+ // Paper start - Anti-Xray - Add chunk packet info
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public void write(FriendlyByteBuf buffer) {
+ this.write(buffer, null, 0);
+ }
+ public void write(FriendlyByteBuf buffer, io.papermc.paper.antixray.ChunkPacketInfo<BlockState> chunkPacketInfo, int chunkSectionIndex) {
buffer.writeShort(this.nonEmptyBlockCount);
- this.states.write(buffer);
- this.biomes.write(buffer);
+ this.states.write(buffer, chunkPacketInfo, chunkSectionIndex);
+ this.biomes.write(buffer, null, chunkSectionIndex);
+ // Paper end - Anti-Xray - Add chunk packet info
}
public int getSerializedSize() {
diff --git a/net/minecraft/world/level/chunk/PalettedContainer.java b/net/minecraft/world/level/chunk/PalettedContainer.java
index cd2b1240f13acaaecbb8e6d0f7f8b326ab24b5b0..9b0f17841230640f532ac33bb86e6585e88b5a57 100644
--- a/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/net/minecraft/world/level/chunk/PalettedContainer.java
@@ -27,6 +27,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
private static final int MIN_PALETTE_BITS = 0;
private final PaletteResize<T> dummyPaletteResize = (bits, objectAdded) -> 0;
public final IdMap<T> registry;
+ private final T @org.jetbrains.annotations.Nullable [] presetValues; // Paper - Anti-Xray - Add preset values
private volatile PalettedContainer.Data<T> data;
private final PalettedContainer.Strategy strategy;
//private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused
@@ -39,13 +40,21 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
// this.threadingDetector.checkAndUnlock(); // Paper - disable this - use proper synchronization
}
+ // Paper start - Anti-Xray - Add preset values
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value) {
- PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = PalettedContainer::unpack;
+ return PalettedContainer.codecRW(registry, codec, strategy, value, null);
+ }
+ public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value, T @org.jetbrains.annotations.Nullable [] presetValues) {
+ PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = (idListx, paletteProviderx, serialized) -> {
+ return unpack(idListx, paletteProviderx, serialized, value, presetValues);
+ };
+ // Paper end
return codec(registry, codec, strategy, value, unpacker);
}
public static <T> Codec<PalettedContainerRO<T>> codecRO(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value) {
- PalettedContainerRO.Unpacker<T, PalettedContainerRO<T>> unpacker = (registry1, strategy1, packedData) -> unpack(registry1, strategy1, packedData)
+ PalettedContainerRO.Unpacker<T, PalettedContainerRO<T>> unpacker = (registry1, strategy1, packedData) -> unpack(registry1, strategy1, packedData, value, null) // Paper - Anti-Xray - Add preset values
.map(container -> (PalettedContainerRO<T>)container);
return codec(registry, codec, strategy, value, unpacker);
}
@@ -65,27 +74,66 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
);
}
+ // Paper start - Anti-Xray - Add preset values
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
+ public PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values) {
+ this(registry, strategy, configuration, storage, values, null, null);
+ }
public PalettedContainer(
- IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values
+ IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues
) {
+ this.presetValues = presetValues;
this.registry = registry;
this.strategy = strategy;
this.data = new PalettedContainer.Data<>(configuration, storage, configuration.factory().create(configuration.bits(), registry, this, values));
+ if (presetValues != null && (configuration.factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY ? this.data.palette.valueFor(0) != defaultValue : configuration.factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY)) {
+ // In 1.18 Mojang unfortunately removed code that already handled possible resize operations on read from disk for us
+ // We readd this here but in a smarter way than it was before
+ int maxSize = 1 << configuration.bits();
+
+ for (T presetValue : presetValues) {
+ if (this.data.palette.getSize() >= maxSize) {
+ java.util.Set<T> allValues = new java.util.HashSet<>(values);
+ allValues.addAll(Arrays.asList(presetValues));
+ int newBits = Mth.ceillog2(allValues.size());
+
+ if (newBits > configuration.bits()) {
+ this.onResize(newBits, null);
+ }
+
+ break;
+ }
+
+ this.data.palette.idFor(presetValue);
+ }
+ }
+ // Paper end
}
- private PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Data<T> data) {
+ // Paper start - Anti-Xray - Add preset values
+ private PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Data<T> data, T @org.jetbrains.annotations.Nullable [] presetValues) {
+ this.presetValues = presetValues;
+ // Paper end - Anti-Xray
this.registry = registry;
this.strategy = strategy;
this.data = data;
}
- private PalettedContainer(PalettedContainer<T> other) {
+ private PalettedContainer(PalettedContainer<T> other, T @org.jetbrains.annotations.Nullable [] presetValues) { // Paper - Anti-Xray - Add preset values
+ this.presetValues = presetValues; // Paper - Anti-Xray - Add preset values
this.registry = other.registry;
this.strategy = other.strategy;
this.data = other.data.copy(this);
}
+ // Paper start - Anti-Xray - Add preset values
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public PalettedContainer(IdMap<T> registry, T palette, PalettedContainer.Strategy strategy) {
+ this(registry, palette, strategy, null);
+ }
+ public PalettedContainer(IdMap<T> registry, T palette, PalettedContainer.Strategy strategy, T @org.jetbrains.annotations.Nullable [] presetValues) {
+ this.presetValues = presetValues;
+ // Paper end - Anti-Xray
this.strategy = strategy;
this.registry = registry;
this.data = this.createOrReuseData(null, 0);
@@ -100,11 +148,30 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@Override
public synchronized int onResize(int bits, T objectAdded) { // Paper - synchronize
PalettedContainer.Data<T> data = this.data;
+ // Paper start - Anti-Xray - Add preset values
+ if (this.presetValues != null && objectAdded != null && data.configuration().factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY) {
+ int duplicates = 0;
+ List<T> presetValues = Arrays.asList(this.presetValues);
+ duplicates += presetValues.contains(objectAdded) ? 1 : 0;
+ duplicates += presetValues.contains(data.palette.valueFor(0)) ? 1 : 0;
+ bits = Mth.ceillog2((1 << this.strategy.calculateBitsForSerialization(this.registry, 1 << bits)) + presetValues.size() - duplicates);
+ }
+ // Paper end - Anti-Xray
PalettedContainer.Data<T> data1 = this.createOrReuseData(data, bits);
data1.copyFrom(data.palette, data.storage);
this.data = data1;
- return data1.palette.idFor(objectAdded);
+ // Paper start - Anti-Xray
+ this.addPresetValues();
+ return objectAdded == null ? -1 : data1.palette.idFor(objectAdded);
+ }
+ private void addPresetValues() {
+ if (this.presetValues != null && this.data.configuration().factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY) {
+ for (T presetValue : this.presetValues) {
+ this.data.palette.idFor(presetValue);
+ }
+ }
}
+ // Paper end - Anti-Xray
public synchronized T getAndSet(int x, int y, int z, T state) { // Paper - synchronize
this.acquire();
@@ -171,24 +238,35 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
data.palette.read(buffer);
buffer.readFixedSizeLongArray(data.storage.getRaw());
this.data = data;
+ this.addPresetValues(); // Paper - Anti-Xray - Add preset values (inefficient, but this isn't used by the server)
} finally {
this.release();
}
}
+ // Paper start - Anti-Xray; Add chunk packet info
@Override
- public synchronized void write(FriendlyByteBuf buffer) { // Paper - synchronize
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
+ public void write(FriendlyByteBuf buffer) {
+ this.write(buffer, null, 0);
+ }
+ @Override
+ public synchronized void write(FriendlyByteBuf buffer, @Nullable io.papermc.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) { // Paper - Synchronize
this.acquire();
try {
- this.data.write(buffer);
+ this.data.write(buffer, chunkPacketInfo, chunkSectionIndex);
+ if (chunkPacketInfo != null) {
+ chunkPacketInfo.setPresetValues(chunkSectionIndex, this.presetValues);
+ }
+ // Paper end - Anti-Xray
} finally {
this.release();
}
}
private static <T> DataResult<PalettedContainer<T>> unpack(
- IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainerRO.PackedData<T> packedData
+ IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainerRO.PackedData<T> packedData, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues // Paper - Anti-Xray - Add preset values
) {
List<T> list = packedData.paletteEntries();
int size = strategy.size();
@@ -221,7 +299,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
}
}
- return DataResult.success(new PalettedContainer<>(registry, strategy, configuration, bitStorage, list));
+ return DataResult.success(new PalettedContainer<>(registry, strategy, configuration, bitStorage, list, defaultValue, presetValues)); // Paper - Anti-Xray - Add preset values
}
@Override
@@ -279,12 +357,12 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@Override
public PalettedContainer<T> copy() {
- return new PalettedContainer<>(this);
+ return new PalettedContainer<>(this, this.presetValues); // Paper - Anti-Xray - Add preset values
}
@Override
public PalettedContainer<T> recreate() {
- return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy);
+ return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy, this.presetValues); // Paper - Anti-Xray - Add preset values
}
@Override
@@ -323,9 +401,16 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
return 1 + this.palette.getSerializedSize() + this.storage.getRaw().length * 8;
}
- public void write(FriendlyByteBuf buffer) {
+ // Paper start - Anti-Xray - Add chunk packet info
+ public void write(FriendlyByteBuf buffer, @Nullable io.papermc.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) {
buffer.writeByte(this.storage.getBits());
this.palette.write(buffer);
+ if (chunkPacketInfo != null) {
+ chunkPacketInfo.setBits(chunkSectionIndex, this.configuration.bits());
+ chunkPacketInfo.setPalette(chunkSectionIndex, this.palette);
+ chunkPacketInfo.setIndex(chunkSectionIndex, buffer.writerIndex());
+ }
+ // Paper end - Anti-Xray - Add chunk packet info
buffer.writeFixedSizeLongArray(this.storage.getRaw());
}
diff --git a/net/minecraft/world/level/chunk/PalettedContainerRO.java b/net/minecraft/world/level/chunk/PalettedContainerRO.java
index bfbb1a2bb4abbb369a24f2f01439e9ea3e16794b..8d6ed8be4d93f7d4e6ea80c351020d88ee98aa4d 100644
--- a/net/minecraft/world/level/chunk/PalettedContainerRO.java
+++ b/net/minecraft/world/level/chunk/PalettedContainerRO.java
@@ -14,7 +14,10 @@ public interface PalettedContainerRO<T> {
void getAll(Consumer<T> consumer);
- void write(FriendlyByteBuf buffer);
+ // Paper start - Anti-Xray - Add chunk packet info
+ @Deprecated @io.papermc.paper.annotation.DoNotUse void write(FriendlyByteBuf buffer);
+ void write(FriendlyByteBuf buffer, @javax.annotation.Nullable io.papermc.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex);
+ // Paper end
int getSerializedSize();
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index e846bda9517f3325f0d343758d3235382bb8980d..15417fab103feec3c1f7d5bd5b332e89d3ace3f5 100644
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
@@ -96,7 +96,7 @@ public record SerializableChunkData(
, @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer
) {
public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(
- Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState()
+ Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null // Paper - Anti-Xray
);
private static final Codec<List<SavedTick<Block>>> BLOCK_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.BLOCK.byNameCodec()).listOf();
private static final Codec<List<SavedTick<Fluid>>> FLUID_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.FLUID.byNameCodec()).listOf();
@@ -132,6 +132,7 @@ public record SerializableChunkData(
@Nullable
public static SerializableChunkData parse(LevelHeightAccessor levelHeightAccessor, RegistryAccess registries, CompoundTag tag) {
+ net.minecraft.server.level.ServerLevel serverLevel = (net.minecraft.server.level.ServerLevel) levelHeightAccessor; // Paper - Anti-Xray This is is seemingly only called from ChunkMap, where, we have a server level. We'll fight this later if needed.
if (tag.getString("Status").isEmpty()) {
return null;
} else {
@@ -192,15 +193,17 @@ public record SerializableChunkData(
int byteOr = compoundTag.getByteOr("Y", (byte)0);
LevelChunkSection levelChunkSection;
if (byteOr >= levelHeightAccessor.getMinSectionY() && byteOr <= levelHeightAccessor.getMaxSectionY()) {
+ final BlockState[] presetBlockStates = serverLevel.chunkPacketBlockController.getPresetBlockStates(serverLevel, chunkPos, byteOr); // Paper - Anti-Xray - Add preset block states
+ final Codec<PalettedContainer<BlockState>> blockStateCodec = presetBlockStates == null ? BLOCK_STATE_CODEC : PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), presetBlockStates); // Paper - Anti-Xray
PalettedContainer<BlockState> palettedContainer = compoundTag.getCompound("block_states")
.map(
- compoundTag1 -> BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, compoundTag1)
+ compoundTag1 -> blockStateCodec.parse(NbtOps.INSTANCE, compoundTag1) // Paper - Anti-Xray
.promotePartial(string -> logErrors(chunkPos, byteOr, string))
.getOrThrow(SerializableChunkData.ChunkReadException::new)
)
.orElseGet(
() -> new PalettedContainer<>(
- Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES
+ Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, presetBlockStates // Paper - Anti-Xray - Add preset biomes
)
);
PalettedContainer<Holder<Biome>> palettedContainerRo = compoundTag.getCompound("biomes") // CraftBukkit - read/write
@@ -211,7 +214,7 @@ public record SerializableChunkData(
)
.orElseGet(
() -> new PalettedContainer<>(
- registry.asHolderIdMap(), registry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES
+ registry.asHolderIdMap(), registry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null // Paper - Anti-Xray - Add preset biomes
)
);
levelChunkSection = new LevelChunkSection(palettedContainer, palettedContainerRo);
@@ -384,7 +387,7 @@ public record SerializableChunkData(
// CraftBukkit start - read/write
private static Codec<PalettedContainer<Holder<Biome>>> makeBiomeCodecRW(Registry<Biome> biomeRegistry) {
- return PalettedContainer.codecRW(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS));
+ return PalettedContainer.codecRW(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS), null); // Paper - Anti-Xray - Add preset biomes
}
// CraftBukkit end