Block entity performance improvements (#1481)

* BlockEntity performance improvements

* Use chunk cache if possible for block caching

* Get new block state from ViaVersion if block entity

* Add Javadoc for FlowerPotBlockEntityTranslator.isFlowerBlock

* Remove debug line

* Don't add all RequiresBlockState instances if cache chunks is enabled

* Double chest map get optimization

* Last changes

Co-authored-by: DoctorMacc <toy.fighter1@gmail.com>
This commit is contained in:
Tim203 2020-11-05 22:36:22 +01:00 committed by GitHub
parent 434a2e1500
commit c64d57439f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 362 additions and 404 deletions

View File

@ -43,16 +43,19 @@ import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.GameRule; import org.geysermc.connector.utils.GameRule;
import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.LanguageUtils;
import us.myles.ViaVersion.api.Pair; import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.MappingData;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.protocol.Protocol; import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry; import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion; import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2; import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
public class GeyserSpigotWorldManager extends GeyserWorldManager { public class GeyserSpigotWorldManager extends GeyserWorldManager {
/** /**
* The current client protocol version for ViaVersion usage. * The current client protocol version for ViaVersion usage.
*/ */
@ -99,8 +102,9 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
} }
// Only load in the biomes that are present in this version of Minecraft // Only load in the biomes that are present in this version of Minecraft
for (Biome enumBiome : Biome.values()) { for (Biome enumBiome : Biome.values()) {
if (biomes.has(enumBiome.toString())) { JsonNode biome = biomes.get(enumBiome.toString());
biomeToIdMap.put(enumBiome.ordinal(), biomes.get(enumBiome.toString()).intValue()); if (biome != null) {
biomeToIdMap.put(enumBiome.ordinal(), biome.intValue());
} else { } else {
GeyserConnector.getInstance().getLogger().debug("No biome mapping found for " + enumBiome.toString() + GeyserConnector.getInstance().getLogger().debug("No biome mapping found for " + enumBiome.toString() +
", defaulting to 0"); ", defaulting to 0");
@ -127,30 +131,38 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
public static int getLegacyBlock(GeyserSession session, int x, int y, int z, boolean isViaVersion) { public static int getLegacyBlock(GeyserSession session, int x, int y, int z, boolean isViaVersion) {
if (isViaVersion) { if (isViaVersion) {
return getLegacyBlock(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld(), x, y, z, true); Player bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
// Get block entity storage
BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class);
return getLegacyBlock(storage, bukkitPlayer.getWorld(), x, y, z);
} else { } else {
return BlockTranslator.AIR; return BlockTranslator.AIR;
} }
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public static int getLegacyBlock(World world, int x, int y, int z, boolean isViaVersion) { public static int getLegacyBlock(BlockStorage storage, World world, int x, int y, int z) {
if (isViaVersion) { Block block = world.getBlockAt(x, y, z);
Block block = world.getBlockAt(x, y, z); // Black magic that gets the old block state ID
// Black magic that gets the old block state ID int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF); // Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2
// Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2 blockId = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData().getNewBlockId(blockId);
blockId = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData().getNewBlockId(blockId); List<Pair<Integer, Protocol>> protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
List<Pair<Integer, Protocol>> protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION, ProtocolVersion.v1_13.getId());
ProtocolVersion.v1_13.getId()); // Translate block entity differences - some information was stored in block tags and not block states
for (int i = protocolList.size() - 1; i >= 0; i--) { if (storage.isWelcome(blockId)) { // No getOrDefault method
if (protocolList.get(i).getValue().getMappingData() == null) continue; BlockStorage.ReplacementData data = storage.get(new Position(x, (short) y, z));
blockId = protocolList.get(i).getValue().getMappingData().getNewBlockStateId(blockId); if (data != null && data.getReplacement() != -1) {
blockId = data.getReplacement();
} }
return blockId;
} else {
return BlockTranslator.AIR;
} }
for (int i = protocolList.size() - 1; i >= 0; i--) {
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
if (mappingData != null) {
blockId = mappingData.getNewBlockStateId(blockId);
}
}
return blockId;
} }
@Override @Override
@ -162,11 +174,13 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
return; return;
} }
World world = bukkitPlayer.getWorld(); World world = bukkitPlayer.getWorld();
if (this.isLegacy) { if (this.isLegacy) {
// Get block entity storage
BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class);
for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
for (int blockZ = 0; blockZ < 16; blockZ++) { for (int blockZ = 0; blockZ < 16; blockZ++) {
for (int blockX = 0; blockX < 16; blockX++) { for (int blockX = 0; blockX < 16; blockX++) {
chunk.set(blockX, blockY, blockZ, getLegacyBlock(world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ, true)); chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ));
} }
} }
} }
@ -176,7 +190,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
for (int blockZ = 0; blockZ < 16; blockZ++) { for (int blockZ = 0; blockZ < 16; blockZ++) {
for (int blockX = 0; blockX < 16; blockX++) { for (int blockX = 0; blockX < 16; blockX++) {
Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ); Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), 0); int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.AIR);
chunk.set(blockX, blockY, blockZ, id); chunk.set(blockX, blockY, blockZ, id);
} }
} }

View File

