diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSet.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSet.java index 59f09922a..b9796f734 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSet.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSet.java @@ -24,6 +24,9 @@ package com.viaversion.viaversion.api.minecraft; import com.viaversion.viaversion.util.EitherImpl; +/** + * Set of ids that either holds a string tag key or an array of ids. + */ public final class HolderSet extends EitherImpl { public HolderSet(final String tagKey) { diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java index ea34f2cd9..e3b98bfdb 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java @@ -65,7 +65,7 @@ public final class StructuredDataKey { public static final StructuredDataKey CREATIVE_SLOT_LOCK = new StructuredDataKey<>("creative_slot_lock", Type.EMPTY); public static final StructuredDataKey ENCHANTMENT_GLINT_OVERRIDE = new StructuredDataKey<>("enchantment_glint_override", Type.BOOLEAN); public static final StructuredDataKey INTANGIBLE_PROJECTILE = new StructuredDataKey<>("intangible_projectile", Type.EMPTY); - public static final StructuredDataKey STORED_ENCHANTMENTS = new StructuredDataKey<>("storded_enchantments", Enchantments.TYPE); + public static final StructuredDataKey STORED_ENCHANTMENTS = new StructuredDataKey<>("stored_enchantments", Enchantments.TYPE); public static final StructuredDataKey DYED_COLOR = new StructuredDataKey<>("dyed_color", DyedColor.TYPE); public static final StructuredDataKey MAP_COLOR = new StructuredDataKey<>("map_color", Type.INT); public static final StructuredDataKey MAP_ID = new StructuredDataKey<>("map_id", Type.VAR_INT); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/data/MappingData.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/data/MappingData.java index 818ff8ba4..ad52a4a80 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/data/MappingData.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/data/MappingData.java @@ -18,19 +18,16 @@ package com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.data; 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.viaversion.viaversion.api.data.MappingDataBase; import com.viaversion.viaversion.api.data.MappingDataLoader; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import java.util.ArrayList; -import java.util.List; +import com.viaversion.viaversion.util.KeyMappings; +import org.checkerframework.checker.nullness.qual.Nullable; public class MappingData extends MappingDataBase { - private final Object2IntMap byId = new Object2IntOpenHashMap<>(); - private final List itemIds = new ArrayList<>(); + private KeyMappings items; + private KeyMappings blocks; public MappingData() { super("1.20.3", "1.20.5"); @@ -40,20 +37,24 @@ public class MappingData extends MappingDataBase { protected void loadExtras(final CompoundTag data) { super.loadExtras(data); - final ListTag items = MappingDataLoader.loadNBT("itemIds-1.20.3.nbt").getListTag("items", StringTag.class); - for (int i = 0; i < items.size(); i++) { - final StringTag tag = items.get(i); - itemIds.add(tag.getValue()); - byId.put(tag.getValue(), i); - } - byId.defaultReturnValue(-1); + final CompoundTag extraMappings = MappingDataLoader.loadNBT("items-blocks-1.20.3.nbt"); + items = new KeyMappings(extraMappings.getListTag("items", StringTag.class)); + blocks = new KeyMappings(extraMappings.getListTag("blocks", StringTag.class)); } public int itemId(final String name) { - return byId.getInt(name); + return items.keyToId(name); } - public String itemName(final int id) { - return itemIds.get(id); + public @Nullable String itemName(final int id) { + return items.idToKey(id); + } + + public int blockId(final String name) { + return blocks.keyToId(name); + } + + public @Nullable String blockName(final int id) { + return blocks.idToKey(id); } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java index 6d9c8af1e..f9d2cd4e2 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java @@ -17,16 +17,19 @@ */ package com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.rewriter; +import com.github.steveice10.opennbt.stringified.SNBT; 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.NumberTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.data.ParticleMappings; import com.viaversion.viaversion.api.minecraft.GameProfile; import com.viaversion.viaversion.api.minecraft.GlobalPosition; import com.viaversion.viaversion.api.minecraft.Holder; +import com.viaversion.viaversion.api.minecraft.HolderSet; import com.viaversion.viaversion.api.minecraft.Particle; import com.viaversion.viaversion.api.minecraft.data.StructuredData; import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer; @@ -34,6 +37,7 @@ import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey; import com.viaversion.viaversion.api.minecraft.item.DataItem; import com.viaversion.viaversion.api.minecraft.item.Item; import com.viaversion.viaversion.api.minecraft.item.StructuredItem; +import com.viaversion.viaversion.api.minecraft.item.data.AdventureModePredicate; import com.viaversion.viaversion.api.minecraft.item.data.ArmorTrim; import com.viaversion.viaversion.api.minecraft.item.data.ArmorTrimMaterial; import com.viaversion.viaversion.api.minecraft.item.data.ArmorTrimPattern; @@ -41,6 +45,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.AttributeModifier; import com.viaversion.viaversion.api.minecraft.item.data.AttributeModifiers; import com.viaversion.viaversion.api.minecraft.item.data.BannerPatternLayer; import com.viaversion.viaversion.api.minecraft.item.data.Bee; +import com.viaversion.viaversion.api.minecraft.item.data.BlockPredicate; import com.viaversion.viaversion.api.minecraft.item.data.BlockStateProperties; import com.viaversion.viaversion.api.minecraft.item.data.DyedColor; import com.viaversion.viaversion.api.minecraft.item.data.Enchantments; @@ -53,6 +58,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.ModifierData; import com.viaversion.viaversion.api.minecraft.item.data.PotionContents; import com.viaversion.viaversion.api.minecraft.item.data.PotionEffect; import com.viaversion.viaversion.api.minecraft.item.data.PotionEffectData; +import com.viaversion.viaversion.api.minecraft.item.data.StatePropertyMatcher; import com.viaversion.viaversion.api.minecraft.item.data.SuspiciousStewEffect; import com.viaversion.viaversion.api.minecraft.item.data.Unbreakable; import com.viaversion.viaversion.api.minecraft.item.data.WrittenBook; @@ -79,6 +85,7 @@ import com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.packet.Serverb import com.viaversion.viaversion.rewriter.BlockRewriter; import com.viaversion.viaversion.rewriter.ItemRewriter; import com.viaversion.viaversion.util.ComponentUtil; +import com.viaversion.viaversion.util.Either; import com.viaversion.viaversion.util.Key; import com.viaversion.viaversion.util.UUIDUtil; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; @@ -88,12 +95,14 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; +import java.util.logging.Level; import org.checkerframework.checker.nullness.qual.Nullable; public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter { public static final String[] MOB_TAGS = {"NoAI", "Silent", "NoGravity", "Glowing", "Invulnerable", "Health", "Age", "Variant", "HuntingCooldown", "BucketVariantTag"}; private static final GameProfile.Property[] EMPTY_PROPERTIES = new GameProfile.Property[0]; + private static final StatePropertyMatcher[] EMPTY_PROPERTY_MATCHERS = new StatePropertyMatcher[0]; public BlockItemPacketRewriter1_20_5(final Protocol1_20_5To1_20_3 protocol) { super(protocol, Type.ITEM1_20_2, Type.ITEM1_20_2_ARRAY, Types1_20_5.ITEM, Types1_20_5.ITEM_ARRAY); @@ -392,8 +401,6 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter canPlaceOnTag = tag.getListTag("CanPlaceOn", StringTag.class); + if (canPlaceOnTag != null) { + data.set(StructuredDataKey.CAN_PLACE_ON, updateBlockPredicates(canPlaceOnTag, (hideFlagsValue & StructuredDataConverter.HIDE_CAN_PLACE_ON) == 0)); + } + + final ListTag canDestroyTag = tag.getListTag("CanDestroy", StringTag.class); + if (canDestroyTag != null) { + data.set(StructuredDataKey.CAN_BREAK, updateBlockPredicates(canDestroyTag, (hideFlagsValue & StructuredDataConverter.HIDE_CAN_DESTROY) == 0)); + } + + // TODO MAP_POST_PROCESSING data.set(StructuredDataKey.CUSTOM_DATA, tag); return item; } + private AdventureModePredicate updateBlockPredicates(final ListTag tag, final boolean showInTooltip) { + final BlockPredicate[] predicates = tag.stream() + .map(StringTag::getValue) + .map(this::deserializeBlockPredicate) + .filter(Objects::nonNull) + .toArray(BlockPredicate[]::new); + return new AdventureModePredicate(predicates, showInTooltip); + } + + private @Nullable BlockPredicate deserializeBlockPredicate(final String rawPredicate) { + final int propertiesStartIndex = rawPredicate.indexOf('['); + final int tagStartIndex = rawPredicate.indexOf('{'); + int idLength = rawPredicate.length(); + if (propertiesStartIndex != -1) { + idLength = propertiesStartIndex; + } + if (tagStartIndex != -1) { + idLength = Math.min(propertiesStartIndex, tagStartIndex); + } + + final String identifier = rawPredicate.substring(0, idLength); + final int id = Protocol1_20_5To1_20_3.MAPPINGS.blockId(identifier); + if (id == -1) { + return null; + } + + final int propertiesEndIndex = rawPredicate.indexOf(']'); + final List propertyMatchers = new ArrayList<>(); + if (propertiesStartIndex != -1 && propertiesEndIndex != -1) { + for (final String property : rawPredicate.substring(propertiesStartIndex + 1, propertiesEndIndex).split(",")) { + final int propertySplitIndex = property.indexOf('='); + if (propertySplitIndex == -1) { + continue; + } + + final String propertyId = property.substring(0, propertySplitIndex).trim(); + final String propertyValue = property.substring(propertySplitIndex + 1).trim(); + propertyMatchers.add(new StatePropertyMatcher(propertyId, Either.left(propertyValue))); // TODO Also handle ranged matchers + } + } + + final int tagEndIndex = rawPredicate.indexOf('}'); + CompoundTag tag = null; + if (tagStartIndex != -1 && tagEndIndex != -1) { + try { + tag = SNBT.deserializeCompoundTag(rawPredicate.substring(tagStartIndex, tagEndIndex + 1)); + } catch (final Exception e) { + if (Via.getManager().isDebug()) { + Via.getPlatform().getLogger().log(Level.SEVERE, "Failed to parse block predicate tag: " + rawPredicate.substring(tagStartIndex, tagEndIndex + 1), e); + } + } + } + + return new BlockPredicate( + new HolderSet(new int[]{Protocol1_20_5To1_20_3.MAPPINGS.getNewBlockId(id)}), + propertyMatchers.isEmpty() ? null : propertyMatchers.toArray(EMPTY_PROPERTY_MATCHERS), + tag + ); + } + private void updateAttributes(final StructuredDataContainer data, final ListTag attributeModifiersTag, final boolean showInTooltip) { final AttributeModifier[] modifiers = attributeModifiersTag.stream().map(modifierTag -> { final String attributeName = modifierTag.getString("AttributeName"); @@ -676,7 +750,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter keys) { + this(keys.toArray(new String[0])); + } + + public KeyMappings(final ListTag keys) { + this(keys.getValue().stream().map(StringTag::getValue).toArray(String[]::new)); + } + public @Nullable String idToKey(final int id) { if (id < 0 || id >= keys.length) { return null; diff --git a/common/src/main/resources/assets/viaversion/data/items-blocks-1.20.3.nbt b/common/src/main/resources/assets/viaversion/data/items-blocks-1.20.3.nbt new file mode 100644 index 000000000..4d60cfd51 Binary files /dev/null and b/common/src/main/resources/assets/viaversion/data/items-blocks-1.20.3.nbt differ