Keep hidden block entities in chunks without their data
Pull Request Build / Build (pull_request) Successful in 1m38s
Backport CommonCore / Create CommonCore backport PRs (pull_request) Successful in 5s

Hidden block entities were removed from chunk data, so the client did not
render blocks like shulker boxes at all. They are now sent with empty data,
hiding their contents but not the block itself.
This commit is contained in:
2026-10-31 22:24:51 +01:00
parent 4dbe2ccac7
commit 9eea5f5e25
2 changed files with 23 additions and 11 deletions
@@ -23,6 +23,7 @@ import de.steamwar.core.ProtocolUtils;
import de.steamwar.techhider.AccessPrivilegeProvider.ForPlayer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.util.SimpleBitStorage;
@@ -31,6 +32,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.UnaryOperator;
@@ -263,21 +265,30 @@ public class ChunkHider {
private List<ClientboundLevelChunkPacketData.BlockEntityInfo> filterBlockEntities(ForPlayer access, List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntities, int chunkX, int chunkZ) {
int fourBitBitmask = 0b0000_1111;
return blockEntities.stream()
.filter((blockEntityInfo) -> {
BlockEntityType<?> type = blockEntityInfo.type;
int packedXZ = blockEntityInfo.packedXZ;
List<ClientboundLevelChunkPacketData.BlockEntityInfo> filtered = new ArrayList<>(blockEntities.size());
for (ClientboundLevelChunkPacketData.BlockEntityInfo blockEntityInfo : blockEntities) {
BlockEntityType<?> type = blockEntityInfo.type;
int packedXZ = blockEntityInfo.packedXZ;
int localX = (packedXZ >> 4) & fourBitBitmask;
int localZ = packedXZ & fourBitBitmask;
int localX = (packedXZ >> 4) & fourBitBitmask;
int localZ = packedXZ & fourBitBitmask;
int worldX = (chunkX * SECTION_SPAN_SIZE) + localX;
int worldZ = (chunkZ * SECTION_SPAN_SIZE) + localZ;
int worldX = (chunkX * SECTION_SPAN_SIZE) + localX;
int worldZ = (chunkZ * SECTION_SPAN_SIZE) + localZ;
int worldY = blockEntityInfo.y;
int worldY = blockEntityInfo.y;
return access.canAccessPosition(worldX, worldY, worldZ) && access.canAccessBlockEntity(worldX, worldY, worldZ, type);
}).toList();
// A hidden position is obfuscated as a whole
if (!access.canAccessPosition(worldX, worldY, worldZ)) continue;
if (access.canAccessBlockEntity(worldX, worldY, worldZ, type)) {
filtered.add(blockEntityInfo);
} else {
// Without its block entity, the client would not render blocks like shulker boxes, so only their data is hidden
filtered.add(new ClientboundLevelChunkPacketData.BlockEntityInfo(packedXZ, worldY, type, new CompoundTag()));
}
}
return filtered;
}
private void copyOverSectionBiomeData(ByteBuf oldData, ByteBuf newData) {
+1
View File
@@ -25,3 +25,4 @@ accessible class net/minecraft/network/protocol/game/ClientboundLevelChunkPacket
accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo type Lnet/minecraft/world/level/block/entity/BlockEntityType;
accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo packedXZ I
accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo y I
accessible method net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo <init> (IILnet/minecraft/world/level/block/entity/BlockEntityType;Lnet/minecraft/nbt/CompoundTag;)V