From f7dad9b5d70798b168ef0c94169c54d40de9ea6a Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Wed, 3 Apr 2024 21:09:52 +0200 Subject: [PATCH] Add StructuredEnchantmentRewriter, update translation mappings --- .editorconfig | 5 +- .../api/data/BackwardsMappings.java | 56 ++++-- .../rewriters/BackwardsItemRewriterBase.java | 60 ++++-- .../BackwardsStructuredItemRewriter.java | 10 + .../api/rewriters/EnchantmentRewriter.java | 9 +- .../StructuredEnchantmentRewriter.java | 189 ++++++++++++++++++ .../BlockItemPacketRewriter1_20_5.java | 3 + .../data/mappings-1.20.5to1.20.3.nbt | Bin 9488 -> 9552 bytes .../data/translation-mappings.json | 171 +++++++++++++++- 9 files changed, 465 insertions(+), 38 deletions(-) create mode 100644 common/src/main/java/com/viaversion/viabackwards/api/rewriters/StructuredEnchantmentRewriter.java diff --git a/.editorconfig b/.editorconfig index 7730dd6b..85b4636b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,4 +10,7 @@ ij_java_class_count_to_use_import_on_demand = 999999 ij_java_names_count_to_use_import_on_demand = 999999 ij_java_imports_layout = *,|,$* ij_java_generate_final_locals = true -ij_java_generate_final_parameters = true \ No newline at end of file +ij_java_generate_final_parameters = true + +[*.json] +indent_size = 2 \ No newline at end of file diff --git a/common/src/main/java/com/viaversion/viabackwards/api/data/BackwardsMappings.java b/common/src/main/java/com/viaversion/viabackwards/api/data/BackwardsMappings.java index 5ecd95fe..cf8405d4 100644 --- a/common/src/main/java/com/viaversion/viabackwards/api/data/BackwardsMappings.java +++ b/common/src/main/java/com/viaversion/viabackwards/api/data/BackwardsMappings.java @@ -26,6 +26,7 @@ import com.viaversion.viaversion.api.data.MappingData; import com.viaversion.viaversion.api.data.MappingDataBase; import com.viaversion.viaversion.api.data.Mappings; import com.viaversion.viaversion.api.protocol.Protocol; +import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectArrayMap; import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap; import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap; import com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag; @@ -35,6 +36,7 @@ import com.viaversion.viaversion.libs.opennbt.tag.builtin.Tag; import com.viaversion.viaversion.util.Key; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; @@ -44,6 +46,7 @@ public class BackwardsMappings extends MappingDataBase { protected Int2ObjectMap backwardsItemMappings; private Map backwardsSoundMappings; private Map entityNames; + private Int2ObjectMap enchantmentNames; public BackwardsMappings(final String unmappedVersion, final String mappedVersion) { this(unmappedVersion, mappedVersion, null); @@ -77,23 +80,37 @@ public class BackwardsMappings extends MappingDataBase { } } - final CompoundTag entityNames = data.getCompoundTag("entitynames"); - if (entityNames != null) { - this.entityNames = new HashMap<>(entityNames.size()); - for (final Map.Entry entry : entityNames.entrySet()) { - final StringTag mappedTag = (StringTag) entry.getValue(); - this.entityNames.put(entry.getKey(), mappedTag.getValue()); - } + this.entityNames = loadNameByStringMappings(data, "entitynames"); + this.enchantmentNames = loadNameByIdMappings(data, "enchantmentnames"); + this.backwardsSoundMappings = loadNameByStringMappings(data, "soundnames"); + } + + private @Nullable Map loadNameByStringMappings(final CompoundTag data, final String key) { + final CompoundTag nameMappings = data.getCompoundTag(key); + if (nameMappings == null) { + return null; } - final CompoundTag soundNames = data.getCompoundTag("soundnames"); - if (soundNames != null) { - backwardsSoundMappings = new HashMap<>(soundNames.size()); - for (final Map.Entry entry : soundNames.entrySet()) { - final StringTag mappedTag = (StringTag) entry.getValue(); - backwardsSoundMappings.put(entry.getKey(), mappedTag.getValue()); - } + final Map map = new HashMap<>(nameMappings.size()); + for (final Map.Entry entry : nameMappings.entrySet()) { + final StringTag mappedTag = (StringTag) entry.getValue(); + map.put(entry.getKey(), mappedTag.getValue()); } + return map; + } + + private @Nullable Int2ObjectMap loadNameByIdMappings(final CompoundTag data, final String key) { + final CompoundTag nameMappings = data.getCompoundTag(key); + if (nameMappings == null) { + return null; + } + + final Int2ObjectMap map = new Int2ObjectArrayMap<>(nameMappings.size()); + for (final Map.Entry entry : nameMappings.entrySet()) { + final StringTag mappedTag = (StringTag) entry.getValue(); + map.put(Integer.parseInt(entry.getKey()), mappedTag.getValue()); + } + return map; } @Override @@ -142,13 +159,20 @@ public class BackwardsMappings extends MappingDataBase { public @Nullable String mappedEntityName(final String entityName) { if (entityNames == null) { - ViaBackwards.getPlatform().getLogger().severe("No entity mappings found when requesting them for " + entityName); - new Exception().printStackTrace(); + ViaBackwards.getPlatform().getLogger().log(Level.SEVERE, "No entity mappings found when requesting them for " + entityName, new RuntimeException()); return null; } return entityNames.get(entityName); } + public @Nullable String mappedEnchantmentName(final int enchantmentId) { + if (enchantmentNames == null) { + ViaBackwards.getPlatform().getLogger().log(Level.SEVERE, "No enchantment name mappings found when requesting " + enchantmentId, new RuntimeException()); + return null; + } + return enchantmentNames.get(enchantmentId); + } + public @Nullable Int2ObjectMap getBackwardsItemMappings() { return backwardsItemMappings; } diff --git a/common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsItemRewriterBase.java b/common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsItemRewriterBase.java index c28b0794..3099fe1f 100644 --- a/common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsItemRewriterBase.java +++ b/common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsItemRewriterBase.java @@ -27,6 +27,8 @@ import com.viaversion.viaversion.libs.opennbt.tag.builtin.ListTag; import com.viaversion.viaversion.libs.opennbt.tag.builtin.StringTag; import com.viaversion.viaversion.libs.opennbt.tag.builtin.Tag; import com.viaversion.viaversion.rewriter.ItemRewriter; +import java.util.ArrayList; +import java.util.List; import org.checkerframework.checker.nullness.qual.Nullable; public abstract class BackwardsItemRewriterBase original, String name) { + protected void saveListTag(CompoundTag tag, ListTag original, String name) { // Multiple places might try to backup data - String backupName = nbtTagName("o" + name); - if (!displayTag.contains(backupName)) { - displayTag.put(backupName, original.copy()); + String backupName = nbtTagName(name); + if (!tag.contains(backupName)) { + tag.put(backupName, original.copy()); } } + protected void saveGenericTagList(CompoundTag tag, List original, String name) { + // List tags cannot contain tags of different types, so we have to store them a bit more awkwardly as an indexed compound tag + String backupName = getNbtTagName() + "|" + name; + if (!tag.contains(backupName)) { + CompoundTag output = new CompoundTag(); + for (int i = 0; i < original.size(); i++) { + output.put(Integer.toString(i), original.get(i)); + } + tag.put(backupName, output); + } + } + + protected List removeGenericTagList(CompoundTag tag, String name) { + String backupName = getNbtTagName() + "|" + name; + CompoundTag data = tag.getCompoundTag(backupName); + if (data == null) { + return null; + } + + tag.remove(backupName); + return new ArrayList<>(data.values()); + } + protected void restoreDisplayTag(Item item) { if (item.tag() == null) return; @@ -90,19 +115,30 @@ public abstract class BackwardsItemRewriterBase) original).copy()); } } + public @Nullable ListTag removeListTag(CompoundTag tag, String tagName, Class tagType) { + String backupName = nbtTagName(tagName); + ListTag data = tag.getListTag(backupName, tagType); + if (data == null) { + return null; + } + + tag.remove(backupName); + return data; + } + @Override public String nbtTagName() { return "VB|" + protocol.getClass().getSimpleName(); diff --git a/common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsStructuredItemRewriter.java b/common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsStructuredItemRewriter.java index 6f952827..c0de1282 100644 --- a/common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsStructuredItemRewriter.java +++ b/common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsStructuredItemRewriter.java @@ -36,6 +36,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; public class BackwardsStructuredItemRewriter> extends BackwardsItemRewriter { + protected final StructuredEnchantmentRewriter enchantmentRewriter = new StructuredEnchantmentRewriter(this); + public BackwardsStructuredItemRewriter(final T protocol, final Type itemType, final Type itemArrayType) { super(protocol, itemType, itemArrayType); } @@ -53,6 +55,10 @@ public class BackwardsStructuredItemRewriter customNameData = data.getNonEmpty(StructuredDataKey.CUSTOM_NAME); @@ -114,6 +120,10 @@ public class BackwardsStructuredItemRewriter enchantmentMappings = new HashMap<>(); - private final BackwardsItemRewriter itemRewriter; + protected final Map enchantmentMappings = new HashMap<>(); + protected final BackwardsItemRewriter itemRewriter; private final boolean jsonFormat; public EnchantmentRewriter(BackwardsItemRewriter itemRewriter, boolean jsonFormat) { @@ -48,7 +49,7 @@ public class EnchantmentRewriter { } public void registerEnchantment(String key, String replacementLore) { - enchantmentMappings.put(key, replacementLore); + enchantmentMappings.put(Key.stripMinecraftNamespace(key), replacementLore); } public void handleToClient(Item item) { @@ -89,7 +90,7 @@ public class EnchantmentRewriter { continue; } - String enchantmentId = idTag.getValue(); + String enchantmentId = Key.stripMinecraftNamespace(idTag.getValue()); String remappedName = enchantmentMappings.get(enchantmentId); if (remappedName != null) { if (!changed) { diff --git a/common/src/main/java/com/viaversion/viabackwards/api/rewriters/StructuredEnchantmentRewriter.java b/common/src/main/java/com/viaversion/viabackwards/api/rewriters/StructuredEnchantmentRewriter.java new file mode 100644 index 00000000..19491c93 --- /dev/null +++ b/common/src/main/java/com/viaversion/viabackwards/api/rewriters/StructuredEnchantmentRewriter.java @@ -0,0 +1,189 @@ +/* + * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards + * Copyright (C) 2016-2024 ViaVersion and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.viaversion.viabackwards.api.rewriters; + +import com.viaversion.viabackwards.api.data.BackwardsMappings; +import com.viaversion.viaversion.api.data.Mappings; +import com.viaversion.viaversion.api.minecraft.data.StructuredData; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey; +import com.viaversion.viaversion.api.minecraft.item.Item; +import com.viaversion.viaversion.api.minecraft.item.data.Enchantments; +import com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap; +import com.viaversion.viaversion.libs.fastutil.ints.IntIntPair; +import com.viaversion.viaversion.libs.fastutil.objects.ObjectIterator; +import com.viaversion.viaversion.libs.opennbt.tag.builtin.ByteTag; +import com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag; +import com.viaversion.viaversion.libs.opennbt.tag.builtin.ListTag; +import com.viaversion.viaversion.libs.opennbt.tag.builtin.NumberTag; +import com.viaversion.viaversion.libs.opennbt.tag.builtin.Tag; +import com.viaversion.viaversion.util.ComponentUtil; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class StructuredEnchantmentRewriter { + + protected final BackwardsItemRewriter itemRewriter; + private boolean rewriteIds = true; + + public StructuredEnchantmentRewriter(final BackwardsItemRewriter itemRewriter) { + this.itemRewriter = itemRewriter; + } + + public void handleToClient(final Item item) { + final StructuredDataContainer data = item.structuredData(); + rewriteEnchantmentsToClient(data, StructuredDataKey.ENCHANTMENTS, false); + rewriteEnchantmentsToClient(data, StructuredDataKey.STORED_ENCHANTMENTS, true); + } + + public void handleToServer(final Item item) { + final StructuredDataContainer data = item.structuredData(); + final StructuredData customData = data.getNonEmpty(StructuredDataKey.CUSTOM_DATA); + if (customData == null) { + return; + } + + final CompoundTag tag = customData.value(); + if (tag.contains(itemRewriter.getNbtTagName() + "|enchantments")) { + rewriteEnchantmentsToServer(data, tag, StructuredDataKey.ENCHANTMENTS, false); + } + if (tag.contains(itemRewriter.getNbtTagName() + "|stored_enchantments")) { + rewriteEnchantmentsToServer(data, tag, StructuredDataKey.STORED_ENCHANTMENTS, true); + } + } + + public void rewriteEnchantmentsToClient(final StructuredDataContainer data, final StructuredDataKey key, final boolean storedEnchant) { + final StructuredData enchantmentsData = data.getNonEmpty(key); + if (enchantmentsData == null) { + return; + } + + final CompoundTag tag = data.computeIfAbsent(StructuredDataKey.CUSTOM_DATA, $ -> new CompoundTag()).value(); + final Enchantments enchantments = enchantmentsData.value(); + final List loreToAdd = new ArrayList<>(); + boolean changed = false; + + final ObjectIterator iterator = enchantments.enchantments().int2IntEntrySet().iterator(); + final List updatedIds = new ArrayList<>(); + while (iterator.hasNext()) { + final Int2IntMap.Entry entry = iterator.next(); + final BackwardsMappings mappingData = itemRewriter.protocol().getMappingData(); + final Mappings mappings = mappingData.getEnchantmentMappings(); + final int mappedId = mappings.getNewId(entry.getIntKey()); + if (mappedId != -1) { + if (rewriteIds) { + // Update the map after to iteration to preserve the current ids before possibly saving the original, avoid CME + updatedIds.add(IntIntPair.of(entry.getIntKey(), mappedId)); + } + continue; + } + + final String remappedName = mappingData.mappedEnchantmentName(entry.getIntKey()); + if (remappedName != null) { + if (!changed) { + // Backup original before doing modifications + itemRewriter.saveListTag(tag, asTag(enchantments), key.identifier()); + changed = true; + } + + final int level = entry.getIntValue(); + loreToAdd.add(ComponentUtil.jsonStringToTag(ComponentUtil.legacyToJsonString("ยง7" + remappedName + " " + EnchantmentRewriter.getRomanNumber(level), true))); + iterator.remove(); + } + } + + for (final IntIntPair pair : updatedIds) { + enchantments.add(pair.firstInt(), pair.secondInt()); + } + + if (loreToAdd.isEmpty()) { + // No removed enchantments + return; + } + + // Add glint override if there are no enchantments left + if (!storedEnchant && enchantments.size() == 0) { + final StructuredData glintOverride = data.getNonEmpty(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE); + if (glintOverride != null) { + tag.putBoolean(itemRewriter.getNbtTagName() + "|glint", glintOverride.value()); + } else { + tag.putBoolean(itemRewriter.getNbtTagName() + "|noglint", true); + } + data.set(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE, true); + } + + // Save original lore + final StructuredData loreData = data.getNonEmpty(StructuredDataKey.LORE); + if (loreData != null) { + final List loreList = Arrays.asList(loreData.value()); + itemRewriter.saveGenericTagList(tag, loreList, "lore"); + loreToAdd.addAll(loreList); + } else { + tag.putBoolean(itemRewriter.getNbtTagName() + "|nolore", true); + } + + if (enchantments.showInTooltip()) { + tag.putBoolean(itemRewriter.getNbtTagName() + "|show_" + key.identifier(), true); + } + + data.set(StructuredDataKey.LORE, loreToAdd.toArray(new Tag[0])); + } + + private ListTag asTag(final Enchantments enchantments) { + final ListTag listTag = new ListTag<>(CompoundTag.class); + for (final Int2IntMap.Entry entry : enchantments.enchantments().int2IntEntrySet()) { + final CompoundTag enchantment = new CompoundTag(); + enchantment.putInt("id", entry.getIntKey()); + enchantment.putInt("lvl", entry.getIntValue()); + listTag.add(enchantment); + } + return listTag; + } + + public void rewriteEnchantmentsToServer(final StructuredDataContainer data, final CompoundTag tag, final StructuredDataKey key, final boolean storedEnchant) { + final ListTag enchantmentsTag = itemRewriter.removeListTag(tag, key.identifier(), CompoundTag.class); + if (enchantmentsTag == null) { + return; + } + + final Tag glintTag = tag.remove(itemRewriter.getNbtTagName() + "|glint"); + if (glintTag instanceof ByteTag) { + data.set(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE, ((NumberTag) glintTag).asBoolean()); + } else if (tag.remove(itemRewriter.getNbtTagName() + "|noglint") != null) { + data.remove(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE); + } + + final List lore = itemRewriter.removeGenericTagList(tag, "lore"); + if (lore != null) { + data.set(StructuredDataKey.LORE, lore.toArray(new Tag[0])); + } else if (tag.remove(itemRewriter.getNbtTagName() + "|nolore") != null) { + data.remove(StructuredDataKey.LORE); + } + + final Enchantments enchantments = new Enchantments(tag.remove(itemRewriter.getNbtTagName() + "|show_" + key.identifier()) != null); + for (final CompoundTag enchantment : enchantmentsTag) { + enchantments.add(enchantment.getInt("id"), enchantment.getInt("lvl")); + } + data.set(key, enchantments); + } + + public void setRewriteIds(final boolean rewriteIds) { + this.rewriteIds = rewriteIds; + } +} diff --git a/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_3to1_20_5/rewriter/BlockItemPacketRewriter1_20_5.java b/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_3to1_20_5/rewriter/BlockItemPacketRewriter1_20_5.java index 8dbc653b..a9336dd1 100644 --- a/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_3to1_20_5/rewriter/BlockItemPacketRewriter1_20_5.java +++ b/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_3to1_20_5/rewriter/BlockItemPacketRewriter1_20_5.java @@ -18,6 +18,8 @@ package com.viaversion.viabackwards.protocol.protocol1_20_3to1_20_5.rewriter; import com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter; +import com.viaversion.viabackwards.api.rewriters.EnchantmentRewriter; +import com.viaversion.viabackwards.api.rewriters.StructuredEnchantmentRewriter; import com.viaversion.viabackwards.protocol.protocol1_20_3to1_20_5.Protocol1_20_3To1_20_5; import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.minecraft.Particle; @@ -42,6 +44,7 @@ public final class BlockItemPacketRewriter1_20_5 extends BackwardsStructuredItem public BlockItemPacketRewriter1_20_5(final Protocol1_20_3To1_20_5 protocol) { super(protocol, Types1_20_5.ITEM, Types1_20_5.ITEM_ARRAY, Type.ITEM1_20_2, Type.ITEM1_20_2_ARRAY); + enchantmentRewriter.setRewriteIds(false); // Let VV handle it } @Override diff --git a/common/src/main/resources/assets/viabackwards/data/mappings-1.20.5to1.20.3.nbt b/common/src/main/resources/assets/viabackwards/data/mappings-1.20.5to1.20.3.nbt index 05291b98481281f5bac1967e65ff0472be5a633c..9cf244d32407b9927852be8bebe091cf07564da5 100644 GIT binary patch delta 74 zcmbQ>b-`=HO=(+!)V$=3#JrN+)Vz|s#N5