@ -141,6 +141,7 @@ public class ItemFrameEntity extends Entity {
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0); updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(bedrockPosition); updateBlockPacket.setBlockPosition(bedrockPosition);
// TODO 1.16.100 set to BEDROCK_AIR
updateBlockPacket.setRuntimeId(0); updateBlockPacket.setRuntimeId(0);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
@ -196,18 +197,6 @@ public class ItemFrameEntity extends Entity {
return session.getItemFrameCache().getOrDefault(position, -1); return session.getItemFrameCache().getOrDefault(position, -1);
} }
/**
* Determines if the position contains an item frame.
* Does largely the same thing as getItemFrameEntityId, but for speed purposes is implemented separately,
* since every block destroy packet has to check for an item frame.
* @param position position of block.
* @param session GeyserSession.
* @return true if position contains item frame, false if not.
*/
public static boolean positionContainsItemFrame(GeyserSession session, Vector3i position) {
return session.getItemFrameCache().containsKey(position);
}
/** /**
* Force-remove from the position-to-ID map so it doesn't cause conflicts. * Force-remove from the position-to-ID map so it doesn't cause conflicts.
* @param session GeyserSession. * @param session GeyserSession.

View File

@ -194,10 +194,9 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
session.sendUpstreamPacket(blockBreakPacket); session.sendUpstreamPacket(blockBreakPacket);
} }
if (ItemFrameEntity.positionContainsItemFrame(session, packet.getBlockPosition()) && long frameEntityId = ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition());
session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition())) != null) { if (frameEntityId != -1 && session.getEntityCache().getEntityByJavaId(frameEntityId) != null) {
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition()), ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) frameEntityId, InteractAction.ATTACK, session.isSneaking());
InteractAction.ATTACK, session.isSneaking());
session.sendDownstreamPacket(attackPacket); session.sendDownstreamPacket(attackPacket);
break; break;
} }

View File

@ -226,8 +226,14 @@ public class ItemRegistry {
* @return an item entry from the given java edition identifier * @return an item entry from the given java edition identifier
*/ */
public static ItemEntry getItemEntry(String javaIdentifier) { public static ItemEntry getItemEntry(String javaIdentifier) {
return JAVA_IDENTIFIER_MAP.computeIfAbsent(javaIdentifier, key -> ITEM_ENTRIES.values() return JAVA_IDENTIFIER_MAP.computeIfAbsent(javaIdentifier, key -> {
.stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null)); for (ItemEntry entry : ITEM_ENTRIES.values()) {
if (entry.getJavaIdentifier().equals(key)) {
return entry;
}
}
return null;
});
} }
/** /**

View File

@ -51,7 +51,6 @@ import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public abstract class ItemTranslator { public abstract class ItemTranslator {
private static final Int2ObjectMap<ItemTranslator> ITEM_STACK_TRANSLATORS = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<ItemTranslator> ITEM_STACK_TRANSLATORS = new Int2ObjectOpenHashMap<>();
private static final List<NbtItemStackTranslator> NBT_TRANSLATORS; private static final List<NbtItemStackTranslator> NBT_TRANSLATORS;
@ -220,7 +219,7 @@ public abstract class ItemTranslator {
public abstract List<ItemEntry> getAppliedItems(); public abstract List<ItemEntry> getAppliedItems();
public NbtMap translateNbtToBedrock(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag) { public NbtMap translateNbtToBedrock(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag) {
Map<String, Object> javaValue = new HashMap<>(); NbtMapBuilder builder = NbtMap.builder();
if (tag.getValue() != null && !tag.getValue().isEmpty()) { if (tag.getValue() != null && !tag.getValue().isEmpty()) {
for (String str : tag.getValue().keySet()) { for (String str : tag.getValue().keySet()) {
com.github.steveice10.opennbt.tag.builtin.Tag javaTag = tag.get(str); com.github.steveice10.opennbt.tag.builtin.Tag javaTag = tag.get(str);
@ -228,11 +227,9 @@ public abstract class ItemTranslator {
if (translatedTag == null) if (translatedTag == null)
continue; continue;
javaValue.put(javaTag.getName(), translatedTag); builder.put(javaTag.getName(), translatedTag);
} }
} }
NbtMapBuilder builder = NbtMap.builder();
javaValue.forEach(builder::put);
return builder.build(); return builder.build();
} }

View File

@ -26,20 +26,16 @@
package org.geysermc.connector.network.translators.item.translators; package org.geysermc.connector.network.translators.item.translators;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.*;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.nbt.NbtList; import com.nukkitx.nbt.NbtList;
import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType; import com.nukkitx.nbt.NbtType;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -49,54 +45,13 @@ import java.util.stream.Collectors;
@ItemRemapper @ItemRemapper
public class BannerTranslator extends ItemTranslator { public class BannerTranslator extends ItemTranslator {
private final List<ItemEntry> appliedItems; private final List<ItemEntry> appliedItems;
public BannerTranslator() { public BannerTranslator() {
appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("banner")).collect(Collectors.toList()); appliedItems = ItemRegistry.ITEM_ENTRIES.values()
} .stream()
.filter(entry -> entry.getJavaIdentifier().endsWith("banner"))
@Override .collect(Collectors.toList());
public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, itemEntry);
ItemData itemData = super.translateToBedrock(itemStack, itemEntry);
CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag");
if (blockEntityTag.contains("Patterns")) {
ListTag patterns = blockEntityTag.get("Patterns");
NbtMapBuilder builder = itemData.getTag().toBuilder();
builder.put("Patterns", convertBannerPattern(patterns));
itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.build());
}
return itemData;
}
@Override
public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
if (itemData.getTag() == null) return super.translateToJava(itemData, itemEntry);
ItemStack itemStack = super.translateToJava(itemData, itemEntry);
NbtMap nbtTag = itemData.getTag();
if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) {
List<NbtMap> patterns = nbtTag.getList("Patterns", NbtType.COMPOUND);
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
blockEntityTag.put(convertBannerPattern(patterns));
itemStack.getNbt().put(blockEntityTag);
}
return itemStack;
}
@Override
public List<ItemEntry> getAppliedItems() {
return appliedItems;
} }
/** /**
@ -133,7 +88,6 @@ public class BannerTranslator extends ItemTranslator {
return NbtMap.builder() return NbtMap.builder()
.putInt("Color", 15 - (int) pattern.get("Color").getValue()) .putInt("Color", 15 - (int) pattern.get("Color").getValue())
.putString("Pattern", (String) pattern.get("Pattern").getValue())
.putString("Pattern", patternName) .putString("Pattern", patternName)
.build(); .build();
} }
@ -147,8 +101,7 @@ public class BannerTranslator extends ItemTranslator {
public static ListTag convertBannerPattern(List<NbtMap> patterns) { public static ListTag convertBannerPattern(List<NbtMap> patterns) {
List<Tag> tagsList = new ArrayList<>(); List<Tag> tagsList = new ArrayList<>();
for (Object patternTag : patterns) { for (Object patternTag : patterns) {
CompoundTag newPatternTag = getJavaBannerPattern((NbtMap) patternTag); tagsList.add(getJavaBannerPattern((NbtMap) patternTag));
tagsList.add(newPatternTag);
} }
return new ListTag("Patterns", tagsList); return new ListTag("Patterns", tagsList);
@ -167,4 +120,51 @@ public class BannerTranslator extends ItemTranslator {
return new CompoundTag("", tags); return new CompoundTag("", tags);
} }
@Override
public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
if (itemStack.getNbt() == null) {
return super.translateToBedrock(itemStack, itemEntry);
}
ItemData itemData = super.translateToBedrock(itemStack, itemEntry);
CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag");
if (blockEntityTag.contains("Patterns")) {
ListTag patterns = blockEntityTag.get("Patterns");
NbtMapBuilder builder = itemData.getTag().toBuilder();
builder.put("Patterns", convertBannerPattern(patterns));
itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.build());
}
return itemData;
}
@Override
public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
if (itemData.getTag() == null) {
return super.translateToJava(itemData, itemEntry);
}
ItemStack itemStack = super.translateToJava(itemData, itemEntry);
NbtMap nbtTag = itemData.getTag();
if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) {
List<NbtMap> patterns = nbtTag.getList("Patterns", NbtType.COMPOUND);
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
blockEntityTag.put(convertBannerPattern(patterns));
itemStack.getNbt().put(blockEntityTag);
}
return itemStack;
}
@Override
public List<ItemEntry> getAppliedItems() {
return appliedItems;
}
} }

View File

@ -45,14 +45,13 @@ import org.geysermc.connector.utils.ChunkUtils;
@Translator(packet = ServerChunkDataPacket.class) @Translator(packet = ServerChunkDataPacket.class)
public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPacket> { public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPacket> {
/** /**
* Determines if we should process non-full chunks * Determines if we should process non-full chunks
*/ */
private final boolean isCacheChunks; private final boolean cacheChunks;
public JavaChunkDataTranslator() { public JavaChunkDataTranslator() {
isCacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks();
} }
@Override @Override
@ -61,7 +60,7 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
ChunkUtils.updateChunkPosition(session, session.getPlayerEntity().getPosition().toInt()); ChunkUtils.updateChunkPosition(session, session.getPlayerEntity().getPosition().toInt());
} }
if (packet.getColumn().getBiomeData() == null && !isCacheChunks) { if (packet.getColumn().getBiomeData() == null && !cacheChunks) {
// Non-full chunk without chunk caching // Non-full chunk without chunk caching
session.getConnector().getLogger().debug("Not sending non-full chunk because chunk caching is off."); session.getConnector().getLogger().debug("Not sending non-full chunk because chunk caching is off.");
return; return;

View File

@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdate
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.Translator;
@ -40,6 +41,11 @@ import org.geysermc.connector.utils.ChunkUtils;
@Translator(packet = ServerUpdateTileEntityPacket.class) @Translator(packet = ServerUpdateTileEntityPacket.class)
public class JavaUpdateTileEntityTranslator extends PacketTranslator<ServerUpdateTileEntityPacket> { public class JavaUpdateTileEntityTranslator extends PacketTranslator<ServerUpdateTileEntityPacket> {
private final boolean cacheChunks;
public JavaUpdateTileEntityTranslator() {
cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks();
}
@Override @Override
public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) { public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) {
@ -48,16 +54,17 @@ public class JavaUpdateTileEntityTranslator extends PacketTranslator<ServerUpdat
BlockEntityUtils.updateBlockEntity(session, null, packet.getPosition()); BlockEntityUtils.updateBlockEntity(session, null, packet.getPosition());
return; return;
} }
BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(id); BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(id);
// If not null then the BlockState is used in BlockEntityTranslator.translateTag() // The Java block state is used in BlockEntityTranslator.translateTag() to make up for some inconsistencies
if (ChunkUtils.CACHED_BLOCK_ENTITIES.containsKey(packet.getPosition())) { // between Java block states and Bedrock block entity data
int blockState = ChunkUtils.CACHED_BLOCK_ENTITIES.getOrDefault(packet.getPosition(), 0); int blockState = cacheChunks ?
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), // Cache chunks is enabled; use chunk cache
blockState), packet.getPosition()); session.getConnector().getWorldManager().getBlockAt(session, packet.getPosition()) :
ChunkUtils.CACHED_BLOCK_ENTITIES.remove(packet.getPosition(), blockState); // Cache chunks is not enabled; use block entity cache
} else { ChunkUtils.CACHED_BLOCK_ENTITIES.removeInt(packet.getPosition());
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), 0), packet.getPosition()); BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), blockState), packet.getPosition());
}
// 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
if (packet.getType() == UpdatedTileType.COMMAND_BLOCK && session.getOpPermissionLevel() >= 2 && if (packet.getType() == UpdatedTileType.COMMAND_BLOCK && session.getOpPermissionLevel() >= 2 &&
session.getGameMode() == GameMode.CREATIVE && packet.getNbt().size() > 5) { session.getGameMode() == GameMode.CREATIVE && packet.getNbt().size() > 5) {

View File

@ -36,7 +36,6 @@ import java.util.Map;
* Used for block entities if the Java block state contains Bedrock block information. * Used for block entities if the Java block state contains Bedrock block information.
*/ */
public class BlockStateValues { public class BlockStateValues {
private static final Int2IntMap BANNER_COLORS = new Int2IntOpenHashMap(); private static final Int2IntMap BANNER_COLORS = new Int2IntOpenHashMap();
private static final Int2ByteMap BED_COLORS = new Int2ByteOpenHashMap(); private static final Int2ByteMap BED_COLORS = new Int2ByteOpenHashMap();
private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap(); private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap();
@ -52,7 +51,8 @@ public class BlockStateValues {
/** /**
* Determines if the block state contains Bedrock block information * Determines if the block state contains Bedrock block information
* @param entry The String to JsonNode map used in BlockTranslator *
* @param entry The String to JsonNode map used in BlockTranslator
* @param javaBlockState the Java Block State of the block * @param javaBlockState the Java Block State of the block
*/ */
public static void storeBlockStateValues(Map.Entry<String, JsonNode> entry, int javaBlockState) { public static void storeBlockStateValues(Map.Entry<String, JsonNode> entry, int javaBlockState) {
@ -101,7 +101,7 @@ public class BlockStateValues {
} }
JsonNode skullVariation = entry.getValue().get("variation"); JsonNode skullVariation = entry.getValue().get("variation");
if(skullVariation != null) { if (skullVariation != null) {
SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue()); SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue());
} }
@ -124,10 +124,7 @@ public class BlockStateValues {
* @return Banner color integer or -1 if no color * @return Banner color integer or -1 if no color
*/ */
public static int getBannerColor(int state) { public static int getBannerColor(int state) {
if (BANNER_COLORS.containsKey(state)) { return BANNER_COLORS.getOrDefault(state, -1);
return BANNER_COLORS.get(state);
}
return -1;
} }
/** /**
@ -138,10 +135,7 @@ public class BlockStateValues {
* @return Bed color byte or -1 if no color * @return Bed color byte or -1 if no color
*/ */
public static byte getBedColor(int state) { public static byte getBedColor(int state) {
if (BED_COLORS.containsKey(state)) { return BED_COLORS.getOrDefault(state, (byte) -1);
return BED_COLORS.get(state);
}
return -1;
} }
/** /**
@ -157,6 +151,7 @@ public class BlockStateValues {
/** /**
* All double chest values are part of the block state in Java and part of the block entity tag in Bedrock. * All double chest values are part of the block state in Java and part of the block entity tag in Bedrock.
* This gives the DoubleChestValue that can be calculated into the final tag. * This gives the DoubleChestValue that can be calculated into the final tag.
*
* @return The map of all DoubleChestValues. * @return The map of all DoubleChestValues.
*/ */
public static Int2ObjectMap<DoubleChestValue> getDoubleChestValues() { public static Int2ObjectMap<DoubleChestValue> getDoubleChestValues() {
@ -165,6 +160,7 @@ public class BlockStateValues {
/** /**
* Get the Int2ObjectMap of flower pot block states to containing plant * Get the Int2ObjectMap of flower pot block states to containing plant
*
* @return Int2ObjectMap of flower pot values * @return Int2ObjectMap of flower pot values
*/ */
public static Int2ObjectMap<String> getFlowerPotValues() { public static Int2ObjectMap<String> getFlowerPotValues() {
@ -173,6 +169,7 @@ public class BlockStateValues {
/** /**
* Get the map of contained flower pot plants to Bedrock CompoundTag * Get the map of contained flower pot plants to Bedrock CompoundTag
*
* @return Map of flower pot blocks. * @return Map of flower pot blocks.
*/ */
public static Map<String, NbtMap> getFlowerPotBlocks() { public static Map<String, NbtMap> getFlowerPotBlocks() {
@ -182,18 +179,17 @@ public class BlockStateValues {
/** /**
* The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock. * The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock.
* This gives an integer pitch that Bedrock can use. * This gives an integer pitch that Bedrock can use.
*
* @param state BlockState of the block * @param state BlockState of the block
* @return note block note integer or -1 if not present * @return note block note integer or -1 if not present
*/ */
public static int getNoteblockPitch(int state) { public static int getNoteblockPitch(int state) {
if (NOTEBLOCK_PITCHES.containsKey(state)) { return NOTEBLOCK_PITCHES.getOrDefault(state, -1);
return NOTEBLOCK_PITCHES.get(state);
}
return -1;
} }
/** /**
* Get the Int2BooleanMap showing if a piston block state is extended or not. * Get the Int2BooleanMap showing if a piston block state is extended or not.
*
* @return the Int2BooleanMap of piston extensions. * @return the Int2BooleanMap of piston extensions.
*/ */
public static Int2BooleanMap getPistonValues() { public static Int2BooleanMap getPistonValues() {
@ -212,10 +208,7 @@ public class BlockStateValues {
* @return Skull variant byte or -1 if no variant * @return Skull variant byte or -1 if no variant
*/ */
public static byte getSkullVariant(int state) { public static byte getSkullVariant(int state) {
if (SKULL_VARIANTS.containsKey(state)) { return SKULL_VARIANTS.getOrDefault(state, (byte) -1);
return SKULL_VARIANTS.get(state);
}
return -1;
} }
/** /**
@ -226,10 +219,7 @@ public class BlockStateValues {
* @return Skull rotation value or -1 if no value * @return Skull rotation value or -1 if no value
*/ */
public static byte getSkullRotation(int state) { public static byte getSkullRotation(int state) {
if (SKULL_ROTATIONS.containsKey(state)) { return SKULL_ROTATIONS.getOrDefault(state, (byte) -1);
return SKULL_ROTATIONS.get(state);
}
return -1;
} }
@ -241,9 +231,6 @@ public class BlockStateValues {
* @return Shulker direction value or -1 if no value * @return Shulker direction value or -1 if no value
*/ */
public static byte getShulkerBoxDirection(int state) { public static byte getShulkerBoxDirection(int state) {
if (SHULKERBOX_DIRECTIONS.containsKey(state)) { return SHULKERBOX_DIRECTIONS.getOrDefault(state, (byte) -1);
return SHULKERBOX_DIRECTIONS.get(state);
}
return -1;
} }
} }

View File

@ -27,39 +27,31 @@ package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.nukkitx.nbt.NbtMapBuilder;
import org.geysermc.connector.network.translators.item.translators.BannerTranslator; import org.geysermc.connector.network.translators.item.translators.BannerTranslator;
import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "Banner", regex = "banner") @BlockEntity(name = "Banner", regex = "banner")
public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override @Override
public boolean isBlock(int blockState) { public boolean isBlock(int blockState) {
return BlockStateValues.getBannerColor(blockState) != -1; return BlockStateValues.getBannerColor(blockState) != -1;
} }
@Override @Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Map<String, Object> tags = new HashMap<>();
int bannerColor = BlockStateValues.getBannerColor(blockState); int bannerColor = BlockStateValues.getBannerColor(blockState);
if (bannerColor != -1) { if (bannerColor != -1) {
tags.put("Base", 15 - bannerColor); builder.put("Base", 15 - bannerColor);
} }
if (tag.contains("Patterns")) { if (tag.contains("Patterns")) {
ListTag patterns = tag.get("Patterns"); ListTag patterns = tag.get("Patterns");
tags.put("Patterns", BannerTranslator.convertBannerPattern(patterns)); builder.put("Patterns", BannerTranslator.convertBannerPattern(patterns));
} }
if (tag.contains("CustomName")) { if (tag.contains("CustomName")) {
tags.put("CustomName", tag.get("CustomName").getValue()); builder.put("CustomName", tag.get("CustomName").getValue());
} }
return tags;
} }
} }

View File

@ -26,27 +26,23 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.nbt.NbtMapBuilder;
import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "Bed", regex = "bed") @BlockEntity(name = "Bed", regex = "bed")
public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override @Override
public boolean isBlock(int blockState) { public boolean isBlock(int blockState) {
return BlockStateValues.getBedColor(blockState) != -1; return BlockStateValues.getBedColor(blockState) != -1;
} }
@Override @Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Map<String, Object> tags = new HashMap<>();
byte bedcolor = BlockStateValues.getBedColor(blockState); byte bedcolor = BlockStateValues.getBedColor(blockState);
// Just in case... // Just in case...
if (bedcolor == -1) bedcolor = 0; if (bedcolor == -1) {
tags.put("color", bedcolor); bedcolor = 0;
return tags; }
builder.put("color", bedcolor);
} }
} }

View File

@ -33,7 +33,6 @@ import org.geysermc.connector.network.session.GeyserSession;
* Implemented only if a block is a block entity in Bedrock and not Java Edition. * Implemented only if a block is a block entity in Bedrock and not Java Edition.
*/ */
public interface BedrockOnlyBlockEntity { public interface BedrockOnlyBlockEntity {
/** /**
* Update the block on Bedrock Edition. * Update the block on Bedrock Edition.
* @param session GeyserSession. * @param session GeyserSession.
@ -49,7 +48,7 @@ public interface BedrockOnlyBlockEntity {
* @return Bedrock tag, or null if not a Bedrock-only Block Entity * @return Bedrock tag, or null if not a Bedrock-only Block Entity
*/ */
static NbtMap getTag(Vector3i position, int blockState) { static NbtMap getTag(Vector3i position, int blockState) {
if (new FlowerPotBlockEntityTranslator().isBlock(blockState)) { if (FlowerPotBlockEntityTranslator.isFlowerBlock(blockState)) {
return FlowerPotBlockEntityTranslator.getTag(blockState, position); return FlowerPotBlockEntityTranslator.getTag(blockState, position);
} else if (PistonBlockEntityTranslator.isBlock(blockState)) { } else if (PistonBlockEntityTranslator.isBlock(blockState)) {
return PistonBlockEntityTranslator.getTag(blockState, position); return PistonBlockEntityTranslator.getTag(blockState, position);

View File

@ -41,10 +41,16 @@ import org.reflections.Reflections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/**
* The class that all block entities (on both Java and Bedrock) should translate with
*/
public abstract class BlockEntityTranslator { public abstract class BlockEntityTranslator {
public static final Map<String, BlockEntityTranslator> BLOCK_ENTITY_TRANSLATORS = new HashMap<>(); public static final Map<String, BlockEntityTranslator> BLOCK_ENTITY_TRANSLATORS = new HashMap<>();
public static ObjectArrayList<RequiresBlockState> REQUIRES_BLOCK_STATE_LIST = new ObjectArrayList<>(); /**
* 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 chunks on as we don't need to double-cache data
*/
public static final ObjectArrayList<RequiresBlockState> REQUIRES_BLOCK_STATE_LIST = new ObjectArrayList<>();
/** /**
* Contains a list of irregular block entity name translations that can't be fit into the regex * Contains a list of irregular block entity name translations that can't be fit into the regex
@ -78,27 +84,33 @@ public abstract class BlockEntityTranslator {
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_entity.failed", clazz.getCanonicalName())); GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_entity.failed", clazz.getCanonicalName()));
} }
} }
boolean cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks();
for (Class<?> clazz : ref.getSubTypesOf(RequiresBlockState.class)) { for (Class<?> clazz : ref.getSubTypesOf(RequiresBlockState.class)) {
GeyserConnector.getInstance().getLogger().debug("Found block entity that requires block state: " + clazz.getCanonicalName()); GeyserConnector.getInstance().getLogger().debug("Found block entity that requires block state: " + clazz.getCanonicalName());
try { try {
REQUIRES_BLOCK_STATE_LIST.add((RequiresBlockState) clazz.newInstance()); RequiresBlockState requiresBlockState = (RequiresBlockState) clazz.newInstance();
if (cacheChunks && !(requiresBlockState instanceof BedrockOnlyBlockEntity)) {
// Not needed to put this one in the map; cache chunks takes care of that for us
GeyserConnector.getInstance().getLogger().debug("Not adding because cache chunks is enabled.");
continue;
}
REQUIRES_BLOCK_STATE_LIST.add(requiresBlockState);
} catch (InstantiationException | IllegalAccessException e) { } catch (InstantiationException | IllegalAccessException e) {
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_state.failed", clazz.getCanonicalName())); GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_state.failed", clazz.getCanonicalName()));
} }
} }
} }
public abstract Map<String, Object> translateTag(CompoundTag tag, int blockState); public abstract void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState);
public NbtMap getBlockEntityTag(String id, CompoundTag tag, int blockState) { public NbtMap getBlockEntityTag(String id, CompoundTag tag, int blockState) {
int x = Integer.parseInt(String.valueOf(tag.getValue().get("x").getValue())); int x = ((IntTag) tag.getValue().get("x")).getValue();
int y = Integer.parseInt(String.valueOf(tag.getValue().get("y").getValue())); int y = ((IntTag) tag.getValue().get("y")).getValue();
int z = Integer.parseInt(String.valueOf(tag.getValue().get("z").getValue())); int z = ((IntTag) tag.getValue().get("z")).getValue();
NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(id), x, y, z).toBuilder(); NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(id), x, y, z).toBuilder();
Map<String, Object> translatedTags = translateTag(tag, blockState); translateTag(tagBuilder, tag, blockState);
translatedTags.forEach(tagBuilder::put);
return tagBuilder.build(); return tagBuilder.build();
} }

View File

@ -32,22 +32,16 @@ import com.nukkitx.nbt.NbtMapBuilder;
import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemRegistry;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "Campfire", regex = "campfire") @BlockEntity(name = "Campfire", regex = "campfire")
public class CampfireBlockEntityTranslator extends BlockEntityTranslator { public class CampfireBlockEntityTranslator extends BlockEntityTranslator {
@Override @Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Map<String, Object> tags = new HashMap<>();
ListTag items = tag.get("Items"); ListTag items = tag.get("Items");
int i = 1; int i = 1;
for (com.github.steveice10.opennbt.tag.builtin.Tag itemTag : items.getValue()) { for (com.github.steveice10.opennbt.tag.builtin.Tag itemTag : items.getValue()) {
tags.put("Item" + i, getItem((CompoundTag) itemTag)); builder.put("Item" + i, getItem((CompoundTag) itemTag));
i++; i++;
} }
return tags;
} }
protected NbtMap getItem(CompoundTag tag) { protected NbtMap getItem(CompoundTag tag) {

View File

@ -26,38 +26,33 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.*; import com.github.steveice10.opennbt.tag.builtin.*;
import com.nukkitx.nbt.NbtMapBuilder;
import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import org.geysermc.connector.utils.MessageUtils; import org.geysermc.connector.utils.MessageUtils;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "CommandBlock", regex = "command_block") @BlockEntity(name = "CommandBlock", regex = "command_block")
public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override @Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Map<String, Object> map = new HashMap<>();
if (tag.size() < 5) { if (tag.size() < 5) {
return map; // 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
map.put("conditionalMode", BlockStateValues.getCommandBlockValues().getOrDefault(blockState, (byte) 0)); builder.put("conditionalMode", BlockStateValues.getCommandBlockValues().getOrDefault(blockState, (byte) 0));
// Java and Bedrock values // Java and Bedrock values
map.put("conditionMet", ((ByteTag) tag.get("conditionMet")).getValue()); builder.put("conditionMet", ((ByteTag) tag.get("conditionMet")).getValue());
map.put("auto", ((ByteTag) tag.get("auto")).getValue()); builder.put("auto", ((ByteTag) tag.get("auto")).getValue());
map.put("CustomName", MessageUtils.getBedrockMessage(((StringTag) tag.get("CustomName")).getValue())); builder.put("CustomName", MessageUtils.getBedrockMessage(((StringTag) tag.get("CustomName")).getValue()));
map.put("powered", ((ByteTag) tag.get("powered")).getValue()); builder.put("powered", ((ByteTag) tag.get("powered")).getValue());
map.put("Command", ((StringTag) tag.get("Command")).getValue()); builder.put("Command", ((StringTag) tag.get("Command")).getValue());
map.put("SuccessCount", ((IntTag) tag.get("SuccessCount")).getValue()); builder.put("SuccessCount", ((IntTag) tag.get("SuccessCount")).getValue());
map.put("TrackOutput", ((ByteTag) tag.get("TrackOutput")).getValue()); builder.put("TrackOutput", ((ByteTag) tag.get("TrackOutput")).getValue());
map.put("UpdateLastExecution", ((ByteTag) tag.get("UpdateLastExecution")).getValue()); builder.put("UpdateLastExecution", ((ByteTag) tag.get("UpdateLastExecution")).getValue());
if (tag.get("LastExecution") != null) { if (tag.get("LastExecution") != null) {
map.put("LastExecution", ((LongTag) tag.get("LastExecution")).getValue()); builder.put("LastExecution", ((LongTag) tag.get("LastExecution")).getValue());
} else { } else {
map.put("LastExecution", (long) 0); builder.put("LastExecution", (long) 0);
} }
return map;
} }
@Override @Override

View File

@ -33,15 +33,11 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import org.geysermc.connector.network.translators.world.block.DoubleChestValue; import org.geysermc.connector.network.translators.world.block.DoubleChestValue;
import org.geysermc.connector.utils.BlockEntityUtils; import org.geysermc.connector.utils.BlockEntityUtils;
import java.util.HashMap;
import java.util.Map;
/** /**
* Chests have more block entity properties in Bedrock, which is solved by implementing the BedrockOnlyBlockEntity * Chests have more block entity properties in Bedrock, which is solved by implementing the BedrockOnlyBlockEntity
*/ */
@BlockEntity(name = "Chest", regex = "chest") @BlockEntity(name = "Chest", regex = "chest")
public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState { public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState {
@Override @Override
public boolean isBlock(int blockState) { public boolean isBlock(int blockState) {
return BlockStateValues.getDoubleChestValues().containsKey(blockState); return BlockStateValues.getDoubleChestValues().containsKey(blockState);
@ -51,44 +47,39 @@ public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator impl
public void updateBlock(GeyserSession session, int blockState, Vector3i position) { public void updateBlock(GeyserSession session, int blockState, Vector3i position) {
CompoundTag javaTag = getConstantJavaTag("chest", position.getX(), position.getY(), position.getZ()); CompoundTag javaTag = getConstantJavaTag("chest", position.getX(), position.getY(), position.getZ());
NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId("chest"), position.getX(), position.getY(), position.getZ()).toBuilder(); NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId("chest"), position.getX(), position.getY(), position.getZ()).toBuilder();
translateTag(javaTag, blockState).forEach(tagBuilder::put); translateTag(tagBuilder, javaTag, blockState);
BlockEntityUtils.updateBlockEntity(session, tagBuilder.build(), position); BlockEntityUtils.updateBlockEntity(session, tagBuilder.build(), position);
} }
@Override @Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Map<String, Object> tags = new HashMap<>(); DoubleChestValue chestValues = BlockStateValues.getDoubleChestValues().getOrDefault(blockState, null);
if (BlockStateValues.getDoubleChestValues().containsKey(blockState)) { if (chestValues != null) {
DoubleChestValue chestValues = BlockStateValues.getDoubleChestValues().get(blockState); int x = (int) tag.getValue().get("x").getValue();
if (chestValues != null) { int z = (int) tag.getValue().get("z").getValue();
int x = (int) tag.getValue().get("x").getValue(); // Calculate the position of the other chest based on the Java block state
int z = (int) tag.getValue().get("z").getValue(); if (chestValues.isFacingEast) {
// Calculate the position of the other chest based on the Java block state if (chestValues.isDirectionPositive) {
if (chestValues.isFacingEast) { // East
if (chestValues.isDirectionPositive) { z = z + (chestValues.isLeft ? 1 : -1);
// East
z = z + (chestValues.isLeft ? 1 : -1);
} else {
// West
z = z + (chestValues.isLeft ? -1 : 1);
}
} else { } else {
if (chestValues.isDirectionPositive) { // West
// South z = z + (chestValues.isLeft ? -1 : 1);
x = x + (chestValues.isLeft ? -1 : 1);
} else {
// North
x = x + (chestValues.isLeft ? 1 : -1);
}
} }
tags.put("pairx", x); } else {
tags.put("pairz", z); if (chestValues.isDirectionPositive) {
if (!chestValues.isLeft) { // South
tags.put("pairlead", (byte) 1); x = x + (chestValues.isLeft ? -1 : 1);
} else {
// North
x = x + (chestValues.isLeft ? 1 : -1);
} }
} }
builder.put("pairx", x);
builder.put("pairz", z);
if (!chestValues.isLeft) {
builder.put("pairlead", (byte) 1);
}
} }
return tags;
} }
} }

View File

@ -26,16 +26,11 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.nbt.NbtMapBuilder;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "Empty", regex = "") @BlockEntity(name = "Empty", regex = "")
public class EmptyBlockEntityTranslator extends BlockEntityTranslator { public class EmptyBlockEntityTranslator extends BlockEntityTranslator {
@Override @Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
return new HashMap<>();
} }
} }

View File

@ -28,21 +28,18 @@ package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.nukkitx.nbt.NbtList; import com.nukkitx.nbt.NbtList;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType; import com.nukkitx.nbt.NbtType;
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 java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map;
@BlockEntity(name = "EndGateway", regex = "end_gateway") @BlockEntity(name = "EndGateway", regex = "end_gateway")
public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator { public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator {
@Override @Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Map<String, Object> tags = new HashMap<>(); builder.put("Age", (int) ((long) tag.get("Age").getValue()));
tags.put("Age", (int) ((long) tag.get("Age").getValue()));
// Java sometimes does not provide this tag, but Bedrock crashes if it doesn't exist // Java sometimes does not provide this tag, but Bedrock crashes if it doesn't exist
// Linked coordinates // Linked coordinates
IntList tagsList = new IntArrayList(); IntList tagsList = new IntArrayList();
@ -50,8 +47,7 @@ public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator {
tagsList.add(getExitPortalCoordinate(tag, "X")); tagsList.add(getExitPortalCoordinate(tag, "X"));
tagsList.add(getExitPortalCoordinate(tag, "Y")); tagsList.add(getExitPortalCoordinate(tag, "Y"));
tagsList.add(getExitPortalCoordinate(tag, "Z")); tagsList.add(getExitPortalCoordinate(tag, "Z"));
tags.put("ExitPortal", new NbtList<>(NbtType.INT, tagsList)); builder.put("ExitPortal", new NbtList<>(NbtType.INT, tagsList));
return tags;
} }
private int getExitPortalCoordinate(CompoundTag tag, String axis) { private int getExitPortalCoordinate(CompoundTag tag, String axis) {
@ -60,6 +56,7 @@ public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator {
LinkedHashMap<?, ?> compoundTag = (LinkedHashMap<?, ?>) tag.get("ExitPortal").getValue(); LinkedHashMap<?, ?> compoundTag = (LinkedHashMap<?, ?>) tag.get("ExitPortal").getValue();
IntTag intTag = (IntTag) compoundTag.get(axis); IntTag intTag = (IntTag) compoundTag.get(axis);
return intTag.getValue(); return intTag.getValue();
} return 0; }
return 0;
} }
} }

View File

@ -35,30 +35,19 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.BlockEntityUtils; import org.geysermc.connector.utils.BlockEntityUtils;
public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState { public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState {
/**
@Override * @param blockState the Java block state of a potential flower pot block
public boolean isBlock(int blockState) { * @return true if the block is a flower pot
return (BlockStateValues.getFlowerPotValues().containsKey(blockState)); */
} public static boolean isFlowerBlock(int blockState) {
return BlockStateValues.getFlowerPotValues().containsKey(blockState);
@Override
public void updateBlock(GeyserSession session, int blockState, Vector3i position) {
BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position);
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(blockState));
updateBlockPacket.setBlockPosition(position);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
session.sendUpstreamPacket(updateBlockPacket);
BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position);
} }
/** /**
* Get the Nukkit CompoundTag of the flower pot. * Get the Nukkit CompoundTag of the flower pot.
*
* @param blockState Java block state of flower pot. * @param blockState Java block state of flower pot.
* @param position Bedrock position of flower pot. * @param position Bedrock position of flower pot.
* @return Bedrock tag of flower pot. * @return Bedrock tag of flower pot.
*/ */
public static NbtMap getTag(int blockState, Vector3i position) { public static NbtMap getTag(int blockState, Vector3i position) {
@ -80,4 +69,23 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, R
} }
return tagBuilder.build(); return tagBuilder.build();
} }
@Override
public boolean isBlock(int blockState) {
return isFlowerBlock(blockState);
}
@Override
public void updateBlock(GeyserSession session, int blockState, Vector3i position) {
BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position);
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(blockState));
updateBlockPacket.setBlockPosition(position);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
session.sendUpstreamPacket(updateBlockPacket);
BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position);
}
} }

