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:
@@ -5,6 +5,7 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.item.BlockModelWrapper;
|
||||
import net.minecraft.client.renderer.item.ConditionalItemModel;
|
||||
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.properties.conditional.Broken;
|
||||
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.HasComponent;
|
||||
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.ContextDimension;
|
||||
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.resources.model.Material;
|
||||
import net.minecraft.client.resources.model.ResolvedModel;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
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.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.ItemAttributeModifiers;
|
||||
import net.minecraft.world.item.equipment.trim.TrimMaterial;
|
||||
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.GeometryRenderer;
|
||||
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.predicate.GeyserConditionPredicate;
|
||||
import org.geysermc.rainbow.mapping.geyser.predicate.GeyserMatchPredicate;
|
||||
import org.geysermc.rainbow.mapping.geyser.predicate.GeyserPredicate;
|
||||
import org.geysermc.rainbow.mixin.ConditionalItemModelAccessor;
|
||||
import org.geysermc.rainbow.mixin.RangeSelectItemModelAccessor;
|
||||
import org.geysermc.rainbow.mixin.SelectItemModelAccessor;
|
||||
import org.geysermc.rainbow.pack.BedrockItem;
|
||||
import org.geysermc.rainbow.pack.BedrockTextures;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -65,32 +65,39 @@ public class BedrockItemMapper {
|
||||
.map(ResourceLocation::withDefaultNamespace)
|
||||
.toList();
|
||||
|
||||
public static void tryMapStack(ItemStack stack, ResourceLocation model, ProblemReporter reporter,
|
||||
GeyserMappings mappings, Path packPath, BedrockItemConsumer itemConsumer, Consumer<ResourceLocation> additionalTextureConsumer) {
|
||||
String displayName = stack.getHoverName().getString();
|
||||
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 tryMapStack(ItemStack stack, ResourceLocation modelLocation, ProblemReporter reporter, PackContext context) {
|
||||
ItemModel model = Minecraft.getInstance().getModelManager().getItemModel(modelLocation);
|
||||
mapItem(model, stack, reporter.forChild(() -> "client item definition " + modelLocation + " "), base -> new GeyserSingleDefinition(base, Optional.of(modelLocation)), context);
|
||||
}
|
||||
|
||||
public static void mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch, ProblemReporter reporter,
|
||||
Path packPath, Consumer<GeyserBaseDefinition> mappingConsumer, BedrockItemConsumer itemConsumer,
|
||||
Consumer<ResourceLocation> additionalTextureConsumer) {
|
||||
ItemModel model = Minecraft.getInstance().getModelManager().getItemModel(modelLocation);
|
||||
MappingContext context = new MappingContext(List.of(), displayName, protectionValue, componentPatch,
|
||||
reporter.forChild(() -> "client item definition " + modelLocation + " "),
|
||||
packPath, mappingConsumer, itemConsumer, additionalTextureConsumer);
|
||||
mapItem(model, context);
|
||||
public static void tryMapStack(ItemStack stack, int customModelData, ProblemReporter reporter, PackContext context) {
|
||||
ItemModel vanillaModel = Minecraft.getInstance().getModelManager().getItemModel(stack.get(DataComponents.ITEM_MODEL));
|
||||
reporter = reporter.forChild(() -> "item model " + vanillaModel + " with custom model data " + customModelData + " ");
|
||||
if (vanillaModel instanceof RangeSelectItemModel rangeModel) {
|
||||
RangeSelectItemModelAccessor accessor = (RangeSelectItemModelAccessor) rangeModel;
|
||||
RangeSelectItemModelProperty property = accessor.getProperty();
|
||||
// WHY, Mojang?
|
||||
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) {
|
||||
@@ -189,33 +196,30 @@ public class BedrockItemMapper {
|
||||
mapItem(cases.defaultReturnValue(), context.child("select fallback case "));
|
||||
}
|
||||
|
||||
private record MappingContext(List<GeyserPredicate> predicateStack, String displayName, int protectionValue, DataComponentPatch componentPatch, ProblemReporter reporter,
|
||||
Path packPath, Consumer<GeyserBaseDefinition> mappingConsumer, BedrockItemConsumer itemConsumer,
|
||||
Consumer<ResourceLocation> additionalTextureConsumer) {
|
||||
private record MappingContext(List<GeyserPredicate> predicateStack, ItemStack stack, ProblemReporter reporter,
|
||||
Function<GeyserBaseDefinition, GeyserItemDefinition> definitionCreator, PackContext packContext) {
|
||||
|
||||
public MappingContext with(GeyserPredicate predicate, String childName) {
|
||||
return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), displayName, protectionValue, componentPatch,
|
||||
reporter.forChild(() -> childName), packPath, mappingConsumer, itemConsumer, additionalTextureConsumer);
|
||||
return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), stack, reporter.forChild(() -> childName), definitionCreator, packContext);
|
||||
}
|
||||
|
||||
public MappingContext child(String childName) {
|
||||
return new MappingContext(predicateStack, displayName, protectionValue, componentPatch, reporter.forChild(() -> childName),
|
||||
packPath, mappingConsumer, itemConsumer, additionalTextureConsumer);
|
||||
return new MappingContext(predicateStack, stack, reporter.forChild(() -> childName), definitionCreator, packContext);
|
||||
}
|
||||
|
||||
public void create(ResourceLocation bedrockIdentifier, ResourceLocation texture, boolean displayHandheld,
|
||||
Optional<ResolvedModel> customModel) {
|
||||
GeyserBaseDefinition definition = new GeyserBaseDefinition(bedrockIdentifier, Optional.of(displayName), predicateStack,
|
||||
new GeyserBaseDefinition.BedrockOptions(Optional.empty(), true, displayHandheld, protectionValue), componentPatch);
|
||||
GeyserBaseDefinition base = new GeyserBaseDefinition(bedrockIdentifier, Optional.of(stack.getHoverName().getString()), predicateStack,
|
||||
new GeyserBaseDefinition.BedrockOptions(Optional.empty(), true, displayHandheld, calculateProtectionValue(stack)), stack.getComponentsPatch());
|
||||
try {
|
||||
mappingConsumer.accept(definition);
|
||||
packContext.mappings().map(stack.getItemHolder(), definitionCreator.apply(base));
|
||||
} catch (Exception exception) {
|
||||
reporter.forChild(() -> "mapping with bedrock identifier " + bedrockIdentifier + " ").report(() -> "failed to pass mapping: " + exception.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO Should probably get a better way to get geometry texture
|
||||
String safeIdentifier = definition.textureName();
|
||||
String safeIdentifier = base.textureName();
|
||||
String bone = "bone";
|
||||
ResourceLocation geometryTexture = texture;
|
||||
Optional<BedrockGeometryContext> bedrockGeometry = customModel.map(model -> GeometryMapper.mapGeometry(safeIdentifier, bone, model, geometryTexture));
|
||||
@@ -223,18 +227,26 @@ public class BedrockItemMapper {
|
||||
|
||||
boolean exportTexture = true;
|
||||
if (customModel.isPresent()) {
|
||||
ItemStack fakeItem = new ItemStack(Items.FLINT);
|
||||
//fakeItem.set(DataComponents.ITEM_MODEL, model); TODO
|
||||
|
||||
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;
|
||||
additionalTextureConsumer.accept(geometryTexture);
|
||||
packContext.additionalTextureConsumer().accept(geometryTexture);
|
||||
}
|
||||
|
||||
itemConsumer.accept(new BedrockItem(bedrockIdentifier, definition.textureName(), texture, exportTexture,
|
||||
AttachableMapper.mapItem(componentPatch, bedrockIdentifier, bedrockGeometry, bedrockAnimation, additionalTextureConsumer),
|
||||
packContext.itemConsumer().accept(new BedrockItem(bedrockIdentifier, base.textureName(), texture, exportTexture,
|
||||
AttachableMapper.mapItem(stack.getComponentsPatch(), bedrockIdentifier, bedrockGeometry, bedrockAnimation, packContext.additionalTextureConsumer()),
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
src/main/java/org/geysermc/rainbow/mapping/PackContext.java
Normal file
10
src/main/java/org/geysermc/rainbow/mapping/PackContext.java
Normal 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) {
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
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.minecraft.client.Minecraft;
|
||||
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.StringUtil;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.CustomModelData;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.geysermc.rainbow.CodecUtil;
|
||||
import org.geysermc.rainbow.PackConstants;
|
||||
import org.geysermc.rainbow.Rainbow;
|
||||
import org.geysermc.rainbow.mapping.BedrockItemMapper;
|
||||
import org.geysermc.rainbow.mapping.PackContext;
|
||||
import org.geysermc.rainbow.mapping.geyser.GeyserMappings;
|
||||
import org.geysermc.rainbow.mixin.SplashRendererAccessor;
|
||||
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!",
|
||||
"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.",
|
||||
"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 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<ResourceLocation> texturesToExport = new HashSet<>();
|
||||
private final Set<ResourceLocation> modelsMapped = new HashSet<>();
|
||||
private final IntSet customModelDataMapped = new IntOpenHashSet();
|
||||
|
||||
private final ProblemReporter.Collector reporter;
|
||||
|
||||
@@ -90,16 +96,6 @@ public class BedrockPack {
|
||||
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();
|
||||
ProblemReporter mapReporter = new ProblemReporter() {
|
||||
|
||||
@@ -114,14 +110,34 @@ public class BedrockPack {
|
||||
reporter.report(problem);
|
||||
}
|
||||
};
|
||||
|
||||
BedrockItemMapper.tryMapStack(stack, model, mapReporter, mappings, packPath, bedrockItem -> {
|
||||
itemTextures.withItemTexture(bedrockItem);
|
||||
if (bedrockItem.exportTexture()) {
|
||||
texturesToExport.add(bedrockItem.texture());
|
||||
PackContext context = new PackContext(mappings, packPath, item -> {
|
||||
itemTextures.withItemTexture(item);
|
||||
if (item.exportTexture()) {
|
||||
texturesToExport.add(item.texture());
|
||||
}
|
||||
bedrockItems.add(bedrockItem);
|
||||
bedrockItems.add(item);
|
||||
}, 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"ModelManagerMixin",
|
||||
"PictureInPictureRendererAccessor",
|
||||
"PictureInPictureRendererMixin",
|
||||
"RangeSelectItemModelAccessor",
|
||||
"SelectItemModelAccessor",
|
||||
"SelectItemModelMixin",
|
||||
"SelectItemModelMixin$UnbakedSwitchMixin",
|
||||
|
||||
Reference in New Issue
Block a user