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..83a24b86 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 INJECTED_CMD_MARKER = "VB|injected_cmd"; + + private final String nbtTagName; public BackwardsStructuredItemRewriter(T protocol) { super(protocol); + this.nbtTagName = "VB|" + protocol.getClass().getSimpleName(); } @Override @@ -70,17 +76,39 @@ 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) { + if (mappedItem.customModelData() != null || ViaBackwards.getConfig().passOriginalItemNameToResourcePacks()) { 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()}, + CustomModelData1_21_4 customModelData = dataContainer.get(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4); + if (customModelData == null) { + customModelData = new CustomModelData1_21_4( + mappedItem.customModelData() != null ? new float[]{mappedItem.customModelData().floatValue()} : new float[0], new boolean[0], new String[0], EMPTY_INT_ARRAY - )); + ); + dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, customModelData); + } + + if (ViaBackwards.getConfig().passOriginalItemNameToResourcePacks() && mappingData.getFullItemMappings() != null) { + final String identifier = mappingData.getFullItemMappings().identifier(item.identifier()); + if (identifier != null && !customTag.contains(INJECTED_CMD_MARKER)) { + boolean exists = false; + for (final String s : customModelData.strings()) { + if (s.equals(identifier)) { + exists = true; + break; + } + } + if (!exists) { + dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, new CustomModelData1_21_4( + customModelData.floats(), customModelData.booleans(), ArrayUtil.add(customModelData.strings(), identifier), customModelData.colors() + )); + customTag.putString(nbtTagName("injected_cmd_string"), identifier); + customTag.putBoolean(INJECTED_CMD_MARKER, true); + } + } } - } else if (!dataContainer.has(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5)) { + } else if (mappedItem.customModelData() != null && !dataContainer.has(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5)) { dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5, mappedItem.customModelData()); } } @@ -99,6 +127,35 @@ protected void restoreBackupData(final Item item, final StructuredDataContainer item.setIdentifier(originalTag.asInt()); removeCustomTag(container, customData); } + + final Tag injectedCmdTag = removeBackupTag(customData, "injected_cmd_string"); + if (injectedCmdTag instanceof StringTag stringTag) { + customData.remove(INJECTED_CMD_MARKER); + final CustomModelData1_21_4 customModelData = container.get(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4); + if (customModelData != null && customModelData.strings() != null) { + final String target = stringTag.getValue(); + final String[] oldStrings = customModelData.strings(); + + int index = -1; + for (int i = 0; i < oldStrings.length; i++) { + if (oldStrings[i].equals(target)) { + index = i; + break; + } + } + + if (index != -1) { + final String[] newStrings = ArrayUtil.remove(oldStrings, index); + if (newStrings.length == 0 && customModelData.floats().length == 0 && customModelData.booleans().length == 0 && customModelData.colors().length == 0) { + container.remove(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4); + } else { + container.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, new CustomModelData1_21_4( + customModelData.floats(), customModelData.booleans(), newStrings, customModelData.colors() + )); + } + } + } + } } protected void saveListTag(CompoundTag tag, ListTag original, String name) { @@ -313,6 +370,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