View File

@ -27,21 +27,16 @@ package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.nukkitx.nbt.NbtMapBuilder;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "JigsawBlock", regex = "jigsaw") @BlockEntity(name = "JigsawBlock", regex = "jigsaw")
public class JigsawBlockBlockEntityTranslator extends BlockEntityTranslator { public class JigsawBlockBlockEntityTranslator extends BlockEntityTranslator {
@Override @Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Map<String, Object> map = new HashMap<>(); builder.put("joint", ((StringTag) tag.get("joint")).getValue());
map.put("joint", ((StringTag) tag.get("joint")).getValue()); builder.put("name", ((StringTag) tag.get("name")).getValue());
map.put("name", ((StringTag) tag.get("name")).getValue()); builder.put("target_pool", ((StringTag) tag.get("pool")).getValue());
map.put("target_pool", ((StringTag) tag.get("pool")).getValue()); builder.put("final_state", ((StringTag) tag.get("final_state")).getValue());
map.put("final_state", ((StringTag) tag.get("final_state")).getValue()); builder.put("target", ((StringTag) tag.get("target")).getValue());
map.put("target", ((StringTag) tag.get("target")).getValue());
return map;
} }
} }

View File

@ -36,21 +36,20 @@ import org.geysermc.connector.utils.ChunkUtils;
* Does not implement BlockEntityTranslator because it's only a block entity in Bedrock * Does not implement BlockEntityTranslator because it's only a block entity in Bedrock
*/ */
public class NoteblockBlockEntityTranslator implements RequiresBlockState { public class NoteblockBlockEntityTranslator implements RequiresBlockState {
@Override @Override
public boolean isBlock(int blockState) { public boolean isBlock(int blockState) {
return BlockStateValues.getNoteblockPitch(blockState) != -1; return BlockStateValues.getNoteblockPitch(blockState) != -1;
} }
public static void translate(GeyserSession session, Position position) { public static void translate(GeyserSession session, Position position) {
int blockState = ChunkUtils.CACHED_BLOCK_ENTITIES.getOrDefault(position, 0); int blockState = session.getConnector().getConfig().isCacheChunks() ?
session.getConnector().getWorldManager().getBlockAt(session, position) :
ChunkUtils.CACHED_BLOCK_ENTITIES.removeInt(position);
BlockEventPacket blockEventPacket = new BlockEventPacket(); BlockEventPacket blockEventPacket = new BlockEventPacket();
blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ())); blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
blockEventPacket.setEventType(0); blockEventPacket.setEventType(0);
blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState)); blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState));
session.sendUpstreamPacket(blockEventPacket); session.sendUpstreamPacket(blockEventPacket);
ChunkUtils.CACHED_BLOCK_ENTITIES.remove(position);
} }
} }

