Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -51,9 +53,13 @@ public class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S
T extends BackwardsProtocol<C, ?, ?, S>> extends StructuredItemRewriter<C, S, T> {

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
Expand All @@ -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());
}
}
Expand All @@ -92,13 +92,59 @@ 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);
if (removeBackupTag(customData, "id") instanceof final IntTag originalTag) {
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) {
Expand Down Expand Up @@ -313,6 +359,6 @@ protected Holder<SoundEvent> restoreSoundEventHolder(final CompoundTag tag, fina

@Override
public String nbtTagName() {
return "VB|" + protocol.getClass().getSimpleName();
return this.nbtTagName;
}
}
6 changes: 6 additions & 0 deletions common/src/main/resources/assets/viabackwards/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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