diff --git a/src/main/java/org/geysermc/packgenerator/GeyserMappingsGenerator.java b/src/main/java/org/geysermc/packgenerator/GeyserMappingsGenerator.java index 473ebed..7eb38f7 100644 --- a/src/main/java/org/geysermc/packgenerator/GeyserMappingsGenerator.java +++ b/src/main/java/org/geysermc/packgenerator/GeyserMappingsGenerator.java @@ -21,6 +21,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer { private final PackManager packManager = new PackManager(); private final PackMapper packMapper = new PackMapper(packManager); + // TODO export language overrides @Override public void onInitializeClient() { ClientCommandRegistrationCallback.EVENT.register((dispatcher, buildContext) -> PackGeneratorCommand.register(dispatcher, packManager, packMapper)); diff --git a/src/main/java/org/geysermc/packgenerator/accessor/BlockModelWrapperLocationAccessor.java b/src/main/java/org/geysermc/packgenerator/accessor/BlockModelWrapperLocationAccessor.java index dd0353c..fe0eb13 100644 --- a/src/main/java/org/geysermc/packgenerator/accessor/BlockModelWrapperLocationAccessor.java +++ b/src/main/java/org/geysermc/packgenerator/accessor/BlockModelWrapperLocationAccessor.java @@ -2,7 +2,7 @@ package org.geysermc.packgenerator.accessor; import net.minecraft.resources.ResourceLocation; -// Implemented on BlockModelWrapper, since this class doesn't store its model, we have to store it manually +// Implemented on BlockModelWrapper, since this class doesn't store its model after baking, we have to store it manually public interface BlockModelWrapperLocationAccessor { ResourceLocation geyser_mappings_generator$getModelOrigin(); diff --git a/src/main/java/org/geysermc/packgenerator/accessor/ResolvedModelAccessor.java b/src/main/java/org/geysermc/packgenerator/accessor/ResolvedModelAccessor.java new file mode 100644 index 0000000..bd26077 --- /dev/null +++ b/src/main/java/org/geysermc/packgenerator/accessor/ResolvedModelAccessor.java @@ -0,0 +1,12 @@ +package org.geysermc.packgenerator.accessor; + +import net.minecraft.client.resources.model.ResolvedModel; +import net.minecraft.resources.ResourceLocation; + +import java.util.Optional; + +// Implemented on ModelManager, since this class doesn't keep the resolved models after baking, we have to store it manually +public interface ResolvedModelAccessor { + + Optional geyser_mappings_generator$getResolvedModel(ResourceLocation location); +} diff --git a/src/main/java/org/geysermc/packgenerator/accessor/SelectItemModelCasesAccessor.java b/src/main/java/org/geysermc/packgenerator/accessor/SelectItemModelCasesAccessor.java index 596c470..86ef6fb 100644 --- a/src/main/java/org/geysermc/packgenerator/accessor/SelectItemModelCasesAccessor.java +++ b/src/main/java/org/geysermc/packgenerator/accessor/SelectItemModelCasesAccessor.java @@ -3,7 +3,7 @@ package org.geysermc.packgenerator.accessor; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import net.minecraft.client.renderer.item.ItemModel; -// Implemented on BlockModelWrapper, since this class doesn't store the cases it has in a nice format, we have to store it manually +// Implemented on BlockModelWrapper, since this class doesn't store the cases it has in a nice format after baking, we have to store it manually public interface SelectItemModelCasesAccessor { Object2ObjectMap geyser_mappings_generator$getCases(); diff --git a/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserItemMapper.java b/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserItemMapper.java index fe458b6..5d0eeef 100644 --- a/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserItemMapper.java +++ b/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserItemMapper.java @@ -24,6 +24,7 @@ import net.minecraft.world.item.CrossbowItem; import net.minecraft.world.item.equipment.trim.TrimMaterial; import net.minecraft.world.level.Level; import org.geysermc.packgenerator.accessor.BlockModelWrapperLocationAccessor; +import org.geysermc.packgenerator.accessor.ResolvedModelAccessor; import org.geysermc.packgenerator.accessor.SelectItemModelCasesAccessor; import org.geysermc.packgenerator.mapping.geyser.predicate.GeyserConditionPredicate; import org.geysermc.packgenerator.mapping.geyser.predicate.GeyserMatchPredicate; @@ -41,18 +42,27 @@ public class GeyserItemMapper { public static Stream mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch, ProblemReporter reporter) { ItemModel model = Minecraft.getInstance().getModelManager().getItemModel(modelLocation); - MappingContext context = new MappingContext(List.of(), modelLocation, displayName, protectionValue, componentPatch, reporter.forChild(() -> "model " + modelLocation + " ")); + MappingContext context = new MappingContext(List.of(), modelLocation, displayName, protectionValue, componentPatch, reporter.forChild(() -> "client item definition " + modelLocation + " ")); return mapItem(model, context); } private static Stream mapItem(ItemModel model, MappingContext context) { switch (model) { case BlockModelWrapper modelWrapper -> { - ResourceLocation itemModel = ((BlockModelWrapperLocationAccessor) modelWrapper).geyser_mappings_generator$getModelOrigin(); - if (itemModel.getNamespace().equals(ResourceLocation.DEFAULT_NAMESPACE)) { - itemModel = ResourceLocation.fromNamespaceAndPath("geyser_mc", itemModel.getPath()); - } - return Stream.of(context.create(itemModel)); + ResourceLocation itemModelLocation = ((BlockModelWrapperLocationAccessor) modelWrapper).geyser_mappings_generator$getModelOrigin(); + + return ((ResolvedModelAccessor) Minecraft.getInstance().getModelManager()).geyser_mappings_generator$getResolvedModel(itemModelLocation) + .map(itemModel -> { + ResourceLocation bedrockIdentifier = itemModelLocation; + if (bedrockIdentifier.getNamespace().equals(ResourceLocation.DEFAULT_NAMESPACE)) { + bedrockIdentifier = ResourceLocation.fromNamespaceAndPath("geyser_mc", itemModelLocation.getPath()); + } + return Stream.of(context.create(bedrockIdentifier)); + }) + .orElseGet(() -> { + context.reporter.report(() -> "missing block model " + itemModelLocation); + return Stream.empty(); + }); } case ConditionalItemModel conditional -> { return mapConditionalModel(conditional, context.child("condition " + conditional + " ")); diff --git a/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserSingleDefinition.java b/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserSingleDefinition.java index eec98de..0532cda 100644 --- a/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserSingleDefinition.java +++ b/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserSingleDefinition.java @@ -15,6 +15,7 @@ import java.util.function.Function; // TODO other keys, etc. // TODO sometimes still includes components key when patch before filtering is not empty but after is +// TODO display name can be a component public record GeyserSingleDefinition(Optional model, ResourceLocation bedrockIdentifier, Optional displayName, List predicates, BedrockOptions bedrockOptions, DataComponentPatch components) implements GeyserMapping { private static final List> SUPPORTED_COMPONENTS = List.of(DataComponents.CONSUMABLE, DataComponents.EQUIPPABLE, DataComponents.FOOD, diff --git a/src/main/java/org/geysermc/packgenerator/mixin/ModelManagerMixin.java b/src/main/java/org/geysermc/packgenerator/mixin/ModelManagerMixin.java new file mode 100644 index 0000000..528ddd5 --- /dev/null +++ b/src/main/java/org/geysermc/packgenerator/mixin/ModelManagerMixin.java @@ -0,0 +1,41 @@ +package org.geysermc.packgenerator.mixin; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.client.resources.model.ResolvedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.PreparableReloadListener; +import org.geysermc.packgenerator.accessor.ResolvedModelAccessor; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; + +import java.lang.reflect.InvocationTargetException; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +@Mixin(ModelManager.class) +public abstract class ModelManagerMixin implements PreparableReloadListener, AutoCloseable, ResolvedModelAccessor { + @Unique + private Map unbakedResolvedModels; + + @WrapOperation(method = "method_65753", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;join()Ljava/lang/Object;", ordinal = 0)) + private static Object setResolvedModels(CompletableFuture instance, Operation original) { + Object resolved = original.call(instance); + try { + // Couldn't be bothered setting up access wideners, this resolves the second component of the ResolvedModels record, which is called "models" + ((ModelManagerMixin) (Object) Minecraft.getInstance().getModelManager()).unbakedResolvedModels = (Map) resolved.getClass().getRecordComponents()[1].getAccessor().invoke(resolved); + } catch (IllegalAccessException | InvocationTargetException | ClassCastException exception) { + throw new RuntimeException(exception); + } + return resolved; + } + + @Override + public Optional geyser_mappings_generator$getResolvedModel(ResourceLocation location) { + return unbakedResolvedModels == null ? Optional.empty() : Optional.ofNullable(unbakedResolvedModels.get(location)); + } +} diff --git a/src/main/resources/geyser-mappings-generator.mixins.json b/src/main/resources/geyser-mappings-generator.mixins.json index 9d4aeee..968ff39 100644 --- a/src/main/resources/geyser-mappings-generator.mixins.json +++ b/src/main/resources/geyser-mappings-generator.mixins.json @@ -9,6 +9,7 @@ "BlockModelWrapperMixin$UnbakedMixin", "ConditionalItemModelAccessor", "EntityRenderDispatcherAccessor", + "ModelManagerMixin", "SelectItemModelAccessor", "SelectItemModelMixin", "SelectItemModelMixin$UnbakedSwitchMixin",