Gracefully handle invalid block entity types

This commit is contained in:
Camotoy 2021-11-30 15:06:47 -05:00
parent 295e71627b
commit 75d1a6364c
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
4 changed files with 26 additions and 26 deletions

View file

@ -155,7 +155,7 @@
<dependency> <dependency>
<groupId>com.github.GeyserMC</groupId> <groupId>com.github.GeyserMC</groupId>
<artifactId>MCProtocolLib</artifactId> <artifactId>MCProtocolLib</artifactId>
<version>ed29b72</version> <version>6edba11</version>
<scope>compile</scope> <scope>compile</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>

View file

@ -35,7 +35,7 @@ import org.geysermc.geyser.translator.text.MessageTranslator;
public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override @Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
if (tag.size() < 5) { if (tag == null || tag.size() < 5) {
return; // These values aren't here return; // These values aren't here
} }
// Java infers from the block state, but Bedrock needs it in the tag // Java infers from the block state, but Bedrock needs it in the tag

View file

@ -46,7 +46,11 @@ public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundB
@Override @Override
public void translate(GeyserSession session, ClientboundBlockEntityDataPacket packet) { public void translate(GeyserSession session, ClientboundBlockEntityDataPacket packet) {
BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(packet.getType()); final BlockEntityType type = packet.getType();
if (type == null) {
return;
}
BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(type);
// The Java block state is used in BlockEntityTranslator.translateTag() to make up for some inconsistencies // The Java block state is used in BlockEntityTranslator.translateTag() to make up for some inconsistencies
// between Java block states and Bedrock block entity data // between Java block states and Bedrock block entity data
int blockState; int blockState;
@ -57,7 +61,7 @@ public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundB
} }
Position position = packet.getPosition(); Position position = packet.getPosition();
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(packet.getType(), position.getX(), position.getY(), position.getZ(), BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(type, position.getX(), position.getY(), position.getZ(),
packet.getNbt(), blockState), packet.getPosition()); packet.getNbt(), blockState), packet.getPosition());
// Check for custom skulls. // Check for custom skulls.
if (session.getPreferencesCache().showCustomSkulls() && packet.getNbt() != null && packet.getNbt().contains("SkullOwner")) { if (session.getPreferencesCache().showCustomSkulls() && packet.getNbt() != null && packet.getNbt().contains("SkullOwner")) {
@ -66,7 +70,7 @@ public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundB
// If block entity is command block, OP permission level is appropriate, player is in creative mode and the NBT is not empty // If block entity is command block, OP permission level is appropriate, player is in creative mode and the NBT is not empty
// TODO 1.18 re-test // TODO 1.18 re-test
if (packet.getType() == BlockEntityType.COMMAND_BLOCK && session.getOpPermissionLevel() >= 2 && if (type == BlockEntityType.COMMAND_BLOCK && session.getOpPermissionLevel() >= 2 &&
session.getGameMode() == GameMode.CREATIVE && packet.getNbt() != null && packet.getNbt().size() > 5) { session.getGameMode() == GameMode.CREATIVE && packet.getNbt() != null && packet.getNbt().size() > 5) {
ContainerOpenPacket openPacket = new ContainerOpenPacket(); ContainerOpenPacket openPacket = new ContainerOpenPacket();
openPacket.setBlockPosition(Vector3i.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ())); openPacket.setBlockPosition(Vector3i.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));

View file

@ -50,6 +50,7 @@ import io.netty.buffer.ByteBufOutputStream;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntLists; import it.unimi.dsi.fastutil.ints.IntLists;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.geysermc.geyser.entity.type.ItemFrameEntity; import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
@ -88,11 +89,12 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
int chunkSize = session.getChunkCache().getChunkHeightY(); int chunkSize = session.getChunkCache().getChunkHeightY();
int biomeGlobalPalette = session.getBiomeGlobalPalette(); int biomeGlobalPalette = session.getBiomeGlobalPalette();
// Temporarily stores compound tags of Bedrock-only block entities
List<NbtMap> bedrockOnlyBlockEntities = new ArrayList<>();
DataPalette[] javaChunks = new DataPalette[chunkSize]; DataPalette[] javaChunks = new DataPalette[chunkSize];
DataPalette[] javaBiomes = new DataPalette[chunkSize]; DataPalette[] javaBiomes = new DataPalette[chunkSize];
final BlockEntityInfo[] blockEntities = packet.getBlockEntities();
final List<NbtMap> bedrockBlockEntities = new ObjectArrayList<>(blockEntities.length);
BitSet waterloggedPaletteIds = new BitSet(); BitSet waterloggedPaletteIds = new BitSet();
BitSet pistonOrFlowerPaletteIds = new BitSet(); BitSet pistonOrFlowerPaletteIds = new BitSet();
@ -140,7 +142,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
// Check if block is piston or flower to see if we'll need to create additional block entities, as they're only block entities in Bedrock // Check if block is piston or flower to see if we'll need to create additional block entities, as they're only block entities in Bedrock
if (BlockStateValues.getFlowerPotValues().containsKey(javaId) || BlockStateValues.getPistonValues().containsKey(javaId)) { if (BlockStateValues.getFlowerPotValues().containsKey(javaId) || BlockStateValues.getPistonValues().containsKey(javaId)) {
bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag(session, bedrockBlockEntities.add(BedrockOnlyBlockEntity.getTag(session,
Vector3i.from((packet.getX() << 4) + (yzx & 0xF), ((sectionY + yOffset) << 4) + ((yzx >> 8) & 0xF), (packet.getZ() << 4) + ((yzx >> 4) & 0xF)), Vector3i.from((packet.getX() << 4) + (yzx & 0xF), ((sectionY + yOffset) << 4) + ((yzx >> 8) & 0xF), (packet.getZ() << 4) + ((yzx >> 4) & 0xF)),
javaId javaId
)); ));
@ -192,7 +194,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) { for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) {
int paletteId = javaData.get(yzx); int paletteId = javaData.get(yzx);
if (pistonOrFlowerPaletteIds.get(paletteId)) { if (pistonOrFlowerPaletteIds.get(paletteId)) {
bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag(session, bedrockBlockEntities.add(BedrockOnlyBlockEntity.getTag(session,
Vector3i.from((packet.getX() << 4) + (yzx & 0xF), ((sectionY + yOffset) << 4) + ((yzx >> 8) & 0xF), (packet.getZ() << 4) + ((yzx >> 4) & 0xF)), Vector3i.from((packet.getX() << 4) + (yzx & 0xF), ((sectionY + yOffset) << 4) + ((yzx >> 8) & 0xF), (packet.getZ() << 4) + ((yzx >> 4) & 0xF)),
javaPalette.idToState(paletteId) javaPalette.idToState(paletteId)
)); ));
@ -240,15 +242,16 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
session.getChunkCache().addToCache(packet.getX(), packet.getZ(), javaChunks); session.getChunkCache().addToCache(packet.getX(), packet.getZ(), javaChunks);
BlockEntityInfo[] blockEntities = packet.getBlockEntities();
NbtMap[] bedrockBlockEntities = new NbtMap[blockEntities.length + bedrockOnlyBlockEntities.size()];
int blockEntityCount = 0;
final int chunkBlockX = packet.getX() << 4; final int chunkBlockX = packet.getX() << 4;
final int chunkBlockZ = packet.getZ() << 4; final int chunkBlockZ = packet.getZ() << 4;
while (blockEntityCount < blockEntities.length) { for (BlockEntityInfo blockEntity : blockEntities) {
BlockEntityInfo blockEntity = blockEntities[blockEntityCount];
CompoundTag tag = blockEntity.getNbt();
BlockEntityType type = blockEntity.getType(); BlockEntityType type = blockEntity.getType();
if (type == null) {
// As an example: ViaVersion will send -1 if it cannot find the block entity type
// Vanilla Minecraft gracefully handles this
continue;
}
CompoundTag tag = blockEntity.getNbt();
int x = blockEntity.getX(); // Relative to chunk int x = blockEntity.getX(); // Relative to chunk
int y = blockEntity.getY(); int y = blockEntity.getY();
int z = blockEntity.getZ(); // Relative to chunk int z = blockEntity.getZ(); // Relative to chunk
@ -259,25 +262,18 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
if (type == BlockEntityType.LECTERN && BlockStateValues.getLecternBookStates().get(blockState)) { if (type == BlockEntityType.LECTERN && BlockStateValues.getLecternBookStates().get(blockState)) {
// If getLecternBookStates is false, let's just treat it like a normal block entity // If getLecternBookStates is false, let's just treat it like a normal block entity
bedrockBlockEntities[blockEntityCount++] = session.getGeyser().getWorldManager().getLecternDataAt( bedrockBlockEntities.add(session.getGeyser().getWorldManager().getLecternDataAt(
session, x + chunkBlockX, y, z + chunkBlockZ, true); session, x + chunkBlockX, y, z + chunkBlockZ, true));
continue; continue;
} }
BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(type); BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(type);
bedrockBlockEntities[blockEntityCount] = blockEntityTranslator.getBlockEntityTag(type, x + chunkBlockX, y, z + chunkBlockZ, tag, blockState); bedrockBlockEntities.add(blockEntityTranslator.getBlockEntityTag(type, x + chunkBlockX, y, z + chunkBlockZ, tag, blockState));
// Check for custom skulls // Check for custom skulls
if (session.getPreferencesCache().showCustomSkulls() && type == BlockEntityType.SKULL && tag != null && tag.contains("SkullOwner")) { if (session.getPreferencesCache().showCustomSkulls() && type == BlockEntityType.SKULL && tag != null && tag.contains("SkullOwner")) {
SkullBlockEntityTranslator.spawnPlayer(session, tag, x + chunkBlockX, y, z + chunkBlockZ, blockState); SkullBlockEntityTranslator.spawnPlayer(session, tag, x + chunkBlockX, y, z + chunkBlockZ, blockState);
} }
blockEntityCount++;
}
// Append Bedrock-exclusive block entities to output array
for (NbtMap tag : bedrockOnlyBlockEntities) {
bedrockBlockEntities[blockEntityCount] = tag;
blockEntityCount++;
} }
// Find highest section // Find highest section
@ -300,7 +296,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
size += ChunkUtils.EMPTY_CHUNK_DATA.length; // Consists only of biome data size += ChunkUtils.EMPTY_CHUNK_DATA.length; // Consists only of biome data
size += 1; // Border blocks size += 1; // Border blocks
size += 1; // Extra data length (always 0) size += 1; // Extra data length (always 0)
size += bedrockBlockEntities.length * 64; // Conservative estimate of 64 bytes per tile entity size += bedrockBlockEntities.size() * 64; // Conservative estimate of 64 bytes per tile entity
// Allocate output buffer // Allocate output buffer
byteBuf = ByteBufAllocator.DEFAULT.buffer(size); byteBuf = ByteBufAllocator.DEFAULT.buffer(size);