Fix lava and snow cauldrons looking wrong

Fixes #2955
This commit is contained in:
Camotoy 2022-04-30 20:40:34 -04:00
parent 8a1799e0e3
commit e66014af9e
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
4 changed files with 58 additions and 32 deletions

View file

@ -46,6 +46,7 @@ import java.util.Locale;
public final class BlockStateValues {
private static final Int2IntMap BANNER_COLORS = new FixedInt2IntMap();
private static final Int2ByteMap BED_COLORS = new FixedInt2ByteMap();
private static final IntSet NON_WATER_CAULDRONS = new IntOpenHashSet();
private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap();
private static final Int2ObjectMap<DoubleChestValue> DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<String> FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>();
@ -62,6 +63,7 @@ public final class BlockStateValues {
private static final Int2ByteMap SKULL_ROTATIONS = new Int2ByteOpenHashMap();
private static final Int2IntMap SKULL_WALL_DIRECTIONS = new Int2IntOpenHashMap();
private static final Int2ByteMap SHULKERBOX_DIRECTIONS = new FixedInt2ByteMap();
private static final IntSet WATER_CAULDRONS = new IntOpenHashSet();
private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap();
public static final int JAVA_AIR_ID = 0;
@ -176,7 +178,7 @@ public final class BlockStateValues {
return;
}
if (javaId.startsWith("minecraft:water")) {
if (javaId.startsWith("minecraft:water") && !javaId.contains("cauldron")) {
String strLevel = javaId.substring(javaId.lastIndexOf("level=") + 6, javaId.length() - 1);
int level = Integer.parseInt(strLevel);
WATER_LEVEL.put(javaBlockState, level);
@ -189,6 +191,11 @@ public final class BlockStateValues {
if (direction.isHorizontal()) {
HORIZONTAL_FACING_JIGSAWS.add(javaBlockState);
}
return;
}
if (javaId.contains("_cauldron") && !javaId.contains("water_")) {
NON_WATER_CAULDRONS.add(javaBlockState);
}
}
@ -214,6 +221,15 @@ public final class BlockStateValues {
return BED_COLORS.getOrDefault(state, (byte) -1);
}
/**
* Non-water cauldrons (since Bedrock 1.18.30) must have a block entity packet sent on chunk load to fix rendering issues.
*
* @return if this Java block state is a non-empty non-water cauldron
*/
public static boolean isCauldron(int state) {
return NON_WATER_CAULDRONS.contains(state);
}
/**
* The block state in Java and Bedrock both contain the conditional bit, however command block block entity tags
* in Bedrock need the conditional information.

View file

@ -26,7 +26,10 @@
package org.geysermc.geyser.translator.level.block.entity;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.NbtList;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtType;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
/**
@ -59,6 +62,18 @@ public interface BedrockOnlyBlockEntity extends RequiresBlockState {
return FlowerPotBlockEntityTranslator.getTag(session, blockState, position);
} else if (PistonBlockEntityTranslator.isBlock(blockState)) {
return PistonBlockEntityTranslator.getTag(blockState, position);
} else if (BlockStateValues.isCauldron(blockState)) {
// As of 1.18.30: this is required to make rendering not look weird on chunk load (lava and snow cauldrons look dim)
return NbtMap.builder()
.putString("id", "Cauldron")
.putByte("isMovable", (byte) 0)
.putShort("PotionId", (short) -1)
.putShort("PotionType", (short) -1)
.putList("Items", NbtType.END, NbtList.EMPTY)
.putInt("x", position.getX())
.putInt("y", position.getY())
.putInt("z", position.getZ())
.build();
}
return null;
}

View file

@ -99,7 +99,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
final List<NbtMap> bedrockBlockEntities = new ObjectArrayList<>(blockEntities.length);
BitSet waterloggedPaletteIds = new BitSet();
BitSet pistonOrFlowerPaletteIds = new BitSet();
BitSet bedrockOnlyBlockEntityIds = new BitSet();
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension();
int maxBedrockSectionY = (bedrockDimension.height() >> 4) - 1;
@ -144,7 +144,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
if (BlockStateValues.getFlowerPotValues().containsKey(javaId) || BlockStateValues.getPistonValues().containsKey(javaId)) {
if (BlockStateValues.getFlowerPotValues().containsKey(javaId) || BlockStateValues.getPistonValues().containsKey(javaId) || BlockStateValues.isCauldron(javaId)) {
bedrockBlockEntities.add(BedrockOnlyBlockEntity.getTag(session,
Vector3i.from((packet.getX() << 4) + (yzx & 0xF), ((sectionY + yOffset) << 4) + ((yzx >> 8) & 0xF), (packet.getZ() << 4) + ((yzx >> 4) & 0xF)),
javaId
@ -173,7 +173,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
IntList bedrockPalette = new IntArrayList(javaPalette.size());
waterloggedPaletteIds.clear();
pistonOrFlowerPaletteIds.clear();
bedrockOnlyBlockEntityIds.clear();
// Iterate through palette and convert state IDs to Bedrock, doing some additional checks as we go
for (int i = 0; i < javaPalette.size(); i++) {
@ -184,19 +184,19 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
waterloggedPaletteIds.set(i);
}
// 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)) {
pistonOrFlowerPaletteIds.set(i);
// Check if block is piston, flower or cauldron 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) || BlockStateValues.isCauldron(javaId)) {
bedrockOnlyBlockEntityIds.set(i);
}
}
// Add Bedrock-exclusive block entities
// We only if the palette contained any blocks that are Bedrock-exclusive block entities to avoid iterating through the whole block data
// for no reason, as most sections will not contain any pistons or flower pots
if (!pistonOrFlowerPaletteIds.isEmpty()) {
if (!bedrockOnlyBlockEntityIds.isEmpty()) {
for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) {
int paletteId = javaData.get(yzx);
if (pistonOrFlowerPaletteIds.get(paletteId)) {
if (bedrockOnlyBlockEntityIds.get(paletteId)) {
bedrockBlockEntities.add(BedrockOnlyBlockEntity.getTag(session,
Vector3i.from((packet.getX() << 4) + (yzx & 0xF), ((sectionY + yOffset) << 4) + ((yzx >> 8) & 0xF), (packet.getZ() << 4) + ((yzx >> 4) & 0xF)),
javaPalette.idToState(paletteId)
@ -233,9 +233,9 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
}
// V1 palette
IntList layer1Palette = new IntArrayList(2);
layer1Palette.add(session.getBlockMappings().getBedrockAirId()); // Air - see BlockStorage's constructor for more information
layer1Palette.add(session.getBlockMappings().getBedrockWaterId());
IntList layer1Palette = IntList.of(
session.getBlockMappings().getBedrockAirId(), // Air - see BlockStorage's constructor for more information
session.getBlockMappings().getBedrockWaterId());
layers = new BlockStorage[]{ layer0, new BlockStorage(BitArrayVersion.V1.createArray(BlockStorage.SIZE, layer1Data), layer1Palette) };
}

View file

@ -30,15 +30,15 @@ import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.level.block.entity.BedrockOnlyBlockEntity;
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
import org.geysermc.geyser.translator.level.block.entity.FlowerPotBlockEntityTranslator;
import org.geysermc.geyser.registry.Registries;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class BlockEntityUtils {
@ -46,27 +46,22 @@ public class BlockEntityUtils {
* A list of all block entities that require the Java block state in order to fill out their block entity information.
* This list will be smaller with cache sections on as we don't need to double-cache data
*/
public static final ObjectArrayList<BedrockOnlyBlockEntity> BEDROCK_ONLY_BLOCK_ENTITIES = new ObjectArrayList<>();
public static final List<BedrockOnlyBlockEntity> BEDROCK_ONLY_BLOCK_ENTITIES = List.of(
(BedrockOnlyBlockEntity) Registries.BLOCK_ENTITIES.get().get(BlockEntityType.CHEST),
new FlowerPotBlockEntityTranslator()
);
/**
* Contains a list of irregular block entity name translations that can't be fit into the regex
*/
public static final Map<BlockEntityType, String> BLOCK_ENTITY_TRANSLATIONS = new HashMap<>() {
{
public static final Map<BlockEntityType, String> BLOCK_ENTITY_TRANSLATIONS = Map.of(
// Bedrock/Java differences
put(BlockEntityType.ENCHANTING_TABLE, "EnchantTable");
put(BlockEntityType.JIGSAW, "JigsawBlock");
put(BlockEntityType.PISTON, "PistonArm");
put(BlockEntityType.TRAPPED_CHEST, "Chest");
BlockEntityType.ENCHANTING_TABLE, "EnchantTable",
BlockEntityType.JIGSAW, "JigsawBlock",
BlockEntityType.PISTON, "PistonArm",
BlockEntityType.TRAPPED_CHEST, "Chest"
// There are some legacy IDs sent but as far as I can tell they are not needed for things to work properly
}
};
static {
// Seeing as there are only two - and, hopefully, will only ever be two - we can hardcode this
BEDROCK_ONLY_BLOCK_ENTITIES.add((BedrockOnlyBlockEntity) Registries.BLOCK_ENTITIES.get().get(BlockEntityType.CHEST));
BEDROCK_ONLY_BLOCK_ENTITIES.add(new FlowerPotBlockEntityTranslator());
}
);
public static String getBedrockBlockEntityId(BlockEntityType type) {
// These are the only exceptions when it comes to block entity ids
@ -77,9 +72,9 @@ public class BlockEntityUtils {
String id = type.name();
// Split at every space or capital letter - for the latter, some legacy Java block entity tags are the correct format already
String[] words = id.split("_");;
String[] words = id.split("_");
for (int i = 0; i < words.length; i++) {
words[i] = words[i].substring(0, 1).toUpperCase() + words[i].substring(1).toLowerCase();
words[i] = words[i].substring(0, 1).toUpperCase(Locale.ROOT) + words[i].substring(1).toLowerCase(Locale.ROOT);
}
return String.join("", words);