Switch to Cloudburst NBT only

This commit is contained in:
Camotoy 2024-04-26 21:44:59 -04:00
parent 16cb76f523
commit 2fa7585db3
58 changed files with 586 additions and 927 deletions

View file

@ -45,7 +45,6 @@ import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.geysermc.api.Geyser;
import org.geysermc.cumulus.form.Form;
import org.geysermc.cumulus.form.util.FormBuilder;
import org.geysermc.erosion.packet.Packets;
import org.geysermc.floodgate.crypto.AesCipher;
import org.geysermc.floodgate.crypto.AesKeyProducer;
import org.geysermc.floodgate.crypto.Base64Topping;
@ -384,7 +383,7 @@ public class GeyserImpl implements GeyserApi {
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
Packets.initGeyser();
//Packets.initGeyser();
if (Epoll.isAvailable()) {
this.erosionUnixListener = new UnixSocketClientListener();

View file

@ -25,8 +25,6 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@ -57,6 +55,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEn
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import java.util.*;
@ -194,9 +193,9 @@ public class LivingEntity extends Entity {
/**
* Checks to see if a nametag interaction would go through.
*/
// Implementation note for 1.20.5: this code was moved to the NameTag item.
protected final InteractionResult checkInteractWithNameTag(GeyserItemStack itemStack) {
CompoundTag nbt = itemStack.getNbt();
if (nbt != null && nbt.get("display") instanceof CompoundTag displayTag && displayTag.get("Name") instanceof StringTag) {
if (itemStack.getComponent(DataComponentType.CUSTOM_NAME) != null) {
// The mob shall be named
return InteractionResult.SUCCESS;
}

View file

@ -25,14 +25,14 @@
package org.geysermc.geyser.entity.type.player;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.data.Ability;
import org.cloudburstmc.protocol.bedrock.data.AbilityLayer;
import org.cloudburstmc.protocol.bedrock.data.GameType;
@ -298,11 +298,11 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
dirtyMetadata.put(EntityDataTypes.MARK_VARIANT, ~entityMetadata.getPrimitiveValue() & 0xff);
}
public void setLeftParrot(EntityMetadata<CompoundTag, ?> entityMetadata) {
public void setLeftParrot(EntityMetadata<NbtMap, ?> entityMetadata) {
setParrot(entityMetadata.getValue(), true);
}
public void setRightParrot(EntityMetadata<CompoundTag, ?> entityMetadata) {
public void setRightParrot(EntityMetadata<NbtMap, ?> entityMetadata) {
setParrot(entityMetadata.getValue(), false);
}
@ -310,7 +310,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
* Sets the parrot occupying the shoulder. Bedrock Edition requires a full entity whereas Java Edition just
* spawns it from the NBT data provided
*/
protected void setParrot(CompoundTag tag, boolean isLeft) {
protected void setParrot(NbtMap tag, boolean isLeft) {
if (tag != null && !tag.isEmpty()) {
if ((isLeft && leftParrot != null) || (!isLeft && rightParrot != null)) {
// No need to update a parrot's data when it already exists
@ -320,7 +320,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
ParrotEntity parrot = new ParrotEntity(session, 0, session.getEntityCache().getNextEntityId().incrementAndGet(),
null, EntityDefinitions.PARROT, position, motion, getYaw(), getPitch(), getHeadYaw());
parrot.spawnEntity();
parrot.getDirtyMetadata().put(EntityDataTypes.VARIANT, (Integer) tag.get("Variant").getValue());
parrot.getDirtyMetadata().put(EntityDataTypes.VARIANT, (Integer) tag.get("Variant"));
// Different position whether the parrot is left or right
float offset = isLeft ? 0.4f : -0.4f;
parrot.getDirtyMetadata().put(EntityDataTypes.SEAT_OFFSET, Vector3f.from(offset, -0.22, -0.1));
@ -437,11 +437,11 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
} else if (numberFormat instanceof FixedFormat fixedFormat) {
numberString = MessageTranslator.convertMessage(fixedFormat.getValue());
} else if (numberFormat instanceof StyledFormat styledFormat) {
CompoundTag styledAmount = styledFormat.getStyle().clone();
styledAmount.put(new StringTag("text", String.valueOf(amount)));
NbtMapBuilder styledAmount = styledFormat.getStyle().toBuilder();
styledAmount.putString("text", String.valueOf(amount));
numberString = MessageTranslator.convertJsonMessage(
NbtComponentSerializer.tagComponentToJson(styledAmount).toString());
NbtComponentSerializer.tagComponentToJson(styledAmount.build()).toString(), session.locale());
} else {
numberString = String.valueOf(amount);
}

View file

@ -25,11 +25,7 @@
package org.geysermc.geyser.inventory;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.*;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
@ -43,6 +39,8 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import java.util.HashMap;
@Data
public class GeyserItemStack {
public static final GeyserItemStack EMPTY = new GeyserItemStack(Items.AIR_ID, 0, null);
@ -52,7 +50,7 @@ public class GeyserItemStack {
private DataComponents components;
private int netId;
@Getter(AccessLevel.NONE)
@Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE)
@EqualsAndHashCode.Exclude
private Item item;
@ -67,6 +65,14 @@ public class GeyserItemStack {
this.netId = netId;
}
public static @NonNull GeyserItemStack of(int javaId, int amount) {
return of(javaId, amount, null);
}
public static @NonNull GeyserItemStack of(int javaId, int amount, DataComponents components) {
return new GeyserItemStack(javaId, amount, components);
}
public static @NonNull GeyserItemStack from(@Nullable ItemStack itemStack) {
return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getDataComponents());
}
@ -79,16 +85,27 @@ public class GeyserItemStack {
return isEmpty() ? 0 : amount;
}
public @Nullable CompoundTag getNbt() {
Thread.dumpStack();
return null;
}
public @Nullable DataComponents getComponents() {
return isEmpty() ? null : components;
}
public <T extends Boolean> boolean getComponent(DataComponentType<T> type, boolean def) {
@NonNull
public DataComponents getOrCreateComponents() {
if (components == null) {
return components = new DataComponents(new HashMap<>());
}
return components;
}
@Nullable
public <T> T getComponent(@NonNull DataComponentType<T> type) {
if (components == null) {
return null;
}
return components.get(type);
}
public <T extends Boolean> boolean getComponent(@NonNull DataComponentType<T> type, boolean def) {
if (components == null) {
return def;
}
@ -100,7 +117,7 @@ public class GeyserItemStack {
return def;
}
public <T extends Integer> int getComponent(DataComponentType<T> type, int def) {
public <T extends Integer> int getComponent(@NonNull DataComponentType<T> type, int def) {
if (components == null) {
return def;
}

View file

@ -25,9 +25,6 @@
package org.geysermc.geyser.inventory;
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ -39,6 +36,7 @@ import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.ItemTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.jetbrains.annotations.Range;
import java.util.Arrays;
@ -137,12 +135,9 @@ public abstract class Inventory {
// Lodestone caching
if (newItem.asItem() == Items.COMPASS) {
CompoundTag nbt = newItem.getNbt();
if (nbt != null) {
Tag lodestoneTag = nbt.get("LodestoneTracked");
if (lodestoneTag instanceof ByteTag) {
session.getLodestoneCache().cacheInventoryItem(newItem);
}
var tracker = newItem.getComponent(DataComponentType.LODESTONE_TRACKER);
if (tracker != null) {
session.getLodestoneCache().cacheInventoryItem(newItem, tracker);
}
}
}

View file

@ -25,8 +25,10 @@
package org.geysermc.geyser.inventory.item;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import lombok.Getter;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents;
import java.util.Locale;
@ -85,6 +87,10 @@ public enum Potion {
this.bedrockId = (short) bedrockId;
}
public PotionContents toComponent() {
return new PotionContents(this.ordinal(), -1, Int2ObjectMaps.emptyMap());
}
public static @Nullable Potion getByJavaIdentifier(String javaIdentifier) {
for (Potion potion : VALUES) {
if (potion.javaIdentifier.equals(javaIdentifier)) {

View file

@ -25,9 +25,6 @@
package org.geysermc.geyser.inventory.recipe;
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import org.cloudburstmc.protocol.bedrock.data.TrimMaterial;
@ -37,6 +34,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemTagDescri
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
/**
* Stores information on trim materials and patterns, including smithing armor hacks for pre-1.20.
@ -54,11 +52,11 @@ public final class TrimRecipe {
// Color is used when hovering over the item
// Find the nearest legacy color from the RGB Java gives us to work with
// Also yes this is a COMPLETE hack but it works ok!!!!!
StringTag colorTag = ((CompoundTag) entry.getData().get("description")).get("color");
TextColor color = TextColor.fromHexString(colorTag.getValue());
String colorTag = entry.getData().getCompound("description").getString("color");
TextColor color = TextColor.fromHexString(colorTag);
String legacy = MessageTranslator.convertMessage(Component.space().color(color));
String itemIdentifier = ((StringTag) entry.getData().get("ingredient")).getValue();
String itemIdentifier = entry.getData().getString("ingredient");
ItemMapping itemMapping = session.getItemMappings().getMapping(itemIdentifier);
if (itemMapping == null) {
// This should never happen so not sure what to do here.
@ -71,7 +69,7 @@ public final class TrimRecipe {
public static TrimPattern readTrimPattern(GeyserSession session, RegistryEntry entry) {
String key = stripNamespace(entry.getId());
String itemIdentifier = ((StringTag) entry.getData().get("template_item")).getValue();
String itemIdentifier = entry.getData().getString("template_item");
ItemMapping itemMapping = session.getItemMappings().getMapping(itemIdentifier);
if (itemMapping == null) {
// This should never happen so not sure what to do here.

View file

@ -25,13 +25,6 @@
package org.geysermc.geyser.inventory.updater;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
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 it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
@ -53,7 +46,12 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
import java.util.Map;
import java.util.Objects;
public class AnvilInventoryUpdater extends InventoryUpdater {
@ -310,9 +308,9 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
*/
private int calcMergeEnchantmentCost(GeyserSession session, GeyserItemStack input, GeyserItemStack material, boolean bedrock) {
boolean hasCompatible = false;
Object2IntMap<JavaEnchantment> combinedEnchantments = getEnchantments(input, bedrock);
Object2IntMap<JavaEnchantment> combinedEnchantments = getEnchantments(input);
int cost = 0;
for (Object2IntMap.Entry<JavaEnchantment> entry : getEnchantments(material, bedrock).object2IntEntrySet()) {
for (Object2IntMap.Entry<JavaEnchantment> entry : getEnchantments(material).object2IntEntrySet()) {
JavaEnchantment enchantment = entry.getKey();
EnchantmentData data = Registries.ENCHANTMENTS.get(enchantment);
if (data == null) {
@ -371,42 +369,26 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
return cost;
}
private Object2IntMap<JavaEnchantment> getEnchantments(GeyserItemStack itemStack, boolean bedrock) {
if (itemStack.getNbt() == null) {
return Object2IntMaps.emptyMap();
}
Object2IntMap<JavaEnchantment> enchantments = new Object2IntOpenHashMap<>();
Tag enchantmentTag;
private Object2IntMap<JavaEnchantment> getEnchantments(GeyserItemStack itemStack) {
ItemEnchantments enchantmentComponent;
if (isEnchantedBook(itemStack)) {
enchantmentTag = itemStack.getNbt().get("StoredEnchantments");
enchantmentComponent = itemStack.getComponent(DataComponentType.STORED_ENCHANTMENTS);
} else {
enchantmentTag = itemStack.getNbt().get("Enchantments");
enchantmentComponent = itemStack.getComponent(DataComponentType.ENCHANTMENTS);
}
if (enchantmentTag instanceof ListTag listTag) {
for (Tag tag : listTag.getValue()) {
if (tag instanceof CompoundTag enchantTag) {
if (enchantTag.get("id") instanceof StringTag javaEnchId) {
JavaEnchantment enchantment = JavaEnchantment.getByJavaIdentifier(javaEnchId.getValue());
if (enchantment == null) {
GeyserImpl.getInstance().getLogger().debug("Unknown Java enchantment in anvil: " + javaEnchId.getValue());
continue;
}
Tag javaEnchLvl = enchantTag.get("lvl");
if (javaEnchLvl == null || !(javaEnchLvl.getValue() instanceof Number number))
continue;
// Handle duplicate enchantments
if (bedrock) {
enchantments.putIfAbsent(enchantment, number.intValue());
} else {
enchantments.mergeInt(enchantment, number.intValue(), Math::max);
}
}
if (enchantmentComponent != null) {
Object2IntMap<JavaEnchantment> enchantments = new Object2IntOpenHashMap<>();
for (Map.Entry<Integer, Integer> entry : enchantmentComponent.getEnchantments().entrySet()) {
JavaEnchantment enchantment = JavaEnchantment.of(entry.getKey());
if (enchantment == null) {
GeyserImpl.getInstance().getLogger().debug("Unknown Java enchantment in anvil: " + entry.getKey());
continue;
}
enchantments.put(enchantment, entry.getValue().intValue());
}
return enchantments;
}
return enchantments;
return Object2IntMaps.emptyMap();
}
private boolean isEnchantedBook(GeyserItemStack itemStack) {

View file

@ -25,8 +25,6 @@
package org.geysermc.geyser.item;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
@ -41,17 +39,4 @@ public interface DyeableLeatherItem {
}
builder.putInt("customColor", dyedItemColor.getRgb());
}
static void translateNbtToJava(CompoundTag tag) {
IntTag color = tag.get("customColor");
if (color == null) {
return;
}
CompoundTag displayTag = tag.get("display");
if (displayTag == null) {
displayTag = new CompoundTag("display");
}
displayTag.put(color);
tag.remove("customColor");
}
}

View file

@ -25,15 +25,12 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.data.TrimMaterial;
import org.cloudburstmc.protocol.bedrock.data.TrimPattern;
import org.geysermc.geyser.item.ArmorMaterial;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ArmorTrim;
@ -70,19 +67,6 @@ public class ArmorItem extends Item {
}
}
@Override
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
super.translateNbtToJava(tag, mapping);
if (tag.get("Trim") instanceof CompoundTag trim) {
StringTag material = trim.remove("Material");
StringTag pattern = trim.remove("Pattern");
// java has a lowercase key, and namespaced value
trim.put(new StringTag("material", "minecraft:" + material.getValue()));
trim.put(new StringTag("pattern", "minecraft:" + pattern.getValue()));
}
}
@Override
public boolean isValidRepairItem(Item other) {
return material.getRepairIngredient() == other;

View file

@ -25,14 +25,16 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.item.TippedArrowPotion;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents;
public class ArrowItem extends Item {
public ArrowItem(String javaIdentifier, Builder builder) {
@ -40,13 +42,13 @@ public class ArrowItem extends Item {
}
@Override
public @NonNull ItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
TippedArrowPotion tippedArrowPotion = TippedArrowPotion.getByBedrockId(itemData.getDamage());
ItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
if (tippedArrowPotion != null) {
itemStack = Items.TIPPED_ARROW.newItemStack(itemStack.getAmount(), itemStack.getDataComponents());
StringTag potionTag = new StringTag("Potion", tippedArrowPotion.getJavaIdentifier());
//itemStack.getDataComponents().put(DataComponentType.POTION_CONTENTS, new PotionContents());
itemStack = Items.TIPPED_ARROW.newItemStack(itemStack.getAmount(), itemStack.getComponents());
PotionContents contents = new PotionContents(tippedArrowPotion.ordinal(), -1, Int2ObjectMaps.emptyMap());
itemStack.getOrCreateComponents().put(DataComponentType.POTION_CONTENTS, contents);
}
return itemStack;
}

View file

@ -25,7 +25,6 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.*;
import it.unimi.dsi.fastutil.Pair;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtList;
@ -52,7 +51,7 @@ public class BannerItem extends BlockItem {
* the correct ominous banner pattern if Bedrock pulls the item from creative.
*/
private static final List<Pair<BannerPattern, DyeColor>> OMINOUS_BANNER_PATTERN;
private static final ListTag OMINOUS_BANNER_PATTERN_BLOCK;
private static final List<NbtMap> OMINOUS_BANNER_PATTERN_BLOCK;
static {
// Construct what an ominous banner is supposed to look like
@ -67,7 +66,7 @@ public class BannerItem extends BlockItem {
Pair.of(BannerPattern.BORDER, DyeColor.BLACK)
);
OMINOUS_BANNER_PATTERN_BLOCK = new ListTag("patterns");
OMINOUS_BANNER_PATTERN_BLOCK = new ArrayList<>();
for (Pair<BannerPattern, DyeColor> pair : OMINOUS_BANNER_PATTERN) {
OMINOUS_BANNER_PATTERN_BLOCK.add(getJavaBannerPatternTag(pair.left(), pair.right()));
}
@ -92,7 +91,7 @@ public class BannerItem extends BlockItem {
return true;
}
public static boolean isOminous(ListTag blockEntityPatterns) {
public static boolean isOminous(List<NbtMap> blockEntityPatterns) {
return OMINOUS_BANNER_PATTERN_BLOCK.equals(blockEntityPatterns);
}
@ -102,10 +101,10 @@ public class BannerItem extends BlockItem {
* @param patterns The patterns to convert
* @return The new converted patterns
*/
public static NbtList<NbtMap> convertBannerPattern(ListTag patterns) {
public static NbtList<NbtMap> convertBannerPattern(List<NbtMap> patterns) {
List<NbtMap> tagsList = new ArrayList<>();
for (Tag patternTag : patterns.getValue()) {
NbtMap bedrockBannerPattern = getBedrockBannerPattern((CompoundTag) patternTag);
for (NbtMap patternTag : patterns) {
NbtMap bedrockBannerPattern = getBedrockBannerPattern(patternTag);
if (bedrockBannerPattern != null) {
tagsList.add(bedrockBannerPattern);
}
@ -120,9 +119,9 @@ public class BannerItem extends BlockItem {
* @param pattern Java edition pattern nbt
* @return The Bedrock edition format pattern nbt
*/
private static NbtMap getBedrockBannerPattern(CompoundTag pattern) {
BannerPattern bannerPattern = BannerPattern.getByJavaIdentifier((String) pattern.get("pattern").getValue());
DyeColor dyeColor = DyeColor.getByJavaIdentifier((String) pattern.get("color").getValue());
private static NbtMap getBedrockBannerPattern(NbtMap pattern) {
BannerPattern bannerPattern = BannerPattern.getByJavaIdentifier(pattern.getString("pattern"));
DyeColor dyeColor = DyeColor.getByJavaIdentifier(pattern.getString("color"));
if (bannerPattern == null || dyeColor == null) {
return null;
}
@ -133,11 +132,11 @@ public class BannerItem extends BlockItem {
.build();
}
public static CompoundTag getJavaBannerPatternTag(BannerPattern bannerPattern, DyeColor dyeColor) {
CompoundTag tag = new CompoundTag("");
tag.put(new StringTag("pattern", bannerPattern.getJavaIdentifier()));
tag.put(new StringTag("color", dyeColor.getJavaIdentifier()));
return tag;
public static NbtMap getJavaBannerPatternTag(BannerPattern bannerPattern, DyeColor dyeColor) {
return NbtMap.builder()
.putString("pattern", bannerPattern.getJavaIdentifier())
.putString("color", dyeColor.getJavaIdentifier())
.build();
}
/**
@ -190,31 +189,14 @@ public class BannerItem extends BlockItem {
}
@Override
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) { // TODO
super.translateNbtToJava(tag, mapping);
public void translateNbtToJava(@NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) { // TODO
super.translateNbtToJava(bedrockTag, components, mapping);
if (tag.get("Type") instanceof IntTag type && type.getValue() == 1) {
if (bedrockTag.getInt("Type") == 1) {
// Ominous banner pattern
tag.remove("Type");
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
blockEntityTag.put(OMINOUS_BANNER_PATTERN_BLOCK);
tag.put(blockEntityTag);
} else if (tag.get("Patterns") instanceof ListTag patterns && patterns.getElementType() == CompoundTag.class) {
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
ListTag javaPatterns = new ListTag("patterns");
for (Tag pattern : patterns.getValue()) {
BannerPattern bannerPattern = BannerPattern.getByBedrockIdentifier((String) ((CompoundTag) pattern).get("Pattern").getValue());
DyeColor dyeColor = DyeColor.getById((int) ((CompoundTag) pattern).get("Color").getValue());
if (bannerPattern != null && dyeColor != null) {
javaPatterns.add(getJavaBannerPatternTag(bannerPattern, dyeColor));
}
}
blockEntityTag.put(javaPatterns);
tag.put(blockEntityTag);
tag.remove("Patterns"); // Remove the old Bedrock patterns list
// TODO more registry stuff
//components.put(DataComponentType.BANNER_PATTERNS);
}
// Bedrock's creative inventory does not support other patterns as of 1.20.5
}
}

View file

@ -28,11 +28,11 @@ package org.geysermc.geyser.item.type;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.LodestoneTracker;
@ -78,7 +78,7 @@ public class CompassItem extends Item {
}
@Override
public @NonNull ItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
if (mapping.getBedrockIdentifier().equals("minecraft:lodestone_compass")) {
// Revert the entry back to the compass
mapping = mappings.getStoredItems().compass();

View file

@ -25,10 +25,6 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
@ -63,22 +59,4 @@ public class CrossbowItem extends Item {
builder.putCompound("chargedItem", newProjectile.build());
}
}
@Override
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
super.translateNbtToJava(tag, mapping);
if (tag.get("chargedItem") != null) {
CompoundTag chargedItem = tag.get("chargedItem");
CompoundTag newProjectile = new CompoundTag("");
newProjectile.put(new ByteTag("Count", (byte) chargedItem.get("Count").getValue()));
newProjectile.put(new StringTag("id", (String) chargedItem.get("Name").getValue()));
ListTag chargedProjectiles = new ListTag("ChargedProjectiles");
chargedProjectiles.add(newProjectile);
tag.put(chargedProjectiles);
}
}
}

View file

@ -25,11 +25,9 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.item.ArmorMaterial;
import org.geysermc.geyser.item.DyeableLeatherItem;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
@ -45,11 +43,4 @@ public class DyeableArmorItem extends ArmorItem implements DyeableLeatherItem {
DyeableLeatherItem.translateComponentsToBedrock(components, builder);
}
@Override
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
super.translateNbtToJava(tag, mapping);
DyeableLeatherItem.translateNbtToJava(tag);
}
}

View file

@ -25,10 +25,8 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.item.DyeableLeatherItem;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
@ -44,11 +42,4 @@ public class DyeableHorseArmorItem extends Item implements DyeableLeatherItem {
DyeableLeatherItem.translateComponentsToBedrock(components, builder);
}
@Override
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
super.translateNbtToJava(tag, mapping);
DyeableLeatherItem.translateNbtToJava(tag);
}
}

View file

@ -25,9 +25,14 @@
package org.geysermc.geyser.item.type;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.item.Enchantment;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
@ -62,4 +67,29 @@ public class EnchantedBookItem extends Item {
builder.putList("ench", NbtType.COMPOUND, bedrockEnchants);
}
}
@Override
public void translateNbtToJava(@NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
super.translateNbtToJava(bedrockTag, components, mapping);
List<NbtMap> enchantmentTag = bedrockTag.getList("ench", NbtType.COMPOUND);
if (enchantmentTag != null) {
Int2IntMap javaEnchantments = new Int2IntOpenHashMap(enchantmentTag.size());
for (NbtMap bedrockEnchantment : enchantmentTag) {
short bedrockId = bedrockEnchantment.getShort("id");
Enchantment enchantment = Enchantment.getByBedrockId(bedrockId);
if (enchantment != null) {
int level = bedrockEnchantment.getShort("lvl", (short) 1);
// TODO
javaEnchantments.put(Enchantment.JavaEnchantment.valueOf(enchantment.name()).ordinal(), level);
} else {
GeyserImpl.getInstance().getLogger().debug("Unknown bedrock enchantment: " + bedrockId);
}
}
if (!javaEnchantments.isEmpty()) {
components.put(DataComponentType.STORED_ENCHANTMENTS, new ItemEnchantments(javaEnchantments, true));
}
}
}
}

View file

@ -25,9 +25,7 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
import it.unimi.dsi.fastutil.ints.IntArrays;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
@ -36,7 +34,6 @@ import org.geysermc.geyser.level.FireworkColor;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.geyser.util.MathUtils;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Fireworks;
@ -73,8 +70,23 @@ public class FireworkRocketItem extends Item {
}
@Override
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
super.translateNbtToJava(tag, mapping);
public void translateNbtToJava(@NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
super.translateNbtToJava(bedrockTag, components, mapping);
NbtMap fireworksTag = bedrockTag.getCompound("Fireworks");
if (!fireworksTag.isEmpty()) {
List<NbtMap> explosions = fireworksTag.getList("Explosions", NbtType.COMPOUND);
if (!explosions.isEmpty()) {
List<Fireworks.FireworkExplosion> javaExplosions = new ArrayList<>();
for (NbtMap explosion : explosions) {
Fireworks.FireworkExplosion javaExplosion = translateExplosionToJava(explosion);
if (javaExplosion != null) {
javaExplosions.add(javaExplosion);
}
}
components.put(DataComponentType.FIREWORKS, new Fireworks(1, javaExplosions));
}
}
}
static NbtMap translateExplosionToBedrock(Fireworks.FireworkExplosion explosion) {
@ -111,45 +123,22 @@ public class FireworkRocketItem extends Item {
return newExplosionData.build();
}
static CompoundTag translateExplosionToJava(CompoundTag explosion, String newName) {
CompoundTag newExplosionData = new CompoundTag(newName);
if (explosion.get("FireworkType") != null) {
newExplosionData.put(new ByteTag("Type", MathUtils.getNbtByte(explosion.get("FireworkType").getValue())));
}
if (explosion.get("FireworkColor") != null) {
byte[] oldColors = (byte[]) explosion.get("FireworkColor").getValue();
int[] colors = new int[oldColors.length];
/**
* The only thing that the Bedrock creative inventory has - as of 1.20.80 - is color.
*/
static Fireworks.FireworkExplosion translateExplosionToJava(NbtMap explosion) {
byte[] javaColors = explosion.getByteArray("FireworkColor", null);
if (javaColors != null) {
int[] colors = new int[javaColors.length];
int i = 0;
for (byte color : oldColors) {
for (byte color : javaColors) {
colors[i++] = FireworkColor.fromBedrockId(color);
}
newExplosionData.put(new IntArrayTag("Colors", colors));
return new Fireworks.FireworkExplosion(0, colors, IntArrays.EMPTY_ARRAY, false, false);
} else {
return null;
}
if (explosion.get("FireworkFade") != null) {
byte[] oldColors = (byte[]) explosion.get("FireworkFade").getValue();
int[] colors = new int[oldColors.length];
int i = 0;
for (byte color : oldColors) {
colors[i++] = FireworkColor.fromBedrockId(color);
}
newExplosionData.put(new IntArrayTag("FadeColors", colors));
}
if (explosion.get("FireworkTrail") != null) {
newExplosionData.put(new ByteTag("Trail", MathUtils.getNbtByte(explosion.get("FireworkTrail").getValue())));
}
if (explosion.get("FireworkFlicker") != null) {
newExplosionData.put(new ByteTag("Flicker", MathUtils.getNbtByte(explosion.get("FireworkFlicker").getValue())));
}
return newExplosionData;
}
}

View file

@ -25,8 +25,6 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.geyser.registry.type.ItemMapping;
@ -80,15 +78,16 @@ public class FireworkStarItem extends Item {
}
@Override
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
super.translateNbtToJava(tag, mapping);
public void translateNbtToJava(@NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
super.translateNbtToJava(bedrockTag, components, mapping);
Tag explosion = tag.remove("FireworksItem");
if (explosion instanceof CompoundTag) {
CompoundTag newExplosion = FireworkRocketItem.translateExplosionToJava((CompoundTag) explosion, "Explosion");
tag.put(newExplosion);
NbtMap explosion = bedrockTag.getCompound("FireworksItem");
if (!explosion.isEmpty()) {
Fireworks.FireworkExplosion newExplosion = FireworkRocketItem.translateExplosionToJava(explosion);
if (newExplosion == null) {
return;
}
components.put(DataComponentType.FIREWORK_EXPLOSION, newExplosion);
}
// Remove custom color, if any, since this only exists on Bedrock
tag.remove("customColor");
}
}

View file

@ -25,14 +25,12 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Instrument;
@ -80,18 +78,11 @@ public class GoatHornItem extends Item {
}
@Override
public @NonNull ItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
ItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
int damage = itemData.getDamage();
if (damage < 0 || damage >= INSTRUMENTS.size()) {
GeyserImpl.getInstance().getLogger().debug("Unknown goat horn instrument for damage: " + damage);
damage = 0;
}
String instrument = INSTRUMENTS.get(damage);
StringTag instrumentTag = new StringTag("instrument", "minecraft:" + instrument);
//itemStack.getNbt().put(instrumentTag);
itemStack.getOrCreateComponents().put(DataComponentType.INSTRUMENT, Holder.ofId(damage));
return itemStack;
}

View file

@ -25,7 +25,6 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.*;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -33,6 +32,7 @@ import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.item.Enchantment;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.registry.type.ItemMapping;
@ -44,7 +44,6 @@ import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.geyser.translator.item.ItemTranslator;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.Identifier;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
@ -108,11 +107,8 @@ public class Item {
return builder;
}
public @NonNull ItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
if (itemData.getTag() == null) {
return new ItemStack(javaId, itemData.getCount(), null);
}
return new ItemStack(javaId, itemData.getCount(), null/*ItemTranslator.translateToJavaNBT("", itemData.getTag())*/);
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
return GeyserItemStack.of(javaId, itemData.getCount());
}
public ItemMapping toBedrockDefinition(DataComponents components, ItemMappings mappings) {
@ -162,57 +158,60 @@ public class Item {
* </ul>
* Therefore, if translation cannot be achieved for a certain item, it is not necessarily bad.
*/
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
CompoundTag displayTag = tag.get("display");
if (displayTag != null) {
if (displayTag.contains("Name")) {
StringTag nameTag = displayTag.get("Name");
displayTag.put(new StringTag("Name", MessageTranslator.convertToJavaMessage(nameTag.getValue())));
}
public void translateNbtToJava(@NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
// TODO see if any items from the creative menu need this
// CompoundTag displayTag = tag.get("display");
// if (displayTag != null) {
// if (displayTag.contains("Name")) {
// StringTag nameTag = displayTag.get("Name");
// displayTag.put(new StringTag("Name", MessageTranslator.convertToJavaMessage(nameTag.getValue())));
// }
//
// if (displayTag.contains("Lore")) {
// ListTag loreTag = displayTag.get("Lore");
// List<Tag> lore = new ArrayList<>();
// for (Tag subTag : loreTag.getValue()) {
// if (!(subTag instanceof StringTag)) continue;
// lore.add(new StringTag("", MessageTranslator.convertToJavaMessage(((StringTag) subTag).getValue())));
// }
// displayTag.put(new ListTag("Lore", lore));
// }
// }
if (displayTag.contains("Lore")) {
ListTag loreTag = displayTag.get("Lore");
List<Tag> lore = new ArrayList<>();
for (Tag subTag : loreTag.getValue()) {
if (!(subTag instanceof StringTag)) continue;
lore.add(new StringTag("", MessageTranslator.convertToJavaMessage(((StringTag) subTag).getValue())));
}
displayTag.put(new ListTag("Lore", lore));
}
}
ListTag enchantmentTag = tag.remove("ench");
if (enchantmentTag != null) {
List<Tag> enchantments = new ArrayList<>();
for (Tag value : enchantmentTag.getValue()) {
if (!(value instanceof CompoundTag tagValue))
continue;
ShortTag bedrockId = tagValue.get("id");
if (bedrockId == null) continue;
Enchantment enchantment = Enchantment.getByBedrockId(bedrockId.getValue());
if (enchantment != null) {
CompoundTag javaTag = new CompoundTag("");
Map<String, Tag> javaValue = javaTag.getValue();
javaValue.put("id", new StringTag("id", enchantment.getJavaIdentifier()));
ShortTag levelTag = tagValue.get("lvl");
javaValue.put("lvl", new IntTag("lvl", levelTag != null ? levelTag.getValue() : 1));
javaTag.setValue(javaValue);
enchantments.add(javaTag);
} else {
GeyserImpl.getInstance().getLogger().debug("Unknown bedrock enchantment: " + bedrockId);
}
}
if (!enchantments.isEmpty()) {
if ((this instanceof EnchantedBookItem)) {
tag.put(new ListTag("StoredEnchantments", enchantments));
} else {
tag.put(new ListTag("Enchantments", enchantments));
}
}
}
// TODO no creative item should have enchantments *except* enchanted books
// List<NbtMap> enchantmentTag = bedrockTag.getList("ench", NbtType.COMPOUND);
// if (enchantmentTag != null) {
// List<Tag> enchantments = new ArrayList<>();
// for (Tag value : enchantmentTag.getValue()) {
// if (!(value instanceof CompoundTag tagValue))
// continue;
//
// ShortTag bedrockId = tagValue.get("id");
// if (bedrockId == null) continue;
//
// Enchantment enchantment = Enchantment.getByBedrockId(bedrockId.getValue());
// if (enchantment != null) {
// CompoundTag javaTag = new CompoundTag("");
// Map<String, Tag> javaValue = javaTag.getValue();
// javaValue.put("id", new StringTag("id", enchantment.getJavaIdentifier()));
// ShortTag levelTag = tagValue.get("lvl");
// javaValue.put("lvl", new IntTag("lvl", levelTag != null ? levelTag.getValue() : 1));
// javaTag.setValue(javaValue);
//
// enchantments.add(javaTag);
// } else {
// GeyserImpl.getInstance().getLogger().debug("Unknown bedrock enchantment: " + bedrockId);
// }
// }
// if (!enchantments.isEmpty()) {
// if ((this instanceof EnchantedBookItem)) {
// bedrockTag.put(new ListTag("StoredEnchantments", enchantments));
// components.put(DataComponentType.STORED_ENCHANTMENTS, enchantments);
// } else {
// components.put(DataComponentType.ENCHANTMENTS, enchantments);
// }
// }
// }
}
protected final @Nullable NbtMap remapEnchantment(GeyserSession session, int enchantId, int level, BedrockItemBuilder builder) {
@ -243,8 +242,8 @@ public class Item {
/* Translation methods end */
public ItemStack newItemStack(int count, DataComponents components) {
return new ItemStack(this.javaId, count, components);
public GeyserItemStack newItemStack(int count, DataComponents components) {
return GeyserItemStack.of(this.javaId, count, components);
}
public void setJavaId(int javaId) { // TODO like this?

View file

@ -25,10 +25,7 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
@ -52,15 +49,4 @@ public class MapItem extends Item {
builder.putInt("map_name_index", mapValue);
builder.putByte("map_display_players", (byte) 1);
}
@Override
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
super.translateNbtToJava(tag, mapping);
IntTag mapNameIndex = tag.remove("map_name_index");
if (mapNameIndex != null) {
tag.put(new IntTag("map", mapNameIndex.getValue()));
tag.remove("map_uuid");
}
}
}

View file

@ -25,16 +25,15 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.item.Potion;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.translator.item.CustomItemTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents;
@ -69,12 +68,11 @@ public class PotionItem extends Item {
}
@Override
public @NonNull ItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
Potion potion = Potion.getByBedrockId(itemData.getDamage());
ItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
if (potion != null) {
StringTag potionTag = new StringTag("Potion", potion.getJavaIdentifier());
//itemStack.getNbt().put(potionTag);
itemStack.getOrCreateComponents().put(DataComponentType.POTION_CONTENTS, potion.toComponent());
}
return itemStack;
}

View file

@ -25,7 +25,6 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
@ -85,12 +84,4 @@ public class ShulkerBoxItem extends BlockItem {
}
builder.putList("Items", NbtType.COMPOUND, itemsList);
}
@Override
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
super.translateNbtToJava(tag, mapping);
// Remove any extraneous Bedrock tag and don't touch the Java one
tag.remove("Items");
}
}

View file

@ -25,13 +25,12 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.geyser.entity.type.living.animal.TropicalFishEntity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.MinecraftLocale;
@ -58,26 +57,22 @@ public class TropicalFishBucketItem extends Item {
builder.putString("CustomName", MinecraftLocale.getLocaleString("entity.minecraft.tropical_fish", session.locale()));
// Add Java's client side lore tag
// Do you know how frequently Java NBT used to be before 1.20.5? It was a lot. And now it's just this lowly check.
CompoundTag entityTag = components.get(DataComponentType.BUCKET_ENTITY_DATA);
NbtMap entityTag = components.get(DataComponentType.BUCKET_ENTITY_DATA);
if (entityTag != null && !entityTag.isEmpty()) {
//TODO test
Tag bucketVariant = entityTag.get("BucketVariantTag");
if (bucketVariant == null || !(bucketVariant.getValue() instanceof Number)) {
return;
}
int bucketVariant = entityTag.getInt("BucketVariantTag");
List<String> lore = builder.getOrCreateLore();
int varNumber = ((Number) bucketVariant.getValue()).intValue();
int predefinedVariantId = TropicalFishEntity.getPredefinedId(varNumber);
int predefinedVariantId = TropicalFishEntity.getPredefinedId(bucketVariant);
if (predefinedVariantId != -1) {
Component tooltip = Component.translatable("entity.minecraft.tropical_fish.predefined." + predefinedVariantId, LORE_STYLE);
lore.add(0, MessageTranslator.convertMessage(tooltip, session.locale()));
} else {
Component typeTooltip = Component.translatable("entity.minecraft.tropical_fish.type." + TropicalFishEntity.getVariantName(varNumber), LORE_STYLE);
Component typeTooltip = Component.translatable("entity.minecraft.tropical_fish.type." + TropicalFishEntity.getVariantName(bucketVariant), LORE_STYLE);
lore.add(0, MessageTranslator.convertMessage(typeTooltip, session.locale()));
byte baseColor = TropicalFishEntity.getBaseColor(varNumber);
byte patternColor = TropicalFishEntity.getPatternColor(varNumber);
byte baseColor = TropicalFishEntity.getBaseColor(bucketVariant);
byte patternColor = TropicalFishEntity.getPatternColor(bucketVariant);
Component colorTooltip = Component.translatable("color.minecraft." + TropicalFishEntity.getColorName(baseColor), LORE_STYLE);
if (baseColor != patternColor) {
colorTooltip = colorTooltip.append(Component.text(", ", LORE_STYLE))

View file

@ -25,15 +25,10 @@
package org.geysermc.geyser.item.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
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 org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.geyser.translator.text.MessageTranslator;
@ -69,24 +64,4 @@ public class WritableBookItem extends Item {
builder.putList("pages", NbtType.COMPOUND, bedrockPages);
}
@Override
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
super.translateNbtToJava(tag, mapping);
if (!tag.contains("pages")) {
return;
}
List<Tag> pages = new ArrayList<>();
ListTag pagesTag = tag.get("pages");
for (Tag subTag : pagesTag.getValue()) {
if (!(subTag instanceof CompoundTag pageTag))
continue;
StringTag textTag = pageTag.get("text");
pages.add(new StringTag("", textTag.getValue()));
}
tag.remove("pages");
tag.put(new ListTag("pages", pages));
}
}

View file

@ -62,7 +62,7 @@ public class WrittenBookItem extends Item {
for (Filterable<Component> page : bookContent.getPages()) {
NbtMapBuilder pageBuilder = NbtMap.builder();
pageBuilder.putString("photoname", "");
pageBuilder.putString("text", MessageTranslator.convertMessage(page.getRaw()));
pageBuilder.putString("text", MessageTranslator.convertMessage(session, page.getRaw()));
bedrockPages.add(pageBuilder.build());
}
builder.putList("pages", NbtType.COMPOUND, bedrockPages);
@ -70,6 +70,5 @@ public class WrittenBookItem extends Item {
builder.putString("title", bookContent.getTitle().getRaw())
.putString("author", bookContent.getAuthor())
.putInt("generation", bookContent.getGeneration());
// TODO isResolved
}
}

View file

@ -25,13 +25,9 @@
package org.geysermc.geyser.level;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
import java.util.List;
/**
* Represents the information we store from the current Java dimension
* @param piglinSafe Whether piglins and hoglins are safe from conversion in this dimension.
@ -39,33 +35,16 @@ import java.util.List;
*/
public record JavaDimension(int minY, int maxY, boolean piglinSafe, double worldCoordinateScale) {
public static void load(List<RegistryEntry> entries, Int2ObjectMap<JavaDimension> map) {
for (int i = 0; i < entries.size(); i++) {
RegistryEntry entry = entries.get(i);
CompoundTag dimension = entry.getData();
int minY = ((IntTag) dimension.get("min_y")).getValue();
int maxY = ((IntTag) dimension.get("height")).getValue();
// Logical height can be ignored probably - seems to be for artificial limits like the Nether.
// Set if piglins/hoglins should shake
boolean piglinSafe = ((Number) dimension.get("piglin_safe").getValue()).byteValue() != (byte) 0;
// Load world coordinate scale for the world border
double coordinateScale = ((Number) dimension.get("coordinate_scale").getValue()).doubleValue();
map.put(i, new JavaDimension(minY, maxY, piglinSafe, coordinateScale));
}
}
public static JavaDimension read(RegistryEntry entry) {
CompoundTag dimension = entry.getData();
int minY = ((IntTag) dimension.get("min_y")).getValue();
int maxY = ((IntTag) dimension.get("height")).getValue();
NbtMap dimension = entry.getData();
int minY = dimension.getInt("min_y");
int maxY = dimension.getInt("height");
// Logical height can be ignored probably - seems to be for artificial limits like the Nether.
// Set if piglins/hoglins should shake
boolean piglinSafe = ((Number) dimension.get("piglin_safe").getValue()).byteValue() != (byte) 0;
boolean piglinSafe = dimension.getBoolean("piglin_safe");
// Load world coordinate scale for the world border
double coordinateScale = ((Number) dimension.get("coordinate_scale").getValue()).doubleValue();
double coordinateScale = dimension.getDouble("coordinate_scale");
return new JavaDimension(minY, maxY, piglinSafe, coordinateScale);
}

View file

@ -25,15 +25,13 @@
package org.geysermc.geyser.session.cache;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.GlobalPos;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.LodestoneTracker;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.GlobalPos;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.LodestoneTracker;
import java.util.Map;
import java.util.WeakHashMap;
@ -54,17 +52,7 @@ public final class LodestoneCache {
*/
private int id = 1;
public void cacheInventoryItem(GeyserItemStack itemStack) {
DataComponents components = itemStack.getComponents();
if (components == null) {
// invalid
return;
}
LodestoneTracker tracker = components.get(DataComponentType.LODESTONE_TRACKER);
if (tracker == null) {
return;
}
public void cacheInventoryItem(GeyserItemStack itemStack, LodestoneTracker tracker) {
GlobalPos position = tracker.getPos();
if (position == null) {

View file

@ -26,10 +26,9 @@
package org.geysermc.geyser.skin;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.data.skin.ImageData;
import org.cloudburstmc.protocol.bedrock.data.skin.SerializedSkin;
import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket;
@ -45,6 +44,7 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;
@ -224,22 +224,22 @@ public class SkinManager {
* @param tag tag to build the GameProfileData from
* @return The built GameProfileData, or null if this wasn't a valid tag
*/
public static @Nullable GameProfileData from(CompoundTag tag) {
if (!(tag.get("Properties") instanceof CompoundTag propertiesTag)) {
public static @Nullable GameProfileData from(NbtMap tag) {
NbtMap properties = tag.getCompound("Properties", null);
if (properties == null) {
return null;
}
if (!(propertiesTag.get("textures") instanceof ListTag texturesTag) || texturesTag.size() == 0) {
List<NbtMap> textures = properties.getList("textures", NbtType.COMPOUND);
if (textures.isEmpty()) {
return null;
}
if (!(texturesTag.get(0) instanceof CompoundTag texturesData)) {
return null;
}
if (!(texturesData.get("Value") instanceof StringTag skinDataValue)) {
String skinDataValue = textures.get(0).getString("Value", null);
if (skinDataValue == null) {
return null;
}
try {
return loadFromJson(skinDataValue.getValue());
return loadFromJson(skinDataValue);
} catch (IOException e) {
GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for tag " + tag);
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {

View file

@ -25,15 +25,14 @@
package org.geysermc.geyser.text;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
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 net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
@ -42,28 +41,28 @@ public final class TextDecoration {
private final Style style;
private final Set<Parameter> parameters;
public TextDecoration(CompoundTag tag) {
translationKey = (String) tag.get("translation_key").getValue();
public TextDecoration(NbtMap tag) {
translationKey = tag.getString("translation_key");
CompoundTag styleTag = tag.get("style");
NbtMap styleTag = tag.getCompound("style");
Style.Builder builder = Style.style();
if (styleTag != null) {
StringTag color = styleTag.get("color");
if (!styleTag.isEmpty()) {
String color = styleTag.getString("color", null);
if (color != null) {
builder.color(NamedTextColor.NAMES.value(color.getValue()));
builder.color(NamedTextColor.NAMES.value(color));
}
//TODO implement the rest
Tag italic = styleTag.get("italic");
if (italic != null && ((Number) italic.getValue()).byteValue() == (byte) 1) {
boolean italic = styleTag.getBoolean("italic");
if (italic) {
builder.decorate(net.kyori.adventure.text.format.TextDecoration.ITALIC);
}
}
style = builder.build();
this.parameters = EnumSet.noneOf(Parameter.class);
ListTag parameters = tag.get("parameters");
for (Tag parameter : parameters) {
this.parameters.add(Parameter.valueOf(((String) parameter.getValue()).toUpperCase(Locale.ROOT)));
List<String> parameters = tag.getList("parameters", NbtType.STRING);
for (String parameter : parameters) {
this.parameters.add(Parameter.valueOf(parameter.toUpperCase(Locale.ROOT)));
}
}
@ -90,8 +89,8 @@ public final class TextDecoration {
public static TextDecoration readChatType(RegistryEntry entry) {
// Note: The ID is NOT ALWAYS THE SAME! ViaVersion as of 1.19 adds two registry entries that do NOT match vanilla.
CompoundTag tag = entry.getData();
CompoundTag chat = tag.get("chat");
NbtMap tag = entry.getData();
NbtMap chat = tag.getCompound("chat", null);
TextDecoration textDecoration = null;
if (chat != null) {
textDecoration = new TextDecoration(chat);

View file

@ -224,7 +224,7 @@ public abstract class InventoryTranslator {
//only set the head if the destination is the head slot
GeyserItemStack javaItem = inventory.getItem(sourceSlot);
if (javaItem.asItem() == Items.PLAYER_HEAD
&& javaItem.getNbt() != null) {
&& javaItem.getComponents() != null) {
FakeHeadProvider.setHead(session, session.getPlayerEntity(), javaItem.getComponents());
}
} else if (sourceSlot == 5) {

View file

@ -94,7 +94,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
contents[i - 5] = item.getItemData(session);
if (i == 5 &&
item.asItem() == Items.PLAYER_HEAD &&
item.getNbt() != null) {
item.getComponents() != null) {
FakeHeadProvider.setHead(session, session.getPlayerEntity(), item.getComponents());
}
}
@ -138,7 +138,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
if (slot == 5) {
// Check for custom skull
if (javaItem.asItem() == Items.PLAYER_HEAD
&& javaItem.getNbt() != null) {
&& javaItem.getComponents() != null) {
FakeHeadProvider.setHead(session, session.getPlayerEntity(), javaItem.getComponents());
} else {
FakeHeadProvider.restoreOriginalSkin(session, session.getPlayerEntity());

View file

@ -58,7 +58,7 @@ public class ShulkerInventoryTranslator extends AbstractBlockInventoryTranslator
.putInt("z", position.getZ())
.putString("CustomName", inventory.getTitle());
// Don't reset facing property
shulkerBoxTranslator.translateTag(tag, null, javaBlockState);
shulkerBoxTranslator.translateTag(session, tag, null, javaBlockState);
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag.build());

View file

@ -29,22 +29,11 @@ import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.auth.data.GameProfile.Texture;
import com.github.steveice10.mc.auth.data.GameProfile.TextureType;
import com.github.steveice10.mc.auth.exception.property.PropertyException;
import org.geysermc.mcprotocollib.protocol.data.game.Identifier;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.ModifierOperation;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.AdventureModePredicate;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemAttributeModifiers;
import com.github.steveice10.opennbt.tag.builtin.*;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtList;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
@ -63,6 +52,13 @@ import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.mcprotocollib.protocol.data.game.Identifier;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.ModifierOperation;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.AdventureModePredicate;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemAttributeModifiers;
import java.text.DecimalFormat;
import java.util.ArrayList;
@ -93,16 +89,17 @@ public final class ItemTranslator {
ItemMapping bedrockItem = mappings.getMapping(data);
Item javaItem = bedrockItem.getJavaItem();
ItemStack itemStack = javaItem.translateToJava(data, bedrockItem, mappings);
GeyserItemStack itemStack = javaItem.translateToJava(data, bedrockItem, mappings);
// if (itemStack.getNbt() != null) {
// javaItem.translateNbtToJava(itemStack.getNbt(), bedrockItem);
// if (itemStack.getNbt().isEmpty()) {
// // Otherwise, seems to cause issues with villagers accepting books, and I don't see how this will break anything else. - Camotoy
// itemStack = new ItemStack(itemStack.getId(), itemStack.getAmount(), null);
// }
// }
return itemStack;
NbtMap nbt = data.getTag();
if (nbt != null && !nbt.isEmpty()) {
DataComponents components = new DataComponents(new HashMap<>());
javaItem.translateNbtToJava(nbt, components, bedrockItem);
if (!components.getDataComponents().isEmpty()) {
itemStack.setComponents(components);
}
}
return itemStack.getItemStack();
}
public static ItemData.@NonNull Builder translateToBedrock(GeyserSession session, int javaId, int count, DataComponents components) {
@ -375,125 +372,6 @@ public final class ItemTranslator {
}
}
public static NbtMap translateNbtToBedrock(CompoundTag tag) {
if (!tag.getValue().isEmpty()) {
NbtMapBuilder builder = NbtMap.builder();
for (Tag javaTag : tag.values()) {
Object translatedTag = translateToBedrockNBT(javaTag);
if (translatedTag == null)
continue;
builder.put(javaTag.getName(), translatedTag);
}
return builder.build();
}
return NbtMap.EMPTY;
}
private static @Nullable Object translateToBedrockNBT(Tag tag) {
if (tag instanceof CompoundTag compoundTag) {
return translateNbtToBedrock(compoundTag);
}
if (tag instanceof ListTag listTag) {
List<Object> tagList = new ArrayList<>();
for (Tag value : listTag) {
tagList.add(translateToBedrockNBT(value));
}
NbtType<?> type = NbtType.COMPOUND;
if (!tagList.isEmpty()) {
type = NbtType.byClass(tagList.get(0).getClass());
}
//noinspection unchecked,rawtypes
return new NbtList(type, tagList);
}
if (tag instanceof LongArrayTag) {
//Long array tag does not exist in BE
//LongArrayTag longArrayTag = (LongArrayTag) tag;
//return new org.cloudburstmc.nbt.tag.LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
return null;
}
return tag.getValue();
}
public static CompoundTag translateToJavaNBT(String name, NbtMap tag) {
CompoundTag javaTag = new CompoundTag(name);
Map<String, Tag> javaValue = javaTag.getValue();
if (tag != null && !tag.isEmpty()) {
for (Map.Entry<String, Object> entry : tag.entrySet()) {
Tag translatedTag = translateToJavaNBT(entry.getKey(), entry.getValue());
if (translatedTag == null)
continue;
javaValue.put(translatedTag.getName(), translatedTag);
}
}
javaTag.setValue(javaValue);
return javaTag;
}
private static @Nullable Tag translateToJavaNBT(String name, Object object) {
if (object instanceof int[]) {
return new IntArrayTag(name, (int[]) object);
}
if (object instanceof byte[]) {
return new ByteArrayTag(name, (byte[]) object);
}
if (object instanceof Byte) {
return new ByteTag(name, (byte) object);
}
if (object instanceof Float) {
return new FloatTag(name, (float) object);
}
if (object instanceof Double) {
return new DoubleTag(name, (double) object);
}
if (object instanceof Integer) {
return new IntTag(name, (int) object);
}
if (object instanceof long[]) {
return new LongArrayTag(name, (long[]) object);
}
if (object instanceof Long) {
return new LongTag(name, (long) object);
}
if (object instanceof Short) {
return new ShortTag(name, (short) object);
}
if (object instanceof String) {
return new StringTag(name, (String) object);
}
if (object instanceof List) {
List<Tag> tags = new ArrayList<>();
for (Object value : (List<?>) object) {
Tag javaTag = translateToJavaNBT("", value);
if (javaTag != null)
tags.add(javaTag);
}
return new ListTag(name, tags);
}
if (object instanceof NbtMap map) {
return translateToJavaNBT(name, map);
}
return null;
}
/**
* Translates the display name of the item
* @param session the Bedrock client's session

View file

@ -25,40 +25,43 @@
package org.geysermc.geyser.translator.level.block.entity;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.item.type.BannerItem;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import java.util.List;
@BlockEntity(type = BlockEntityType.BANNER)
public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
int bannerColor = BlockStateValues.getBannerColor(blockState);
if (bannerColor != -1) {
builder.put("Base", 15 - bannerColor);
bedrockNbt.putInt("Base", 15 - bannerColor);
}
if (tag == null) {
if (javaNbt == null) {
return;
}
if (tag.get("patterns") instanceof ListTag patterns) {
List<NbtMap> patterns = javaNbt.getList("patterns", NbtType.COMPOUND);
if (!patterns.isEmpty()) {
if (BannerItem.isOminous(patterns)) {
// This is an ominous banner; don't try to translate the raw patterns (it doesn't translate correctly)
// and tell the Bedrock client that this is an ominous banner
builder.putInt("Type", 1);
bedrockNbt.putInt("Type", 1);
} else {
builder.put("Patterns", BannerItem.convertBannerPattern(patterns));
bedrockNbt.putList("Patterns", NbtType.COMPOUND, BannerItem.convertBannerPattern(patterns));
}
}
Tag customName = tag.get("CustomName");
String customName = javaNbt.getString("CustomName", null);
if (customName != null) {
builder.put("CustomName", customName.getValue());
bedrockNbt.putString("CustomName", customName);
}
}
}

View file

@ -25,18 +25,19 @@
package org.geysermc.geyser.translator.level.block.entity;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
@BlockEntity(type = BlockEntityType.BEACON)
public class BeaconBlockEntityTranslator extends BlockEntityTranslator {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
int primary = getOrDefault(tag.get("Primary"), 0);
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
int primary = javaNbt.getInt("primary");
// The effects here generally map one-to-one Java <-> Bedrock. Only the newer ones get more complicated
builder.putInt("primary", primary == -1 ? 0 : primary);
int secondary = getOrDefault(tag.get("Secondary"), 0);
builder.putInt("secondary", secondary == -1 ? 0 : secondary);
bedrockNbt.putInt("primary", primary == -1 ? 0 : primary);
int secondary = javaNbt.getInt("secondary");
bedrockNbt.putInt("secondary", secondary == -1 ? 0 : secondary);
}
}

View file

@ -25,20 +25,21 @@
package org.geysermc.geyser.translator.level.block.entity;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
@BlockEntity(type = BlockEntityType.BED)
public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
byte bedcolor = BlockStateValues.getBedColor(blockState);
// Just in case...
if (bedcolor == -1) {
bedcolor = 0;
}
builder.put("color", bedcolor);
bedrockNbt.putByte("color", bedcolor);
}
}

View file

@ -25,13 +25,11 @@
package org.geysermc.geyser.translator.level.block.entity;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.BlockEntityUtils;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
/**
* The class that all block entities (on both Java and Bedrock) should translate with
@ -40,11 +38,11 @@ public abstract class BlockEntityTranslator {
protected BlockEntityTranslator() {
}
public abstract void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState);
public abstract void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState);
public NbtMap getBlockEntityTag(GeyserSession session, BlockEntityType type, int x, int y, int z, CompoundTag tag, int blockState) {
public NbtMap getBlockEntityTag(GeyserSession session, BlockEntityType type, int x, int y, int z, NbtMap javaNbt, int blockState) {
NbtMapBuilder tagBuilder = getConstantBedrockTag(type, x, y, z);
translateTag(tagBuilder, tag, blockState);
translateTag(session, tagBuilder, javaNbt, blockState);
return tagBuilder.build();
}
@ -59,9 +57,4 @@ public abstract class BlockEntityTranslator {
.putInt("z", z)
.putString("id", bedrockId);
}
@SuppressWarnings("unchecked")
protected <T> T getOrDefault(Tag tag, T defaultValue) {
return (tag != null && tag.getValue() != null) ? (T) tag.getValue() : defaultValue;
}
}

View file

@ -25,49 +25,46 @@
package org.geysermc.geyser.translator.level.block.entity;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
@BlockEntity(type = BlockEntityType.BRUSHABLE_BLOCK)
public class BrushableBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
if (!(tag.remove("item") instanceof CompoundTag itemTag)) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
NbtMap itemTag = javaNbt.getCompound("item");
if (itemTag.isEmpty()) {
return;
}
Tag hitDirection = tag.get("hit_direction");
if (hitDirection == null) {
byte hitDirection = javaNbt.getByte("hit_direction", (byte) -1);
if (hitDirection == -1) {
// java server sends no direction when the item recedes back into the block (if player stops brushing)
return;
}
String id = ((StringTag) itemTag.get("id")).getValue();
String id = itemTag.getString("id");
if (Items.AIR.javaIdentifier().equals(id)) {
return; // server sends air when the block contains nothing
}
ItemMapping mapping = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getMapping(id);
ItemMapping mapping = session.getItemMappings().getMapping(id);
if (mapping == null) {
return;
}
NbtMapBuilder itemBuilder = NbtMap.builder()
.putString("Name", mapping.getBedrockIdentifier())
.putByte("Count", (byte) itemTag.get("Count").getValue());
.putByte("Count", (byte) itemTag.getByte("Count"));
builder.putCompound("item", itemBuilder.build());
bedrockNbt.putCompound("item", itemBuilder.build());
// controls which side the item protrudes from
builder.putByte("brush_direction", ((Number) hitDirection.getValue()).byteValue());
bedrockNbt.putByte("brush_direction", hitDirection);
// controls how much the item protrudes
builder.putInt("brush_count", BlockStateValues.getBrushProgress(blockState));
bedrockNbt.putInt("brush_count", BlockStateValues.getBrushProgress(blockState));
}
}

View file

@ -25,37 +25,37 @@
package org.geysermc.geyser.translator.level.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.registry.Registries;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import java.util.List;
@BlockEntity(type = BlockEntityType.CAMPFIRE)
public class CampfireBlockEntityTranslator extends BlockEntityTranslator {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
if (tag.get("Items") instanceof ListTag items) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
List<NbtMap> items = javaNbt.getList("Items", NbtType.COMPOUND);
if (items != null) {
int i = 1;
for (Tag itemTag : items.getValue()) {
builder.put("Item" + i, getItem((CompoundTag) itemTag));
for (NbtMap itemTag : items) {
bedrockNbt.put("Item" + i, getItem(session, itemTag));
i++;
}
}
}
protected NbtMap getItem(CompoundTag tag) {
// TODO: Version independent mappings
ItemMapping mapping = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getMapping((String) tag.get("id").getValue());
NbtMapBuilder tagBuilder = NbtMap.builder()
.putString("Name", mapping.getBedrockIdentifier())
.putByte("Count", (byte) tag.get("Count").getValue())
.putShort("Damage", (short) mapping.getBedrockData());
tagBuilder.put("tag", NbtMap.builder().build());
protected NbtMap getItem(GeyserSession session, NbtMap tag) {
ItemMapping mapping = session.getItemMappings().getMapping(tag.getString("id"));
if (mapping == null) {
mapping = ItemMapping.AIR;
}
NbtMapBuilder tagBuilder = BedrockItemBuilder.createItemNbt(mapping, tag.getByte("Count"), mapping.getBedrockData());
tagBuilder.put("tag", NbtMap.builder().build()); // I don't think this is necessary... - Camo, 1.20.5/1.20.80
return tagBuilder.build();
}
}

View file

@ -25,34 +25,31 @@
package org.geysermc.geyser.translator.level.block.entity;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.*;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
@BlockEntity(type = BlockEntityType.COMMAND_BLOCK)
public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
if (tag == null || tag.size() < 5) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
if (javaNbt == null || javaNbt.size() < 5) {
return; // These values aren't here
}
// Java infers from the block state, but Bedrock needs it in the tag
builder.put("conditionalMode", BlockStateValues.getCommandBlockValues().getOrDefault(blockState, (byte) 0));
bedrockNbt.putByte("conditionalMode", BlockStateValues.getCommandBlockValues().getOrDefault(blockState, (byte) 0));
// Java and Bedrock values
builder.put("conditionMet", ((ByteTag) tag.get("conditionMet")).getValue());
builder.put("auto", ((ByteTag) tag.get("auto")).getValue());
builder.put("CustomName", MessageTranslator.convertJsonMessage(((StringTag) tag.get("CustomName")).getValue()));
builder.put("powered", ((ByteTag) tag.get("powered")).getValue());
builder.put("Command", ((StringTag) tag.get("Command")).getValue());
builder.put("SuccessCount", ((IntTag) tag.get("SuccessCount")).getValue());
builder.put("TrackOutput", ((ByteTag) tag.get("TrackOutput")).getValue());
builder.put("UpdateLastExecution", ((ByteTag) tag.get("UpdateLastExecution")).getValue());
if (tag.get("LastExecution") != null) {
builder.put("LastExecution", ((LongTag) tag.get("LastExecution")).getValue());
} else {
builder.put("LastExecution", (long) 0);
}
bedrockNbt.putByte("conditionMet", javaNbt.getByte("conditionMet"));
bedrockNbt.putByte("auto", javaNbt.getByte("auto"));
bedrockNbt.putString("CustomName", MessageTranslator.convertJsonMessage(javaNbt.getString("CustomName"), session.locale()));
bedrockNbt.putByte("powered", javaNbt.getByte("powered"));
bedrockNbt.putString("Command", javaNbt.getString("Command"));
bedrockNbt.putInt("SuccessCount", javaNbt.getInt("SuccessCount"));
bedrockNbt.putByte("TrackOutput", javaNbt.getByte("TrackOutput"));
bedrockNbt.putByte("UpdateLastExecution", javaNbt.getByte("UpdateLastExecution"));
bedrockNbt.putLong("LastExecution", javaNbt.getLong("LastExecution")); // Note: may not be present? Was a null check before 1.20.5
}
}

View file

@ -25,33 +25,22 @@
package org.geysermc.geyser.translator.level.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
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 org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import java.util.ArrayList;
import java.util.List;
@BlockEntity(type = BlockEntityType.DECORATED_POT)
public class DecoratedPotBlockEntityTranslator extends BlockEntityTranslator {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
if (tag == null) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
if (javaNbt == null) {
return;
}
// exact same format
if (tag.get("sherds") instanceof ListTag sherds) {
List<String> translated = new ArrayList<>(4);
for (Tag sherd : sherds) {
translated.add(((StringTag) sherd).getValue());
}
builder.putList("sherds", NbtType.STRING, translated);
}
bedrockNbt.putList("sherds", NbtType.STRING, javaNbt.getList("sherds", NbtType.STRING));
}
}

View file

@ -25,8 +25,8 @@
package org.geysermc.geyser.translator.level.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.block.DoubleChestValue;
@ -47,17 +47,17 @@ public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator impl
@Override
public void updateBlock(GeyserSession session, int blockState, Vector3i position) {
NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(BlockEntityType.CHEST), position.getX(), position.getY(), position.getZ());
translateTag(tagBuilder, null, blockState);
translateTag(session, tagBuilder, null, blockState);
BlockEntityUtils.updateBlockEntity(session, tagBuilder.build(), position);
}
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
DoubleChestValue chestValues = BlockStateValues.getDoubleChestValues().get(blockState);
if (chestValues != null) {
int x = (int) builder.get("x");
int z = (int) builder.get("z");
translateChestValue(builder, chestValues, x, z);
int x = (int) bedrockNbt.get("x");
int z = (int) bedrockNbt.get("z");
translateChestValue(bedrockNbt, chestValues, x, z);
}
}
@ -88,10 +88,10 @@ public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator impl
x = x + (chestValues.isLeft() ? 1 : -1);
}
}
builder.put("pairx", x);
builder.put("pairz", z);
builder.putInt("pairx", x);
builder.putInt("pairz", z);
if (!chestValues.isLeft()) {
builder.put("pairlead", (byte) 1);
builder.putInt("pairlead", (byte) 1);
}
}
}

View file

@ -25,11 +25,12 @@
package org.geysermc.geyser.translator.level.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.session.GeyserSession;
public class EmptyBlockEntityTranslator extends BlockEntityTranslator {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
}
}

View file

@ -25,44 +25,28 @@
package org.geysermc.geyser.translator.level.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.LongTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import org.cloudburstmc.nbt.NbtList;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import java.util.LinkedHashMap;
@BlockEntity(type = BlockEntityType.END_GATEWAY)
public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Tag ageTag = tag.get("Age");
if (ageTag instanceof LongTag) {
builder.put("Age", (int) ((long) ageTag.getValue()));
}
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
bedrockNbt.putInt("Age", (int) javaNbt.getLong("Age"));
// Java sometimes does not provide this tag, but Bedrock crashes if it doesn't exist
// Linked coordinates
IntList tagsList = new IntArrayList();
// Yes, the axis letters are capitalized
tagsList.add(getExitPortalCoordinate(tag, "X"));
tagsList.add(getExitPortalCoordinate(tag, "Y"));
tagsList.add(getExitPortalCoordinate(tag, "Z"));
builder.put("ExitPortal", new NbtList<>(NbtType.INT, tagsList));
}
private int getExitPortalCoordinate(CompoundTag tag, String axis) {
// Return 0 if it doesn't exist, otherwise give proper value
if (tag.get("ExitPortal") != null) {
LinkedHashMap<?, ?> compoundTag = (LinkedHashMap<?, ?>) tag.get("ExitPortal").getValue();
IntTag intTag = (IntTag) compoundTag.get(axis);
return intTag.getValue();
}
return 0;
NbtMap exitPortal = javaNbt.getCompound("ExitPortal");
tagsList.add(exitPortal.getInt("X", 0));
tagsList.add(exitPortal.getInt("Y", 0));
tagsList.add(exitPortal.getInt( "Z", 0));
bedrockNbt.put("ExitPortal", new NbtList<>(NbtType.INT, tagsList));
}
}

View file

@ -25,28 +25,27 @@
package org.geysermc.geyser.translator.level.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
@BlockEntity(type = BlockEntityType.JIGSAW)
public class JigsawBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Tag jointTag = tag.get("joint");
if (jointTag instanceof StringTag) {
builder.put("joint", ((StringTag) jointTag).getValue());
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
String joint = javaNbt.getString("joint", null);
if (joint != null) {
bedrockNbt.putString("joint", joint);
} else {
// Tag is not present in at least 1.14.4 Paper
// Minecraft 1.18.1 deliberately has a fallback here, but not for any other value
builder.put("joint", BlockStateValues.getHorizontalFacingJigsaws().contains(blockState) ? "aligned" : "rollable");
bedrockNbt.putString("joint", BlockStateValues.getHorizontalFacingJigsaws().contains(blockState) ? "aligned" : "rollable");
}
builder.put("name", getOrDefault(tag.get("name"), ""));
builder.put("target_pool", getOrDefault(tag.get("pool"), ""));
builder.put("final_state", ((StringTag) tag.get("final_state")).getValue());
builder.put("target", getOrDefault(tag.get("target"), ""));
bedrockNbt.putString("name", javaNbt.getString("name"));
bedrockNbt.putString("target_pool", javaNbt.getString("target_pool"));
bedrockNbt.putString("final_state", javaNbt.getString("final_state"));
bedrockNbt.putString("target", javaNbt.getString("target"));
}
}

View file

@ -25,10 +25,11 @@
package org.geysermc.geyser.translator.level.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.ShulkerInventoryTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
@ -39,12 +40,12 @@ public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator imple
* where {@code tag} is passed as null.
*/
@Override
public void translateTag(NbtMapBuilder builder, @Nullable CompoundTag tag, int blockState) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, @Nullable NbtMap javaNbt, int blockState) {
byte direction = BlockStateValues.getShulkerBoxDirection(blockState);
// Just in case...
if (direction == -1) {
direction = 1;
}
builder.put("facing", direction);
bedrockNbt.putByte("facing", direction);
}
}

View file

@ -25,15 +25,16 @@
package org.geysermc.geyser.translator.level.block.entity;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.SignUtils;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import java.util.List;
@BlockEntity(type = BlockEntityType.SIGN)
public class SignBlockEntityTranslator extends BlockEntityTranslator {
@ -72,25 +73,21 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator {
}
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
builder.putCompound("FrontText", translateSide(tag.get("front_text")));
builder.putCompound("BackText", translateSide(tag.get("back_text")));
var waxed = tag.get("is_waxed");
builder.putBoolean("IsWaxed", waxed != null && waxed.getValue() instanceof Number number && number.byteValue() != 0);
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
bedrockNbt.putCompound("FrontText", translateSide(javaNbt.getCompound("front_text")));
bedrockNbt.putCompound("BackText", translateSide(javaNbt.getCompound("back_text")));
bedrockNbt.putBoolean("IsWaxed", javaNbt.getBoolean("is_waxed"));
}
private NbtMap translateSide(Tag tag) {
if (!(tag instanceof CompoundTag signData)) {
return NbtMap.EMPTY;
}
private NbtMap translateSide(NbtMap javaNbt) {
NbtMapBuilder builder = NbtMap.builder();
StringBuilder signText = new StringBuilder();
Tag messages = signData.get("messages");
if (messages instanceof ListTag listTag) {
var it = listTag.iterator();
List<String> messages = javaNbt.getList("messages", NbtType.STRING);
if (!messages.isEmpty()) {
var it = messages.iterator();
while (it.hasNext()) {
String signLine = (String) it.next().getValue();
String signLine = it.next();
signLine = MessageTranslator.convertMessageLenient(signLine);
// Check the character width on the sign to ensure there is no overflow that is usually hidden
@ -133,13 +130,13 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator {
builder.putString("Text", signText.toString());
// Java Edition 1.14 added the ability to change the text color of the whole sign using dye
Tag color = signData.get("color");
String color = javaNbt.getString("color", null);
if (color != null) {
builder.putInt("SignTextColor", getBedrockSignColor(color.getValue().toString()));
builder.putInt("SignTextColor", getBedrockSignColor(color));
}
// Glowing text
boolean isGlowing = getOrDefault(signData.get("has_glowing_text"), (byte) 0) != (byte) 0;
boolean isGlowing = javaNbt.getBoolean("has_glowing_text");
builder.putBoolean("IgnoreLighting", isGlowing);
return builder.build();
}

View file

@ -25,24 +25,22 @@
package org.geysermc.geyser.translator.level.block.entity;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.geyser.GeyserImpl;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.SkullCache;
import org.geysermc.geyser.skin.SkinProvider;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@ -52,56 +50,60 @@ import java.util.concurrent.ExecutionException;
public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
byte skullVariant = BlockStateValues.getSkullVariant(blockState);
float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f;
// Just in case...
if (skullVariant == -1) {
skullVariant = 0;
}
builder.put("Rotation", rotation);
builder.put("SkullType", skullVariant);
bedrockNbt.putFloat("Rotation", rotation);
bedrockNbt.putByte("SkullType", skullVariant);
if (BlockStateValues.isSkullPowered(blockState)) {
builder.putBoolean("MouthMoving", true);
bedrockNbt.putBoolean("MouthMoving", true);
}
}
private static UUID getUUID(CompoundTag profile) {
if (profile.get("id") instanceof IntArrayTag uuidTag && uuidTag.length() == 4) {
int[] uuidAsArray = uuidTag.getValue();
private static UUID getUUID(NbtMap profile) {
int[] uuidAsArray = profile.getIntArray("id");
if (uuidAsArray.length == 4) {
// thank u viaversion
return new UUID((long) uuidAsArray[0] << 32 | ((long) uuidAsArray[1] & 0xFFFFFFFFL),
(long) uuidAsArray[2] << 32 | ((long) uuidAsArray[3] & 0xFFFFFFFFL));
}
// Convert username to an offline UUID
String username = null;
if (profile.get("name") instanceof StringTag nameTag) {
username = nameTag.getValue().toLowerCase(Locale.ROOT);
String nameTag = profile.getString("name", null);
if (nameTag != null) {
username = nameTag.toLowerCase(Locale.ROOT);
}
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
}
private static CompletableFuture<String> getTextures(CompoundTag profile, UUID uuid) {
ListTag properties = profile.get("properties");
if (properties == null) {
private static CompletableFuture<@Nullable String> getTextures(NbtMap profile, UUID uuid) {
List<NbtMap> properties = profile.getList("properties", NbtType.COMPOUND);
if (properties.isEmpty()) {
if (uuid != null && uuid.version() == 4) {
String uuidString = uuid.toString().replace("-", "");
return SkinProvider.requestTexturesFromUUID(uuidString);
} else if (profile.get("name") instanceof StringTag nameTag) {
// Fall back to username if UUID was missing or was an offline mode UUID
return SkinProvider.requestTexturesFromUsername(nameTag.getValue());
} else {
String nameTag = profile.getString("name", null);
if (nameTag != null) {
// Fall back to username if UUID was missing or was an offline mode UUID
return SkinProvider.requestTexturesFromUsername(nameTag);
}
}
return CompletableFuture.completedFuture(null);
}
LinkedHashMap<?,?> tag1 = (LinkedHashMap<?,?>) properties.get(0).getValue();
StringTag texture = (StringTag) tag1.get("value");
return CompletableFuture.completedFuture(texture.getValue());
NbtMap tag1 = properties.get(0);
String texture = tag1.getString("value", null);
return CompletableFuture.completedFuture(texture);
}
public static @Nullable BlockDefinition translateSkull(GeyserSession session, CompoundTag tag, Vector3i blockPosition, int blockState) {
CompoundTag profile = tag.get("profile");
if (profile == null) {
public static @Nullable BlockDefinition translateSkull(GeyserSession session, NbtMap javaNbt, Vector3i blockPosition, int blockState) {
NbtMap profile = javaNbt.getCompound("profile");
if (profile.isEmpty()) {
session.getSkullCache().removeSkull(blockPosition);
return null;
}
@ -112,13 +114,13 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
try {
String texture = texturesFuture.get();
if (texture == null) {
session.getGeyser().getLogger().debug("Custom skull with invalid profile tag: " + blockPosition + " " + tag);
session.getGeyser().getLogger().debug("Custom skull with invalid profile tag: " + blockPosition + " " + javaNbt);
return null;
}
SkullCache.Skull skull = session.getSkullCache().putSkull(blockPosition, uuid, texture, blockState);
return skull.getBlockDefinition();
} catch (InterruptedException | ExecutionException e) {
session.getGeyser().getLogger().debug("Failed to acquire textures for custom skull: " + blockPosition + " " + tag);
session.getGeyser().getLogger().debug("Failed to acquire textures for custom skull: " + blockPosition + " " + javaNbt);
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
e.printStackTrace();
}
@ -129,7 +131,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
// profile contained a username, so we have to wait for it to be retrieved
texturesFuture.whenComplete((texturesProperty, throwable) -> {
if (texturesProperty == null) {
session.getGeyser().getLogger().debug("Custom skull with invalid profile tag: " + blockPosition + " " + tag);
session.getGeyser().getLogger().debug("Custom skull with invalid profile tag: " + blockPosition + " " + javaNbt);
return;
}
if (session.getEventLoop().inEventLoop()) {

View file

@ -25,10 +25,6 @@
package org.geysermc.geyser.translator.level.block.entity;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i;
@ -38,17 +34,18 @@ import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
@BlockEntity(type = BlockEntityType.MOB_SPAWNER)
public class SpawnerBlockEntityTranslator extends BlockEntityTranslator {
@Override
public NbtMap getBlockEntityTag(GeyserSession session, BlockEntityType type, int x, int y, int z, CompoundTag tag, int blockState) {
public NbtMap getBlockEntityTag(GeyserSession session, BlockEntityType type, int x, int y, int z, NbtMap javaNbt, int blockState) {
// Sending an empty EntityIdentifier to empty the spawner is ignored by the client, so we send a whole new spawner!
// Fixes https://github.com/GeyserMC/Geyser/issues/4214
CompoundTag spawnData = tag.get("SpawnData");
NbtMap spawnData = javaNbt.getCompound("SpawnData");
if (spawnData != null) {
CompoundTag entityTag = spawnData.get("entity");
NbtMap entityTag = spawnData.getCompound("entity");
if (entityTag.isEmpty()) {
Vector3i position = Vector3i.from(x, y, z);
// Set to air and back to reset the spawner - "just" updating the spawner doesn't work
@ -66,62 +63,63 @@ public class SpawnerBlockEntityTranslator extends BlockEntityTranslator {
}
}
return super.getBlockEntityTag(session, type, x, y, z, tag, blockState);
return super.getBlockEntityTag(session, type, x, y, z, javaNbt, blockState);
}
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
Tag current;
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
Object current;
if ((current = tag.get("MaxNearbyEntities")) != null) {
builder.put("MaxNearbyEntities", current.getValue());
// TODO use primitive get and put methods
if ((current = javaNbt.get("MaxNearbyEntities")) != null) {
bedrockNbt.put("MaxNearbyEntities", current);
}
if ((current = tag.get("RequiredPlayerRange")) != null) {
builder.put("RequiredPlayerRange", current.getValue());
if ((current = javaNbt.get("RequiredPlayerRange")) != null) {
bedrockNbt.put("RequiredPlayerRange", current);
}
if ((current = tag.get("SpawnCount")) != null) {
builder.put("SpawnCount", current.getValue());
if ((current = javaNbt.get("SpawnCount")) != null) {
bedrockNbt.put("SpawnCount", current);
}
if ((current = tag.get("MaxSpawnDelay")) != null) {
builder.put("MaxSpawnDelay", current.getValue());
if ((current = javaNbt.get("MaxSpawnDelay")) != null) {
bedrockNbt.put("MaxSpawnDelay", current);
}
if ((current = tag.get("Delay")) != null) {
builder.put("Delay", current.getValue());
if ((current = javaNbt.get("Delay")) != null) {
bedrockNbt.put("Delay", current);
}
if ((current = tag.get("SpawnRange")) != null) {
builder.put("SpawnRange", current.getValue());
if ((current = javaNbt.get("SpawnRange")) != null) {
bedrockNbt.put("SpawnRange", current);
}
if ((current = tag.get("MinSpawnDelay")) != null) {
builder.put("MinSpawnDelay", current.getValue());
if ((current = javaNbt.get("MinSpawnDelay")) != null) {
bedrockNbt.put("MinSpawnDelay", current);
}
translateSpawnData(builder, tag.get("SpawnData"));
translateSpawnData(bedrockNbt, javaNbt.getCompound("SpawnData", null));
builder.put("isMovable", (byte) 1);
bedrockNbt.put("isMovable", (byte) 1);
}
static void translateSpawnData(@NonNull NbtMapBuilder builder, @Nullable CompoundTag spawnData) {
static void translateSpawnData(@NonNull NbtMapBuilder builder, @Nullable NbtMap spawnData) {
if (spawnData == null) {
return;
}
CompoundTag entityTag = spawnData.get("entity");
if (entityTag.get("id") instanceof StringTag idTag) {
NbtMap entityTag = spawnData.getCompound("entity");
String entityId = entityTag.getString("id");
if (entityId != null) {
// As of 1.19.3, spawners can be empty
String entityId = idTag.getValue();
builder.put("EntityIdentifier", entityId);
EntityDefinition<?> definition = Registries.JAVA_ENTITY_IDENTIFIERS.get(entityId);
if (definition != null) {
builder.put("DisplayEntityWidth", definition.width());
builder.put("DisplayEntityHeight", definition.height());
builder.put("DisplayEntityScale", 1.0f);
builder.putFloat("DisplayEntityWidth", definition.width());
builder.putFloat("DisplayEntityHeight", definition.height());
builder.putFloat("DisplayEntityScale", 1.0f);
}
}
}

View file

@ -25,8 +25,6 @@
package org.geysermc.geyser.translator.level.block.entity;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
@ -35,22 +33,23 @@ import org.cloudburstmc.protocol.bedrock.data.structure.StructureRotation;
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.StructureBlockUtils;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
@BlockEntity(type = BlockEntityType.STRUCTURE_BLOCK)
public class StructureBlockBlockEntityTranslator extends BlockEntityTranslator {
@Override
public NbtMap getBlockEntityTag(GeyserSession session, BlockEntityType type, int x, int y, int z, CompoundTag tag, int blockState) {
public NbtMap getBlockEntityTag(GeyserSession session, BlockEntityType type, int x, int y, int z, NbtMap javaNbt, int blockState) {
// Sending a structure with size 0 doesn't clear the outline. Hence, we have to force it by replacing the block :/
int xStructureSize = getOrDefault(tag.get("sizeX"), 0);
int yStructureSize = getOrDefault(tag.get("sizeY"), 0);
int zStructureSize = getOrDefault(tag.get("sizeZ"), 0);
int xStructureSize = javaNbt.getInt("sizeX");
int yStructureSize = javaNbt.getInt("sizeY");
int zStructureSize = javaNbt.getInt("sizeZ");
Vector3i size = Vector3i.from(xStructureSize, yStructureSize, zStructureSize);
if (size.equals(Vector3i.ZERO)) {
Vector3i position = Vector3i.from(x, y, z);
String mode = getOrDefault(tag.get("mode"), "");
String mode = javaNbt.getString("mode");
// Set to air and back to reset the structure block
UpdateBlockPacket emptyBlockPacket = new UpdateBlockPacket();
@ -66,18 +65,18 @@ public class StructureBlockBlockEntityTranslator extends BlockEntityTranslator {
session.sendUpstreamPacket(spawnerBlockPacket);
}
return super.getBlockEntityTag(session, type, x, y, z, tag, blockState);
return super.getBlockEntityTag(session, type, x, y, z, javaNbt, blockState);
}
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
if (tag.size() < 5) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
if (javaNbt.size() < 5) {
return; // These values aren't here
}
builder.putString("structureName", getOrDefault(tag.get("name"), ""));
bedrockNbt.putString("structureName", javaNbt.getString("name"));
String mode = getOrDefault(tag.get("mode"), "");
String mode = javaNbt.getString("mode");
int bedrockData = switch (mode) {
case "LOAD" -> 2;
case "CORNER" -> 3;
@ -85,53 +84,53 @@ public class StructureBlockBlockEntityTranslator extends BlockEntityTranslator {
default -> 1; // SAVE
};
builder.putInt("data", bedrockData);
builder.putString("dataField", ""); // ??? possibly related to Java's "metadata"
bedrockNbt.putInt("data", bedrockData);
bedrockNbt.putString("dataField", ""); // ??? possibly related to Java's "metadata"
// Mirror behaves different in Java and Bedrock - it requires modifying the position in space as well
String mirror = getOrDefault(tag.get("mirror"), "");
String mirror = javaNbt.getString("mirror");
StructureMirror bedrockMirror = switch (mirror) {
case "FRONT_BACK" -> StructureMirror.X;
case "LEFT_RIGHT" -> StructureMirror.Z;
default -> StructureMirror.NONE;
};
builder.putByte("mirror", (byte) bedrockMirror.ordinal());
bedrockNbt.putByte("mirror", (byte) bedrockMirror.ordinal());
builder.putByte("ignoreEntities", getOrDefault(tag.get("ignoreEntities"), (byte) 0));
builder.putByte("isPowered", getOrDefault(tag.get("powered"), (byte) 0));
builder.putLong("seed", getOrDefault(tag.get("seed"), 0L));
builder.putByte("showBoundingBox", getOrDefault(tag.get("showboundingbox"), (byte) 0));
bedrockNbt.putByte("ignoreEntities", javaNbt.getByte("ignoreEntities"));
bedrockNbt.putByte("isPowered", javaNbt.getByte("powered"));
bedrockNbt.putLong("seed", javaNbt.getLong("seed"));
bedrockNbt.putByte("showBoundingBox", javaNbt.getByte("showboundingbox"));
String rotation = getOrDefault(tag.get("rotation"), "");
String rotation = javaNbt.getString("rotation");
StructureRotation bedrockRotation = switch (rotation) {
case "CLOCKWISE_90" -> StructureRotation.ROTATE_90;
case "CLOCKWISE_180" -> StructureRotation.ROTATE_180;
case "COUNTERCLOCKWISE_90" -> StructureRotation.ROTATE_270;
default -> StructureRotation.NONE;
};
builder.putByte("rotation", (byte) bedrockRotation.ordinal());
bedrockNbt.putByte("rotation", (byte) bedrockRotation.ordinal());
int xStructureSize = getOrDefault(tag.get("sizeX"), 0);
int yStructureSize = getOrDefault(tag.get("sizeY"), 0);
int zStructureSize = getOrDefault(tag.get("sizeZ"), 0);
int xStructureSize = javaNbt.getInt("sizeX");
int yStructureSize = javaNbt.getInt("sizeY");
int zStructureSize = javaNbt.getInt("sizeZ");
// The "positions" are also offsets on Java
int posX = getOrDefault(tag.get("posX"), 0);
int posY = getOrDefault(tag.get("posY"), 0);
int posZ = getOrDefault(tag.get("posZ"), 0);
int posX = javaNbt.getInt("posX");
int posY = javaNbt.getInt("posY");
int posZ = javaNbt.getInt("posZ");
Vector3i offset = StructureBlockUtils.calculateOffset(bedrockRotation, bedrockMirror,
xStructureSize, zStructureSize);
builder.putInt("xStructureOffset", posX + offset.getX());
builder.putInt("yStructureOffset", posY);
builder.putInt("zStructureOffset", posZ + offset.getZ());
bedrockNbt.putInt("xStructureOffset", posX + offset.getX());
bedrockNbt.putInt("yStructureOffset", posY);
bedrockNbt.putInt("zStructureOffset", posZ + offset.getZ());
builder.putInt("xStructureSize", xStructureSize);
builder.putInt("yStructureSize", yStructureSize);
builder.putInt("zStructureSize", zStructureSize);
bedrockNbt.putInt("xStructureSize", xStructureSize);
bedrockNbt.putInt("yStructureSize", yStructureSize);
bedrockNbt.putInt("zStructureSize", zStructureSize);
builder.putFloat("integrity", getOrDefault(tag.get("integrity"), 0f)); // Is 1.0f by default on Java but 100.0f on Bedrock
bedrockNbt.putFloat("integrity", javaNbt.getFloat("integrity")); // Is 1.0f by default on Java but 100.0f on Bedrock
// Java's "showair" is unrepresented
}

View file

@ -25,23 +25,24 @@
package org.geysermc.geyser.translator.level.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
@BlockEntity(type = BlockEntityType.TRIAL_SPAWNER)
public class TrialSpawnerBlockEntityTranslator extends BlockEntityTranslator {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
if (tag == null) {
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, int blockState) {
if (javaNbt == null) {
return;
}
// trial spawners have "spawn_data" instead of "SpawnData"
SpawnerBlockEntityTranslator.translateSpawnData(builder, tag.get("spawn_data"));
SpawnerBlockEntityTranslator.translateSpawnData(bedrockNbt, javaNbt.getCompound("spawn_data", null));
// Because trial spawners don't exist on bedrock yet
builder.put("id", "MobSpawner");
bedrockNbt.put("id", "MobSpawner");
}
}

View file

@ -25,9 +25,8 @@
package org.geysermc.geyser.translator.protocol.java.level;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.cloudburstmc.protocol.bedrock.data.structure.StructureMirror;
@ -71,7 +70,7 @@ public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundB
packet.getNbt(), blockState), packet.getPosition());
// Check for custom skulls.
boolean hasCustomHeadBlock = false;
if (session.getPreferencesCache().showCustomSkulls() && packet.getNbt() != null && packet.getNbt().contains("profile")) {
if (session.getPreferencesCache().showCustomSkulls() && packet.getNbt() != null && packet.getNbt().containsKey("profile")) {
BlockDefinition blockDefinition = SkullBlockEntityTranslator.translateSkull(session, packet.getNbt(), position, blockState);
if (blockDefinition != null) {
hasCustomHeadBlock = true;
@ -107,21 +106,21 @@ public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundB
&& packet.getPosition().equals(session.getStructureBlockCache().getCurrentStructureBlock())
&& packet.getNbt() != null && packet.getNbt().size() > 5
) {
CompoundTag map = packet.getNbt();
NbtMap map = packet.getNbt();
String mode = getOrDefault(map.get("mode"), "");
String mode = map.getString("mode");
if (!mode.equalsIgnoreCase("LOAD")) {
return;
}
String mirror = getOrDefault(map.get("mirror"), "");
String mirror = map.getString("mirror");
StructureMirror bedrockMirror = switch (mirror) {
case "FRONT_BACK" -> StructureMirror.X;
case "LEFT_RIGHT" -> StructureMirror.Z;
default -> StructureMirror.NONE;
};
String rotation = getOrDefault(map.get("rotation"), "");
String rotation = map.getString("rotation");
StructureRotation bedrockRotation = switch (rotation) {
case "CLOCKWISE_90" -> StructureRotation.ROTATE_90;
case "CLOCKWISE_180" -> StructureRotation.ROTATE_180;
@ -129,10 +128,10 @@ public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundB
default -> StructureRotation.NONE;
};
String name = getOrDefault(map.get("name"), "");
int sizeX = getOrDefault(map.get("sizeX"), 0);
int sizeY = getOrDefault(map.get("sizeY"), 0);
int sizeZ = getOrDefault(map.get("sizeZ"), 0);
String name = map.getString("name");
int sizeX = map.getInt("sizeX");
int sizeY = map.getInt("sizeY");
int sizeZ = map.getInt("sizeZ");
session.getStructureBlockCache().setCurrentStructureBlock(null);
@ -149,10 +148,4 @@ public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundB
StructureBlockUtils.sendStructureData(session, size, name);
}
}
protected <T> T getOrDefault(Tag tag, T defaultValue) {
//noinspection unchecked
return (tag != null && tag.getValue() != null) ? (T) tag.getValue() : defaultValue;
}
}

View file

@ -25,7 +25,6 @@
package org.geysermc.geyser.translator.protocol.java.level;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufOutputStream;
@ -385,7 +384,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
final int chunkBlockZ = packet.getZ() << 4;
for (BlockEntityInfo blockEntity : blockEntities) {
BlockEntityType type = blockEntity.getType();
CompoundTag tag = blockEntity.getNbt();
NbtMap tag = blockEntity.getNbt();
if (type == null) {
// As an example: ViaVersion will send -1 if it cannot find the block entity type
// Vanilla Minecraft gracefully handles this
@ -422,7 +421,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
}
// Check for custom skulls
if (session.getPreferencesCache().showCustomSkulls() && type == BlockEntityType.SKULL && tag != null && tag.contains("profile")) {
if (session.getPreferencesCache().showCustomSkulls() && type == BlockEntityType.SKULL && tag != null && tag.containsKey("profile")) {
BlockDefinition blockDefinition = SkullBlockEntityTranslator.translateSkull(session, tag, Vector3i.from(x + chunkBlockX, y, z + chunkBlockZ), blockState);
if (blockDefinition != null) {
int bedrockSectionY = (y >> 4) - (bedrockDimension.minY() >> 4);

View file

@ -25,16 +25,12 @@
package org.geysermc.geyser.translator.sound;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
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 org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import java.util.Map;
@ -104,25 +100,14 @@ public interface BlockSoundInteractionTranslator extends SoundInteractionTransla
return true;
}
CompoundTag tag = itemInHand.getNbt();
if (tag == null) {
// No CanPlaceOn tag can exist
return false;
}
ListTag canPlaceOn = tag.get("CanPlaceOn");
if (canPlaceOn == null || canPlaceOn.size() == 0) {
return false;
var canPlaceOn = itemInHand.getComponent(DataComponentType.CAN_PLACE_ON);
if (canPlaceOn == null || canPlaceOn.getPredicates().isEmpty()) {
// Component doesn't exist - no restrictions apply.
return true;
}
String cleanIdentifier = BlockUtils.getCleanIdentifier(blockIdentifier);
for (Tag t : canPlaceOn) {
if (t instanceof StringTag stringTag) {
if (cleanIdentifier.equals(stringTag.getValue())) {
// This operation would/could be a success!
return true;
}
}
for (var blockPredicate : canPlaceOn.getPredicates()) {
// I don't want to deal with this right now. TODO
}
// The block in world is not present in the CanPlaceOn tag on the item

View file

@ -187,14 +187,28 @@ public class MessageTranslator {
}
}
/**
* Convenience method for locale getting.
*/
public static String convertJsonMessage(GeyserSession session, String message) {
return convertJsonMessage(message, session.locale());
}
public static String convertJsonMessage(String message, String locale) {
return convertMessage(GSON_SERIALIZER.deserialize(message), locale);
}
public static String convertJsonMessage(String message) {
return convertJsonMessage(message, GeyserLocale.getDefaultLocale());
/**
* Convenience method for locale getting.
*/
public static String convertMessage(GeyserSession session, Component message) {
return convertMessage(message, session.locale());
}
/**
* DO NOT USE THIS METHOD unless where you're calling from does not have a (reliable) way of getting the
* context's locale.
*/
public static String convertMessage(Component message) {
return convertMessage(message, GeyserLocale.getDefaultLocale());
}

View file

@ -15,7 +15,7 @@ protocol-connection = "3.0.0.Beta1-20240411.165033-128"
raknet = "1.0.0.CR3-20240416.144209-1"
blockstateupdater="1.20.80-20240411.142413-1"
mcauthlib = "d9d773e"
mcprotocollib = "1ca8808" # Revert from jitpack after release
mcprotocollib = "98410a1" # Revert from jitpack after release
adventure = "4.14.0"
adventure-platform = "4.3.0"
junit = "5.9.2"