diff --git a/common/src/main/java/com/viaversion/viabackwards/ViaBackwardsConfig.java b/common/src/main/java/com/viaversion/viabackwards/ViaBackwardsConfig.java index e4ea4a96..9eca8419 100644 --- a/common/src/main/java/com/viaversion/viabackwards/ViaBackwardsConfig.java +++ b/common/src/main/java/com/viaversion/viabackwards/ViaBackwardsConfig.java @@ -47,6 +47,7 @@ public class ViaBackwardsConfig extends Config implements com.viaversion.viaback private boolean dialogsViaChests; private DialogStyleConfig dialogStyleConfig; private boolean codeOfConductAsDialog; + private boolean passOriginalItemNameToResourcePacks; public ViaBackwardsConfig(File configFile, Logger logger) { super(configFile, logger); @@ -75,6 +76,7 @@ private void loadFields() { dialogsViaChests = getBoolean("dialogs-via-chests", true); dialogStyleConfig = loadDialogStyleConfig(getSection("dialog-style")); codeOfConductAsDialog = getBoolean("code-of-conduct-as-dialog", true); + passOriginalItemNameToResourcePacks = getBoolean("pass-original-item-name-to-resource-packs", true); } private DialogStyleConfig loadDialogStyleConfig(final ConfigSection section) { @@ -179,6 +181,11 @@ public boolean codeOfConductAsDialog() { return codeOfConductAsDialog; } + @Override + public boolean passOriginalItemNameToResourcePacks() { + return passOriginalItemNameToResourcePacks; + } + @Override public URL getDefaultConfigURL() { return getClass().getClassLoader().getResource("assets/viabackwards/config.yml"); diff --git a/common/src/main/java/com/viaversion/viabackwards/api/ViaBackwardsConfig.java b/common/src/main/java/com/viaversion/viabackwards/api/ViaBackwardsConfig.java index 807a466e..b16e54a4 100644 --- a/common/src/main/java/com/viaversion/viabackwards/api/ViaBackwardsConfig.java +++ b/common/src/main/java/com/viaversion/viabackwards/api/ViaBackwardsConfig.java @@ -133,4 +133,13 @@ public interface ViaBackwardsConfig extends Config { * @return true if enabled */ boolean codeOfConductAsDialog(); + + /** + * Injects the original vanilla 1.21.4+ item name into custom_model_data strings for resource packs. + * Disable if your server creates custom items using modern items as their base. + * Tip: For server custom items, base them on items in the game before 1.21.4 (e.g. saddle) to ensure compatibility. + * + * @return true if enabled + */ + boolean passOriginalItemNameToResourcePacks(); } 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 9e5c1320..e8040b48 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 @@ -24,6 +24,7 @@ import com.viaversion.nbt.tag.ListTag; import com.viaversion.nbt.tag.StringTag; import com.viaversion.nbt.tag.Tag; +import com.viaversion.viabackwards.ViaBackwards; import com.viaversion.viabackwards.api.BackwardsProtocol; import com.viaversion.viabackwards.api.data.BackwardsMappingData; import com.viaversion.viabackwards.api.data.MappedItem; @@ -40,6 +41,7 @@ import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import com.viaversion.viaversion.rewriter.StructuredItemRewriter; +import com.viaversion.viaversion.util.ArrayUtil; import com.viaversion.viaversion.util.Key; import java.util.ArrayList; import java.util.List; @@ -51,9 +53,13 @@ public class BackwardsStructuredItemRewriter> extends StructuredItemRewriter { private static final int[] EMPTY_INT_ARRAY = new int[0]; + private static final String GLOBAL_MODEL_DATA_MARKER = "VB|injected_cmd"; + + private final String nbtTagName; public BackwardsStructuredItemRewriter(T protocol) { super(protocol); + this.nbtTagName = "VB|" + protocol.getClass().getSimpleName(); } @Override @@ -70,17 +76,11 @@ protected void backupInconvertibleData(final UserConnection connection, final It customTag.putInt(nbtTagName("id"), item.identifier()); // Save original id // Add custom model data - if (mappedItem.customModelData() != null) { + final boolean addOriginalIdentifier = ViaBackwards.getConfig().passOriginalItemNameToResourcePacks(); + if (mappedItem.customModelData() != null || addOriginalIdentifier) { if (connection.getProtocolInfo().protocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_21_4)) { - if (!dataContainer.has(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4)) { - dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, new CustomModelData1_21_4( - new float[]{mappedItem.customModelData().floatValue()}, - new boolean[0], - new String[0], - EMPTY_INT_ARRAY - )); - } - } else if (!dataContainer.has(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5)) { + addCustomModelData(item, addOriginalIdentifier, mappedItem, customTag); + } else if (mappedItem.customModelData() != null && !dataContainer.has(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5)) { dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5, mappedItem.customModelData()); } } @@ -92,6 +92,33 @@ protected void backupInconvertibleData(final UserConnection connection, final It } } + private void addCustomModelData(final Item item, final boolean addOriginalIdentifier, final MappedItem mappedItem, final CompoundTag customTag) { + final StructuredDataContainer dataContainer = item.dataContainer(); + CustomModelData1_21_4 customModelData = dataContainer.get(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4); + if (customModelData == null) { + final String[] strings = addOriginalIdentifier + ? new String[]{protocol.getMappingData().getFullItemMappings().identifier(item.identifier())} + : new String[0]; + customModelData = new CustomModelData1_21_4( + mappedItem.customModelData() != null ? new float[]{mappedItem.customModelData().floatValue()} : new float[0], + new boolean[0], + strings, + EMPTY_INT_ARRAY + ); + dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, customModelData); + // Add one global marker and one for this specific version, so it is removed at the correct protocol + customTag.putBoolean(GLOBAL_MODEL_DATA_MARKER, true); + customTag.putBoolean(nbtTagName("added_custom_model_data"), true); + } else if (addOriginalIdentifier && !customTag.contains(GLOBAL_MODEL_DATA_MARKER)) { + final String identifier = protocol.getMappingData().getFullItemMappings().identifier(item.identifier()); + dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, new CustomModelData1_21_4( + customModelData.floats(), customModelData.booleans(), ArrayUtil.add(customModelData.strings(), identifier), customModelData.colors() + )); + customTag.putBoolean(GLOBAL_MODEL_DATA_MARKER, true); + customTag.putString(nbtTagName("injected_custom_model_data"), identifier); + } + } + @Override protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) { super.restoreBackupData(item, container, customData); @@ -99,6 +126,25 @@ protected void restoreBackupData(final Item item, final StructuredDataContainer item.setIdentifier(originalTag.asInt()); removeCustomTag(container, customData); } + + if (removeBackupTag(customData, "injected_custom_model_data") instanceof StringTag injectedCustomModelData) { + customData.remove(GLOBAL_MODEL_DATA_MARKER); + container.replace(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, customModelData -> { + final String target = injectedCustomModelData.getValue(); + final String[] strings = customModelData.strings(); + for (int i = 0; i < strings.length; i++) { + if (strings[i].equals(target)) { + // Remove the injected string + final String[] filteredStrings = ArrayUtil.remove(strings, i); + return new CustomModelData1_21_4(customModelData.floats(), customModelData.booleans(), filteredStrings, customModelData.colors()); + } + } + return customModelData; + }); + } else if (removeBackupTag(customData, "added_custom_model_data") != null) { + customData.remove(GLOBAL_MODEL_DATA_MARKER); + container.remove(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4); + } } protected void saveListTag(CompoundTag tag, ListTag original, String name) { @@ -313,6 +359,6 @@ protected Holder restoreSoundEventHolder(final CompoundTag tag, fina @Override public String nbtTagName() { - return "VB|" + protocol.getClass().getSimpleName(); + return this.nbtTagName; } } diff --git a/common/src/main/resources/assets/viabackwards/config.yml b/common/src/main/resources/assets/viabackwards/config.yml index 0d99df5a..f2f7ea6c 100644 --- a/common/src/main/resources/assets/viabackwards/config.yml +++ b/common/src/main/resources/assets/viabackwards/config.yml @@ -68,3 +68,9 @@ dialog-style: # Note that this is not supported for clients below 1.21.6 right now due to missing support for # dialogs during the configuration phase. code-of-conduct-as-dialog: true +# +# Passes the original vanilla 1.21.4+ item name into custom_model_data strings. +# This allows client resource packs to restore the appearance of newer items entirely missing from this older version. +# Disable if your server creates custom items using modern items as their base. +# Tip: For server custom items, base them on items in the game before 1.21.4 (e.g. saddle) to ensure compatibility. +pass-original-item-name-to-resource-packs: true