View File

@ -34,9 +34,9 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues;
* Pistons are a special case where they are only a block entity on Bedrock. * Pistons are a special case where they are only a block entity on Bedrock.
*/ */
public class PistonBlockEntityTranslator { public class PistonBlockEntityTranslator {
/** /**
* Used in ChunkUtils to determine if the block is a piston. * Used in ChunkUtils to determine if the block is a piston.
*
* @param blockState Java BlockState of block. * @param blockState Java BlockState of block.
* @return if block is a piston or not. * @return if block is a piston or not.
*/ */
@ -46,8 +46,9 @@ public class PistonBlockEntityTranslator {
/** /**
* Calculates the Nukkit CompoundTag to send to the client on chunk * Calculates the Nukkit CompoundTag to send to the client on chunk
*
* @param blockState Java block state of block. * @param blockState Java block state of block.
* @param position Bedrock position of piston. * @param position Bedrock position of piston.
* @return Bedrock tag of piston. * @return Bedrock tag of piston.
*/ */
public static NbtMap getTag(int blockState, Vector3i position) { public static NbtMap getTag(int blockState, Vector3i position) {
@ -57,14 +58,13 @@ public class PistonBlockEntityTranslator {
.putInt("z", position.getZ()) .putInt("z", position.getZ())
.putByte("isMovable", (byte) 1) .putByte("isMovable", (byte) 1)
.putString("id", "PistonArm"); .putString("id", "PistonArm");
if (BlockStateValues.getPistonValues().containsKey(blockState)) {
boolean extended = BlockStateValues.getPistonValues().get(blockState); boolean extended = BlockStateValues.getPistonValues().get(blockState);
// 1f if extended, otherwise 0f // 1f if extended, otherwise 0f
tagBuilder.putFloat("Progress", (extended) ? 1.0f : 0.0f); tagBuilder.putFloat("Progress", (extended) ? 1.0f : 0.0f);
// 1 if sticky, 0 if not // 1 if sticky, 0 if not
tagBuilder.putByte("Sticky", (byte)((BlockStateValues.isStickyPiston(blockState)) ? 1 : 0)); tagBuilder.putByte("Sticky", (byte) ((BlockStateValues.isStickyPiston(blockState)) ? 1 : 0));
}
return tagBuilder.build(); return tagBuilder.build();
} }
} }

View File

@ -26,23 +26,18 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.nbt.NbtMapBuilder;
import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "ShulkerBox", regex = "shulker_box") @BlockEntity(name = "ShulkerBox", regex = "shulker_box")
public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator { public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator {
@Override @Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Map<String, Object> tags = new HashMap<>();
byte direction = BlockStateValues.getShulkerBoxDirection(blockState); byte direction = BlockStateValues.getShulkerBoxDirection(blockState);
// Just in case... // Just in case...
if (direction == -1) direction = 1; if (direction == -1) {
tags.put("facing", direction); direction = 1;
return tags; }
builder.put("facing", direction);
} }
} }

