Translate CanPlaceOn/CanDestroy NBT (#1253)

* Translate CanPlaceOn/CanDestroy NBT

This commit adds support for the translation of the CanPlaceOn/CanDestroy NBT for Bedrock clients.

* Remove debug line
This commit is contained in:
Camotoy 2020-09-14 20:50:07 -04:00 committed by GitHub
parent 9643b208f3
commit 26802e6dab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 25 deletions

View file

@ -34,7 +34,6 @@ import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
@ -102,13 +101,7 @@ public class ItemFrameEntity extends Entity {
ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue());
NbtMapBuilder builder = NbtMap.builder();
String blockName = "";
for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) {
if (startGamePacketItemEntry.getId() == (short) itemEntry.getBedrockId()) {
blockName = startGamePacketItemEntry.getIdentifier();
break;
}
}
String blockName = ItemRegistry.getBedrockIdentifer(itemEntry);
builder.putByte("Count", (byte) itemData.getCount());
if (itemData.getTag() != null) {

View file

@ -221,6 +221,23 @@ public class ItemRegistry {
.stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null));
}
/**
* Finds the Bedrock string identifier of an ItemEntry
*
* @param entry the ItemEntry to search for
* @return the Bedrock identifier
*/
public static String getBedrockIdentifer(ItemEntry entry) {
String blockName = "";
for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) {
if (startGamePacketItemEntry.getId() == (short) entry.getBedrockId()) {
blockName = startGamePacketItemEntry.getIdentifier(); // Find the Bedrock string name
break;
}
}
return blockName;
}
/**
* Gets a Bedrock {@link ItemData} from a {@link JsonNode}
* @param itemNode the JSON node that contains ProxyPass-compatible Bedrock item data

View file

@ -41,6 +41,7 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.LanguageUtils;
import org.geysermc.connector.utils.MessageUtils;
@ -154,9 +155,43 @@ public abstract class ItemTranslator {
itemData = DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem);
}
if (nbt != null) {
// Translate the canDestroy and canPlaceOn Java NBT
ListTag canDestroy = nbt.get("CanDestroy");
String[] canBreak = new String[0];
ListTag canPlaceOn = nbt.get("CanPlaceOn");
String[] canPlace = new String[0];
canBreak = getCanModify(canDestroy, canBreak);
canPlace = getCanModify(canPlaceOn, canPlace);
itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), itemData.getTag(), canPlace, canBreak);
}
return itemData;
}
/**
* Translates the Java NBT of canDestroy and canPlaceOn to its Bedrock counterparts.
* In Java, this is treated as normal NBT, but in Bedrock, these arguments are extra parts of the item data itself.
* @param canModifyJava the list of items in Java
* @param canModifyBedrock the empty list of items in Bedrock
* @return the new list of items in Bedrock
*/
private static String[] getCanModify(ListTag canModifyJava, String[] canModifyBedrock) {
if (canModifyJava != null && canModifyJava.size() > 0) {
canModifyBedrock = new String[canModifyJava.size()];
for (int i = 0; i < canModifyBedrock.length; i++) {
// Get the Java identifier of the block that can be placed
String block = ((StringTag) canModifyJava.get(i)).getValue();
// Sometimes this is done but it's still valid
if (!block.startsWith("minecraft:")) block = "minecraft:" + block;
// Get the Bedrock identifier of the item and replace it.
// This will unfortunately be limited - for example, beds and banners will be translated weirdly
canModifyBedrock[i] = BlockTranslator.getBedrockBlockIdentifier(block).replace("minecraft:", "");
}
}
return canModifyBedrock;
}
private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() {
@Override
public List<ItemEntry> getAppliedItems() {

View file

@ -26,7 +26,6 @@
package org.geysermc.connector.network.translators.item.translators.nbt;
import com.github.steveice10.opennbt.tag.builtin.*;
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.item.ItemEntry;
@ -51,13 +50,7 @@ public class ShulkerBoxItemTranslator extends NbtItemStackTranslator {
boxItemTag.put(new ByteTag("WasPickedUp", (byte) 0)); // ???
ItemEntry boxItemEntry = ItemRegistry.getItemEntry(((StringTag) itemData.get("id")).getValue());
String blockName = "";
for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) {
if (startGamePacketItemEntry.getId() == (short) boxItemEntry.getBedrockId()) {
blockName = startGamePacketItemEntry.getIdentifier(); // Find the Bedrock string name
break;
}
}
String blockName = ItemRegistry.getBedrockIdentifer(boxItemEntry);
boxItemTag.put(new StringTag("Name", blockName));
boxItemTag.put(new ShortTag("Damage", (short) boxItemEntry.getBedrockData()));

View file

@ -28,15 +28,12 @@ package org.geysermc.connector.network.translators.world.block;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.nukkitx.nbt.NBTInputStream;
import com.nukkitx.nbt.NbtList;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.*;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.world.block.entity.BlockEntity;
import org.geysermc.connector.utils.FileUtils;
@ -52,6 +49,11 @@ public class BlockTranslator {
private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap();
private static final Int2IntMap BEDROCK_TO_JAVA_BLOCK_MAP = new Int2IntOpenHashMap();
/**
* Stores a list of differences in block identifiers.
* Items will not be added to this list if the key and value is the same.
*/
private static final Object2ObjectMap<String, String> JAVA_TO_BEDROCK_IDENTIFIERS = new Object2ObjectOpenHashMap<>();
private static final BiMap<String, Integer> JAVA_ID_BLOCK_MAP = HashBiMap.create();
private static final IntSet WATERLOGGED = new IntOpenHashSet();
private static final Object2IntMap<NbtMap> ITEM_FRAMES = new Object2IntOpenHashMap<>();
@ -152,11 +154,11 @@ public class BlockTranslator {
// Used for adding all "special" Java block states to block state map
String identifier;
String bedrock_identifer = entry.getValue().get("bedrock_identifier").asText();
String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText();
for (Class<?> clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) {
identifier = clazz.getAnnotation(BlockEntity.class).regex();
// Endswith, or else the block bedrock gets picked up for bed
if (bedrock_identifer.endsWith(identifier) && !identifier.equals("")) {
if (bedrockIdentifier.endsWith(identifier) && !identifier.equals("")) {
JAVA_ID_TO_BLOCK_ENTITY_MAP.put(javaRuntimeId, clazz.getAnnotation(BlockEntity.class).name());
break;
}
@ -164,9 +166,15 @@ public class BlockTranslator {
BlockStateValues.storeBlockStateValues(entry, javaRuntimeId);
String cleanJavaIdentifier = entry.getKey().split("\\[")[0];
if (!cleanJavaIdentifier.equals(bedrockIdentifier)) {
JAVA_TO_BEDROCK_IDENTIFIERS.put(cleanJavaIdentifier, bedrockIdentifier);
}
// Get the tag needed for non-empty flower pots
if (entry.getValue().get("pottable") != null) {
BlockStateValues.getFlowerPotBlocks().put(entry.getKey().split("\\[")[0], buildBedrockState(entry.getValue()));
BlockStateValues.getFlowerPotBlocks().put(cleanJavaIdentifier, buildBedrockState(entry.getValue()));
}
if ("minecraft:water[level=0]".equals(javaId)) {
@ -297,6 +305,14 @@ public class BlockTranslator {
return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId);
}
/**
* @param javaIdentifier the Java identifier of the block to search for
* @return the Bedrock identifier if different, or else the Java identifier
*/
public static String getBedrockBlockIdentifier(String javaIdentifier) {
return JAVA_TO_BEDROCK_IDENTIFIERS.getOrDefault(javaIdentifier, javaIdentifier);
}
public static int getItemFrame(NbtMap tag) {
return ITEM_FRAMES.getOrDefault(tag, -1);
}