1
0
mirror of https://github.com/GeyserMC/Rainbow.git synced 2025-12-19 14:59:16 +00:00

Support mapping legacy custom model data items

This commit is contained in:
Eclipse
2025-07-19 12:31:14 +00:00
parent 983ae8c973
commit c6561a558f
5 changed files with 135 additions and 64 deletions

View File

@@ -5,6 +5,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.item.BlockModelWrapper; import net.minecraft.client.renderer.item.BlockModelWrapper;
import net.minecraft.client.renderer.item.ConditionalItemModel; import net.minecraft.client.renderer.item.ConditionalItemModel;
import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.item.RangeSelectItemModel;
import net.minecraft.client.renderer.item.SelectItemModel; import net.minecraft.client.renderer.item.SelectItemModel;
import net.minecraft.client.renderer.item.properties.conditional.Broken; import net.minecraft.client.renderer.item.properties.conditional.Broken;
import net.minecraft.client.renderer.item.properties.conditional.CustomModelDataProperty; import net.minecraft.client.renderer.item.properties.conditional.CustomModelDataProperty;
@@ -12,6 +13,7 @@ import net.minecraft.client.renderer.item.properties.conditional.Damaged;
import net.minecraft.client.renderer.item.properties.conditional.FishingRodCast; import net.minecraft.client.renderer.item.properties.conditional.FishingRodCast;
import net.minecraft.client.renderer.item.properties.conditional.HasComponent; import net.minecraft.client.renderer.item.properties.conditional.HasComponent;
import net.minecraft.client.renderer.item.properties.conditional.ItemModelPropertyTest; import net.minecraft.client.renderer.item.properties.conditional.ItemModelPropertyTest;
import net.minecraft.client.renderer.item.properties.numeric.RangeSelectItemModelProperty;
import net.minecraft.client.renderer.item.properties.select.Charge; import net.minecraft.client.renderer.item.properties.select.Charge;
import net.minecraft.client.renderer.item.properties.select.ContextDimension; import net.minecraft.client.renderer.item.properties.select.ContextDimension;
import net.minecraft.client.renderer.item.properties.select.DisplayContext; import net.minecraft.client.renderer.item.properties.select.DisplayContext;
@@ -19,7 +21,6 @@ import net.minecraft.client.renderer.item.properties.select.SelectItemModelPrope
import net.minecraft.client.renderer.item.properties.select.TrimMaterialProperty; import net.minecraft.client.renderer.item.properties.select.TrimMaterialProperty;
import net.minecraft.client.resources.model.Material; import net.minecraft.client.resources.model.Material;
import net.minecraft.client.resources.model.ResolvedModel; import net.minecraft.client.resources.model.ResolvedModel;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.DataComponents;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@@ -29,7 +30,6 @@ import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.item.CrossbowItem; import net.minecraft.world.item.CrossbowItem;
import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.ItemAttributeModifiers; import net.minecraft.world.item.component.ItemAttributeModifiers;
import net.minecraft.world.item.equipment.trim.TrimMaterial; import net.minecraft.world.item.equipment.trim.TrimMaterial;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@@ -43,20 +43,20 @@ import org.geysermc.rainbow.mapping.geometry.BedrockGeometryContext;
import org.geysermc.rainbow.mapping.geometry.GeometryMapper; import org.geysermc.rainbow.mapping.geometry.GeometryMapper;
import org.geysermc.rainbow.mapping.geometry.GeometryRenderer; import org.geysermc.rainbow.mapping.geometry.GeometryRenderer;
import org.geysermc.rainbow.mapping.geyser.GeyserBaseDefinition; import org.geysermc.rainbow.mapping.geyser.GeyserBaseDefinition;
import org.geysermc.rainbow.mapping.geyser.GeyserMappings; import org.geysermc.rainbow.mapping.geyser.GeyserItemDefinition;
import org.geysermc.rainbow.mapping.geyser.GeyserLegacyDefinition;
import org.geysermc.rainbow.mapping.geyser.GeyserSingleDefinition; import org.geysermc.rainbow.mapping.geyser.GeyserSingleDefinition;
import org.geysermc.rainbow.mapping.geyser.predicate.GeyserConditionPredicate; import org.geysermc.rainbow.mapping.geyser.predicate.GeyserConditionPredicate;
import org.geysermc.rainbow.mapping.geyser.predicate.GeyserMatchPredicate; import org.geysermc.rainbow.mapping.geyser.predicate.GeyserMatchPredicate;
import org.geysermc.rainbow.mapping.geyser.predicate.GeyserPredicate; import org.geysermc.rainbow.mapping.geyser.predicate.GeyserPredicate;
import org.geysermc.rainbow.mixin.ConditionalItemModelAccessor; import org.geysermc.rainbow.mixin.ConditionalItemModelAccessor;
import org.geysermc.rainbow.mixin.RangeSelectItemModelAccessor;
import org.geysermc.rainbow.mixin.SelectItemModelAccessor; import org.geysermc.rainbow.mixin.SelectItemModelAccessor;
import org.geysermc.rainbow.pack.BedrockItem; import org.geysermc.rainbow.pack.BedrockItem;
import org.geysermc.rainbow.pack.BedrockTextures; import org.geysermc.rainbow.pack.BedrockTextures;
import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -65,32 +65,39 @@ public class BedrockItemMapper {
.map(ResourceLocation::withDefaultNamespace) .map(ResourceLocation::withDefaultNamespace)
.toList(); .toList();
public static void tryMapStack(ItemStack stack, ResourceLocation model, ProblemReporter reporter, public static void tryMapStack(ItemStack stack, ResourceLocation modelLocation, ProblemReporter reporter, PackContext context) {
GeyserMappings mappings, Path packPath, BedrockItemConsumer itemConsumer, Consumer<ResourceLocation> additionalTextureConsumer) { ItemModel model = Minecraft.getInstance().getModelManager().getItemModel(modelLocation);
String displayName = stack.getHoverName().getString(); mapItem(model, stack, reporter.forChild(() -> "client item definition " + modelLocation + " "), base -> new GeyserSingleDefinition(base, Optional.of(modelLocation)), context);
int protectionValue = 0;
ItemAttributeModifiers modifiers = stack.get(DataComponents.ATTRIBUTE_MODIFIERS);
if (modifiers != null) {
protectionValue = modifiers.modifiers().stream()
.filter(modifier -> modifier.attribute() == Attributes.ARMOR && modifier.modifier().operation() == AttributeModifier.Operation.ADD_VALUE)
.mapToInt(entry -> (int) entry.modifier().amount())
.sum();
}
mapItem(model, displayName, protectionValue, stack.getComponentsPatch(), reporter,
packPath, mapping -> mappings.map(stack.getItemHolder(), new GeyserSingleDefinition(mapping, Optional.of(model))),
itemConsumer, additionalTextureConsumer);
} }
public static void mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch, ProblemReporter reporter, public static void tryMapStack(ItemStack stack, int customModelData, ProblemReporter reporter, PackContext context) {
Path packPath, Consumer<GeyserBaseDefinition> mappingConsumer, BedrockItemConsumer itemConsumer, ItemModel vanillaModel = Minecraft.getInstance().getModelManager().getItemModel(stack.get(DataComponents.ITEM_MODEL));
Consumer<ResourceLocation> additionalTextureConsumer) { reporter = reporter.forChild(() -> "item model " + vanillaModel + " with custom model data " + customModelData + " ");
ItemModel model = Minecraft.getInstance().getModelManager().getItemModel(modelLocation); if (vanillaModel instanceof RangeSelectItemModel rangeModel) {
MappingContext context = new MappingContext(List.of(), displayName, protectionValue, componentPatch, RangeSelectItemModelAccessor accessor = (RangeSelectItemModelAccessor) rangeModel;
reporter.forChild(() -> "client item definition " + modelLocation + " "), RangeSelectItemModelProperty property = accessor.getProperty();
packPath, mappingConsumer, itemConsumer, additionalTextureConsumer); // WHY, Mojang?
mapItem(model, context); if (property instanceof net.minecraft.client.renderer.item.properties.numeric.CustomModelDataProperty(int index)) {
if (index == 0) {
float scaledCustomModelData = customModelData * accessor.getScale();
int modelIndex = RangeSelectItemModelAccessor.invokeLastIndexLessOrEqual(accessor.getThresholds(), scaledCustomModelData);
ItemModel model = modelIndex == -1 ? accessor.getFallback() : accessor.getModels()[index];
mapItem(model, stack, reporter, base -> new GeyserLegacyDefinition(base, customModelData), context);
} else {
reporter.report(() -> "range_dispatch custom model data property index is not zero, unable to apply custom model data");
}
} else {
reporter.report(() -> "range_dispatch model property is not custom model data, unable to apply custom model data");
}
} else {
reporter.report(() -> "item model is not range_dispatch, unable to apply custom model data");
}
}
public static void mapItem(ItemModel model, ItemStack stack, ProblemReporter reporter,
Function<GeyserBaseDefinition, GeyserItemDefinition> definitionCreator, PackContext packContext) {
mapItem(model, new MappingContext(List.of(), stack, reporter, definitionCreator, packContext));
} }
private static void mapItem(ItemModel model, MappingContext context) { private static void mapItem(ItemModel model, MappingContext context) {
@@ -189,33 +196,30 @@ public class BedrockItemMapper {
mapItem(cases.defaultReturnValue(), context.child("select fallback case ")); mapItem(cases.defaultReturnValue(), context.child("select fallback case "));
} }
private record MappingContext(List<GeyserPredicate> predicateStack, String displayName, int protectionValue, DataComponentPatch componentPatch, ProblemReporter reporter, private record MappingContext(List<GeyserPredicate> predicateStack, ItemStack stack, ProblemReporter reporter,
Path packPath, Consumer<GeyserBaseDefinition> mappingConsumer, BedrockItemConsumer itemConsumer, Function<GeyserBaseDefinition, GeyserItemDefinition> definitionCreator, PackContext packContext) {
Consumer<ResourceLocation> additionalTextureConsumer) {
public MappingContext with(GeyserPredicate predicate, String childName) { public MappingContext with(GeyserPredicate predicate, String childName) {
return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), displayName, protectionValue, componentPatch, return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), stack, reporter.forChild(() -> childName), definitionCreator, packContext);
reporter.forChild(() -> childName), packPath, mappingConsumer, itemConsumer, additionalTextureConsumer);
} }
public MappingContext child(String childName) { public MappingContext child(String childName) {
return new MappingContext(predicateStack, displayName, protectionValue, componentPatch, reporter.forChild(() -> childName), return new MappingContext(predicateStack, stack, reporter.forChild(() -> childName), definitionCreator, packContext);
packPath, mappingConsumer, itemConsumer, additionalTextureConsumer);
} }
public void create(ResourceLocation bedrockIdentifier, ResourceLocation texture, boolean displayHandheld, public void create(ResourceLocation bedrockIdentifier, ResourceLocation texture, boolean displayHandheld,
Optional<ResolvedModel> customModel) { Optional<ResolvedModel> customModel) {
GeyserBaseDefinition definition = new GeyserBaseDefinition(bedrockIdentifier, Optional.of(displayName), predicateStack, GeyserBaseDefinition base = new GeyserBaseDefinition(bedrockIdentifier, Optional.of(stack.getHoverName().getString()), predicateStack,
new GeyserBaseDefinition.BedrockOptions(Optional.empty(), true, displayHandheld, protectionValue), componentPatch); new GeyserBaseDefinition.BedrockOptions(Optional.empty(), true, displayHandheld, calculateProtectionValue(stack)), stack.getComponentsPatch());
try { try {
mappingConsumer.accept(definition); packContext.mappings().map(stack.getItemHolder(), definitionCreator.apply(base));
} catch (Exception exception) { } catch (Exception exception) {
reporter.forChild(() -> "mapping with bedrock identifier " + bedrockIdentifier + " ").report(() -> "failed to pass mapping: " + exception.getMessage()); reporter.forChild(() -> "mapping with bedrock identifier " + bedrockIdentifier + " ").report(() -> "failed to pass mapping: " + exception.getMessage());
return; return;
} }
// TODO Should probably get a better way to get geometry texture // TODO Should probably get a better way to get geometry texture
String safeIdentifier = definition.textureName(); String safeIdentifier = base.textureName();
String bone = "bone"; String bone = "bone";
ResourceLocation geometryTexture = texture; ResourceLocation geometryTexture = texture;
Optional<BedrockGeometryContext> bedrockGeometry = customModel.map(model -> GeometryMapper.mapGeometry(safeIdentifier, bone, model, geometryTexture)); Optional<BedrockGeometryContext> bedrockGeometry = customModel.map(model -> GeometryMapper.mapGeometry(safeIdentifier, bone, model, geometryTexture));
@@ -223,18 +227,26 @@ public class BedrockItemMapper {
boolean exportTexture = true; boolean exportTexture = true;
if (customModel.isPresent()) { if (customModel.isPresent()) {
ItemStack fakeItem = new ItemStack(Items.FLINT);
//fakeItem.set(DataComponents.ITEM_MODEL, model); TODO
texture = texture.withPath(path -> path + "_icon"); texture = texture.withPath(path -> path + "_icon");
GeometryRenderer.render(fakeItem, packPath.resolve(BedrockTextures.TEXTURES_FOLDER + texture.getPath() + ".png")); GeometryRenderer.render(stack, packContext.packPath().resolve(BedrockTextures.TEXTURES_FOLDER + texture.getPath() + ".png"));
exportTexture = false; exportTexture = false;
additionalTextureConsumer.accept(geometryTexture); packContext.additionalTextureConsumer().accept(geometryTexture);
} }
itemConsumer.accept(new BedrockItem(bedrockIdentifier, definition.textureName(), texture, exportTexture, packContext.itemConsumer().accept(new BedrockItem(bedrockIdentifier, base.textureName(), texture, exportTexture,
AttachableMapper.mapItem(componentPatch, bedrockIdentifier, bedrockGeometry, bedrockAnimation, additionalTextureConsumer), AttachableMapper.mapItem(stack.getComponentsPatch(), bedrockIdentifier, bedrockGeometry, bedrockAnimation, packContext.additionalTextureConsumer()),
bedrockGeometry.map(BedrockGeometryContext::geometry), bedrockAnimation.map(BedrockAnimationContext::animation))); bedrockGeometry.map(BedrockGeometryContext::geometry), bedrockAnimation.map(BedrockAnimationContext::animation)));
} }
private static int calculateProtectionValue(ItemStack stack) {
ItemAttributeModifiers modifiers = stack.get(DataComponents.ATTRIBUTE_MODIFIERS);
if (modifiers != null) {
return modifiers.modifiers().stream()
.filter(modifier -> modifier.attribute() == Attributes.ARMOR && modifier.modifier().operation() == AttributeModifier.Operation.ADD_VALUE)
.mapToInt(entry -> (int) entry.modifier().amount())
.sum();
}
return 0;
}
} }
} }