View File

@ -27,51 +27,12 @@ package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.nbt.NbtMapBuilder;
import org.geysermc.connector.utils.MessageUtils; import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.utils.SignUtils; import org.geysermc.connector.utils.SignUtils;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "Sign", regex = "sign") @BlockEntity(name = "Sign", regex = "sign")
public class SignBlockEntityTranslator extends BlockEntityTranslator { public class SignBlockEntityTranslator extends BlockEntityTranslator {
@Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) {
Map<String, Object> tags = new HashMap<>();
StringBuilder signText = new StringBuilder();
for(int i = 0; i < 4; i++) {
int currentLine = i+1;
String signLine = getOrDefault(tag.getValue().get("Text" + currentLine), "");
signLine = MessageUtils.getBedrockMessage(MessageSerializer.fromString(signLine));
// Check the character width on the sign to ensure there is no overflow that is usually hidden
// to Java Edition clients but will appear to Bedrock clients
int signWidth = 0;
StringBuilder finalSignLine = new StringBuilder();
for (char c : signLine.toCharArray()) {
signWidth += SignUtils.getCharacterWidth(c);
if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) {
finalSignLine.append(c);
} else {
break;
}
}
// Java Edition 1.14 added the ability to change the text color of the whole sign using dye
if (tag.contains("Color")) {
signText.append(getBedrockSignColor(tag.get("Color").getValue().toString()));
}
signText.append(finalSignLine.toString());
signText.append("\n");
}
tags.put("Text", MessageUtils.getBedrockMessage(MessageSerializer.fromString(signText.toString())));
return tags;
}
/** /**
* Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code. * Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code.
* <br> * <br>
@ -133,4 +94,36 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator {
return base; return base;
} }
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
StringBuilder signText = new StringBuilder();
for (int i = 0; i < 4; i++) {
int currentLine = i + 1;
String signLine = getOrDefault(tag.getValue().get("Text" + currentLine), "");
signLine = MessageUtils.getBedrockMessage(MessageSerializer.fromString(signLine));
// Check the character width on the sign to ensure there is no overflow that is usually hidden
// to Java Edition clients but will appear to Bedrock clients
int signWidth = 0;
StringBuilder finalSignLine = new StringBuilder();
for (char c : signLine.toCharArray()) {
signWidth += SignUtils.getCharacterWidth(c);
if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) {
finalSignLine.append(c);
} else {
break;
}
}
// Java Edition 1.14 added the ability to change the text color of the whole sign using dye
if (tag.contains("Color")) {
signText.append(getBedrockSignColor(tag.get("Color").getValue().toString()));
}
signText.append(finalSignLine.toString());
signText.append("\n");
}
builder.put("Text", MessageUtils.getBedrockMessage(MessageSerializer.fromString(signText.toString())));
}
} }

View File

@ -25,29 +25,26 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.nbt.NbtMapBuilder;
import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "Skull", regex = "skull") @BlockEntity(name = "Skull", regex = "skull")
public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override @Override
public boolean isBlock(int blockState) { public boolean isBlock(int blockState) {
return BlockStateValues.getSkullVariant(blockState) != -1; return BlockStateValues.getSkullVariant(blockState) != -1;
} }
@Override @Override
public Map<String, Object> translateTag(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Map<String, Object> tags = new HashMap<>();
byte skullVariant = BlockStateValues.getSkullVariant(blockState); byte skullVariant = BlockStateValues.getSkullVariant(blockState);
float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f; float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f;
// Just in case... // Just in case...
if (skullVariant == -1) skullVariant = 0; if (skullVariant == -1) {
tags.put("Rotation", rotation); skullVariant = 0;
tags.put("SkullType", skullVariant); }
return tags; builder.put("Rotation", rotation);
builder.put("SkullType", skullVariant);
} }
} }

View File

@ -26,63 +26,58 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.nbt.NbtMapBuilder;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "MobSpawner", regex = "mob_spawner") @BlockEntity(name = "MobSpawner", regex = "mob_spawner")
public class SpawnerBlockEntityTranslator extends BlockEntityTranslator { public class SpawnerBlockEntityTranslator extends BlockEntityTranslator {
@Override @Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Map<String, Object> tags = new HashMap<>(); Tag current;
if (tag.get("MaxNearbyEntities") != null) { if ((current = tag.get("MaxNearbyEntities")) != null) {
tags.put("MaxNearbyEntities", (short) tag.get("MaxNearbyEntities").getValue()); builder.put("MaxNearbyEntities", current.getValue());
} }
if (tag.get("RequiredPlayerRange") != null) { if ((current = tag.get("RequiredPlayerRange")) != null) {
tags.put("RequiredPlayerRange", (short) tag.get("RequiredPlayerRange").getValue()); builder.put("RequiredPlayerRange", current.getValue());
} }
if (tag.get("SpawnCount") != null) { if ((current = tag.get("SpawnCount")) != null) {
tags.put("SpawnCount", (short) tag.get("SpawnCount").getValue()); builder.put("SpawnCount", current.getValue());
} }
if (tag.get("MaxSpawnDelay") != null) { if ((current = tag.get("MaxSpawnDelay")) != null) {
tags.put("MaxSpawnDelay", (short) tag.get("MaxSpawnDelay").getValue()); builder.put("MaxSpawnDelay", current.getValue());
} }
if (tag.get("Delay") != null) { if ((current = tag.get("Delay")) != null) {
tags.put("Delay", (short) tag.get("Delay").getValue()); builder.put("Delay", current.getValue());
} }
if (tag.get("SpawnRange") != null) { if ((current = tag.get("SpawnRange")) != null) {
tags.put("SpawnRange", (short) tag.get("SpawnRange").getValue()); builder.put("SpawnRange", current.getValue());
} }
if (tag.get("MinSpawnDelay") != null) { if ((current = tag.get("MinSpawnDelay")) != null) {
tags.put("MinSpawnDelay", (short) tag.get("MinSpawnDelay").getValue()); builder.put("MinSpawnDelay", current.getValue());
} }
if (tag.get("SpawnData") != null) { CompoundTag spawnData = tag.get("SpawnData");
CompoundTag spawnData = tag.get("SpawnData"); if (spawnData != null) {
String entityID = (String) spawnData.get("id").getValue(); String entityID = (String) spawnData.get("id").getValue();
tags.put("EntityIdentifier", entityID); builder.put("EntityIdentifier", entityID);
EntityType type = EntityType.getFromIdentifier(entityID); EntityType type = EntityType.getFromIdentifier(entityID);
if (type != null) { if (type != null) {
tags.put("DisplayEntityWidth", type.getWidth()); builder.put("DisplayEntityWidth", type.getWidth());
tags.put("DisplayEntityHeight", type.getHeight()); builder.put("DisplayEntityHeight", type.getHeight());
tags.put("DisplayEntityScale", 1.0f); builder.put("DisplayEntityScale", 1.0f);
} }
} }
tags.put("id", "MobSpawner"); builder.put("id", "MobSpawner");
tags.put("isMovable", (byte) 1); builder.put("isMovable", (byte) 1);
return tags;
} }
} }

View File

@ -33,17 +33,17 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
public class BlockEntityUtils { public class BlockEntityUtils {
private static final BlockEntityTranslator EMPTY_TRANSLATOR = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get("Empty"); private static final BlockEntityTranslator EMPTY_TRANSLATOR = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get("Empty");
public static String getBedrockBlockEntityId(String id) { public static String getBedrockBlockEntityId(String id) {
// These are the only exceptions when it comes to block entity ids // These are the only exceptions when it comes to block entity ids
if (BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.containsKey(id)) { String value = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.get(id);
return BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.get(id); if (value != null) {
return value;
} }
id = id.replace("minecraft:", "") id = id.replace("minecraft:", "")
.replace("_", " "); .replace("_", " ");
// Split at every space or capital letter - for the latter, some legacy Java block entity tags are the correct format already // Split at every space or capital letter - for the latter, some legacy Java block entity tags are the correct format already
String[] words; String[] words;
if (!id.toUpperCase().equals(id)) { // Otherwise we get [S, K, U, L, L] if (!id.toUpperCase().equals(id)) { // Otherwise we get [S, K, U, L, L]
@ -60,11 +60,10 @@ public class BlockEntityUtils {
public static BlockEntityTranslator getBlockEntityTranslator(String name) { public static BlockEntityTranslator getBlockEntityTranslator(String name) {
BlockEntityTranslator blockEntityTranslator = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get(name); BlockEntityTranslator blockEntityTranslator = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get(name);
if (blockEntityTranslator == null) { if (blockEntityTranslator != null) {
return EMPTY_TRANSLATOR; return blockEntityTranslator;
} }
return EMPTY_TRANSLATOR;
return blockEntityTranslator;
} }
public static void updateBlockEntity(GeyserSession session, NbtMap blockEntity, Position position) { public static void updateBlockEntity(GeyserSession session, NbtMap blockEntity, Position position) {

View File

@ -72,9 +72,9 @@ import static org.geysermc.connector.network.translators.world.block.BlockTransl
@UtilityClass @UtilityClass
public class ChunkUtils { public class ChunkUtils {
/** /**
* Temporarily stores positions of BlockState values that are needed for certain block entities actively * Temporarily stores positions of BlockState values that are needed for certain block entities actively.
* Not used if cache chunks is enabled
*/ */
public static final Object2IntMap<Position> CACHED_BLOCK_ENTITIES = new Object2IntOpenHashMap<>(); public static final Object2IntMap<Position> CACHED_BLOCK_ENTITIES = new Object2IntOpenHashMap<>();
@ -300,11 +300,16 @@ public class ChunkUtils {
public static void updateBlock(GeyserSession session, int blockState, Vector3i position) { public static void updateBlock(GeyserSession session, int blockState, Vector3i position) {
// Checks for item frames so they aren't tripped up and removed // Checks for item frames so they aren't tripped up and removed
if (ItemFrameEntity.positionContainsItemFrame(session, position) && blockState == AIR) { long frameEntityId = ItemFrameEntity.getItemFrameEntityId(session, position);
((ItemFrameEntity) session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, position))).updateBlock(session); if (frameEntityId != -1) {
return; // TODO: Very occasionally the item frame doesn't sync up when destroyed
} else if (ItemFrameEntity.positionContainsItemFrame(session, position)) { Entity entity = session.getEntityCache().getEntityByJavaId(frameEntityId);
Entity entity = session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, position)); if (blockState == AIR && entity != null) { // Item frame is still present and no block overrides that; refresh it
((ItemFrameEntity) entity).updateBlock(session);
return;
}
// Otherwise the item frame is gone
if (entity != null) { if (entity != null) {
session.getEntityCache().removeEntity(entity, false); session.getEntityCache().removeEntity(entity, false);
} else { } else {
@ -342,7 +347,10 @@ public class ChunkUtils {
((BedrockOnlyBlockEntity) requiresBlockState).updateBlock(session, blockState, position); ((BedrockOnlyBlockEntity) requiresBlockState).updateBlock(session, blockState, position);
break; break;
} }
CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState); if (!session.getConnector().getConfig().isCacheChunks()) {
// Blocks aren't saved to a chunk cache; resort to this smaller cache
CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState);
}
break; //No block will be a part of two classes break; //No block will be a part of two classes
} }
} }