View File

@@ -0,0 +1,10 @@
package org.geysermc.rainbow.mapping;
import net.minecraft.resources.ResourceLocation;
import org.geysermc.rainbow.mapping.geyser.GeyserMappings;
import java.nio.file.Path;
import java.util.function.Consumer;
public record PackContext(GeyserMappings mappings, Path packPath, BedrockItemConsumer itemConsumer, Consumer<ResourceLocation> additionalTextureConsumer) {
}

View File

@@ -0,0 +1,32 @@
package org.geysermc.rainbow.mixin;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.item.RangeSelectItemModel;
import net.minecraft.client.renderer.item.properties.numeric.RangeSelectItemModelProperty;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(RangeSelectItemModel.class)
public interface RangeSelectItemModelAccessor {
@Accessor
RangeSelectItemModelProperty getProperty();
@Accessor
float getScale();
@Accessor
float[] getThresholds();
@Accessor
ItemModel[] getModels();
@Accessor
ItemModel getFallback();
@Invoker
static int invokeLastIndexLessOrEqual(float[] thresholds, float value) {
throw new AssertionError();
}
}

View File

@@ -1,5 +1,7 @@
package org.geysermc.rainbow.pack; package org.geysermc.rainbow.pack;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.SplashRenderer; import net.minecraft.client.gui.components.SplashRenderer;
@@ -9,11 +11,13 @@ import net.minecraft.util.ProblemReporter;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
import net.minecraft.util.StringUtil; import net.minecraft.util.StringUtil;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomModelData;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.geysermc.rainbow.CodecUtil; import org.geysermc.rainbow.CodecUtil;
import org.geysermc.rainbow.PackConstants; import org.geysermc.rainbow.PackConstants;
import org.geysermc.rainbow.Rainbow; import org.geysermc.rainbow.Rainbow;
import org.geysermc.rainbow.mapping.BedrockItemMapper; import org.geysermc.rainbow.mapping.BedrockItemMapper;
import org.geysermc.rainbow.mapping.PackContext;
import org.geysermc.rainbow.mapping.geyser.GeyserMappings; import org.geysermc.rainbow.mapping.geyser.GeyserMappings;
import org.geysermc.rainbow.mixin.SplashRendererAccessor; import org.geysermc.rainbow.mixin.SplashRendererAccessor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -36,7 +40,8 @@ public class BedrockPack {
"use !!plshelp", "rm -rf --no-preserve-root /*", "welcome to the internet!", "beep beep. boop boop?", "FROG", "it is frog day", "it is cat day!", "use !!plshelp", "rm -rf --no-preserve-root /*", "welcome to the internet!", "beep beep. boop boop?", "FROG", "it is frog day", "it is cat day!",
"eclipse will hear about this.", "you must now say the word 'frog' in the #general channel", "You Just Lost The Game", "you are now breathing manually", "eclipse will hear about this.", "you must now say the word 'frog' in the #general channel", "You Just Lost The Game", "you are now breathing manually",
"you are now blinking manually", "you're eligible for a free hug token! <3", "don't mind me!", "hissss", "Gayser and Floodgayte, my favourite plugins.", "you are now blinking manually", "you're eligible for a free hug token! <3", "don't mind me!", "hissss", "Gayser and Floodgayte, my favourite plugins.",
"meow", "we'll be done here soon™", "got anything else to say?", "we're done now!", "this will be fixed by v6053", "expect it to be done within 180 business days!"); "meow", "we'll be done here soon™", "got anything else to say?", "we're done now!", "this will be fixed by v6053", "expect it to be done within 180 business days!",
"any colour you like", "someone tell Mojang about this");
private static final RandomSource RANDOM = RandomSource.create(); private static final RandomSource RANDOM = RandomSource.create();
private static final Path EXPORT_DIRECTORY = FabricLoader.getInstance().getGameDir().resolve(Rainbow.MOD_ID); private static final Path EXPORT_DIRECTORY = FabricLoader.getInstance().getGameDir().resolve(Rainbow.MOD_ID);
@@ -62,6 +67,7 @@ public class BedrockPack {
private final Set<BedrockItem> bedrockItems = new HashSet<>(); private final Set<BedrockItem> bedrockItems = new HashSet<>();
private final Set<ResourceLocation> texturesToExport = new HashSet<>(); private final Set<ResourceLocation> texturesToExport = new HashSet<>();
private final Set<ResourceLocation> modelsMapped = new HashSet<>(); private final Set<ResourceLocation> modelsMapped = new HashSet<>();
private final IntSet customModelDataMapped = new IntOpenHashSet();
private final ProblemReporter.Collector reporter; private final ProblemReporter.Collector reporter;
@@ -90,16 +96,6 @@ public class BedrockPack {
return MappingResult.NONE_MAPPED; return MappingResult.NONE_MAPPED;
} }
Optional<? extends ResourceLocation> patchedModel = stack.getComponentsPatch().get(DataComponents.ITEM_MODEL);
//noinspection OptionalAssignedToNull - annoying Mojang
if (patchedModel == null || patchedModel.isEmpty()) {
return MappingResult.NONE_MAPPED;
}
ResourceLocation model = patchedModel.get();
if (!modelsMapped.add(model)) {
return MappingResult.NONE_MAPPED;
}
AtomicBoolean problems = new AtomicBoolean(); AtomicBoolean problems = new AtomicBoolean();
ProblemReporter mapReporter = new ProblemReporter() { ProblemReporter mapReporter = new ProblemReporter() {
@@ -114,14 +110,34 @@ public class BedrockPack {
reporter.report(problem); reporter.report(problem);
} }
}; };
PackContext context = new PackContext(mappings, packPath, item -> {
BedrockItemMapper.tryMapStack(stack, model, mapReporter, mappings, packPath, bedrockItem -> { itemTextures.withItemTexture(item);
itemTextures.withItemTexture(bedrockItem); if (item.exportTexture()) {
if (bedrockItem.exportTexture()) { texturesToExport.add(item.texture());
texturesToExport.add(bedrockItem.texture());
} }
bedrockItems.add(bedrockItem); bedrockItems.add(item);
}, texturesToExport::add); }, texturesToExport::add);
Optional<? extends ResourceLocation> patchedModel = stack.getComponentsPatch().get(DataComponents.ITEM_MODEL);
//noinspection OptionalAssignedToNull - annoying Mojang
if (patchedModel == null || patchedModel.isEmpty()) {
CustomModelData customModelData = stack.get(DataComponents.CUSTOM_MODEL_DATA);
Float firstNumber;
if (customModelData == null || (firstNumber = customModelData.getFloat(0)) == null
|| !customModelDataMapped.add((firstNumber.intValue()))) {
return MappingResult.NONE_MAPPED;
}
BedrockItemMapper.tryMapStack(stack, firstNumber.intValue(), mapReporter, context);
} else {
ResourceLocation model = patchedModel.get();
if (!modelsMapped.add(model)) {
return MappingResult.NONE_MAPPED;
}
BedrockItemMapper.tryMapStack(stack, model, mapReporter, context);
}
return problems.get() ? MappingResult.PROBLEMS_OCCURRED : MappingResult.MAPPED_SUCCESSFULLY; return problems.get() ? MappingResult.PROBLEMS_OCCURRED : MappingResult.MAPPED_SUCCESSFULLY;
} }

View File

@@ -13,6 +13,7 @@
"ModelManagerMixin", "ModelManagerMixin",
"PictureInPictureRendererAccessor", "PictureInPictureRendererAccessor",
"PictureInPictureRendererMixin", "PictureInPictureRendererMixin",
"RangeSelectItemModelAccessor",
"SelectItemModelAccessor", "SelectItemModelAccessor",
"SelectItemModelMixin", "SelectItemModelMixin",
"SelectItemModelMixin$UnbakedSwitchMixin", "SelectItemModelMixin$UnbakedSwitchMixin",