diff --git a/src/main/java/org/geysermc/packgenerator/mapping/attachable/AttachableMapper.java b/src/main/java/org/geysermc/packgenerator/mapping/attachable/AttachableMapper.java new file mode 100644 index 0000000..92400be --- /dev/null +++ b/src/main/java/org/geysermc/packgenerator/mapping/attachable/AttachableMapper.java @@ -0,0 +1,40 @@ +package org.geysermc.packgenerator.mapping.attachable; + +import com.mojang.datafixers.util.Pair; +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.model.EquipmentAssetManager; +import net.minecraft.client.resources.model.EquipmentClientInfo; +import net.minecraft.core.component.DataComponents; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import org.geysermc.packgenerator.mixin.EntityRenderDispatcherAccessor; +import org.geysermc.packgenerator.pack.attachable.BedrockAttachable; + +import java.util.List; +import java.util.Optional; + +public class AttachableMapper { + + public static Optional mapItem(ItemStack stack, ResourceLocation bedrockIdentifier) { + // Crazy optional statement + return Optional.ofNullable(stack.get(DataComponents.EQUIPPABLE)) + .flatMap(equippable -> { + EquipmentAssetManager equipmentAssets = ((EntityRenderDispatcherAccessor) Minecraft.getInstance().getEntityRenderDispatcher()).getEquipmentAssets(); + return equippable.assetId().map(asset -> Pair.of(equippable.slot(), equipmentAssets.get(asset))); + }) + .filter(assetInfo -> assetInfo.getSecond() != EquipmentAssetManager.MISSING) + .map(assetInfo -> assetInfo + .mapSecond(info -> info.getLayers(getLayer(assetInfo.getFirst())))) + .filter(assetInfo -> !assetInfo.getSecond().isEmpty()) + .map(assetInfo -> BedrockAttachable.equipment(bedrockIdentifier, assetInfo.getFirst(), getTexture(assetInfo.getSecond(), getLayer(assetInfo.getFirst())))); + } + + private static EquipmentClientInfo.LayerType getLayer(EquipmentSlot slot) { + return slot == EquipmentSlot.LEGS ? EquipmentClientInfo.LayerType.HUMANOID_LEGGINGS : EquipmentClientInfo.LayerType.HUMANOID; + } + + private static String getTexture(List info, EquipmentClientInfo.LayerType layer) { + return "entity/equipment/" + layer.getSerializedName() + "/" + info.getFirst().textureId().getPath(); + } +} diff --git a/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserMapping.java b/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserMapping.java index be8f22b..189e9b9 100644 --- a/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserMapping.java +++ b/src/main/java/org/geysermc/packgenerator/mapping/geyser/GeyserMapping.java @@ -49,7 +49,7 @@ public record GeyserMapping(ResourceLocation model, ResourceLocation bedrockIden return bedrockOptions.icon.orElse(iconFromResourceLocation(bedrockIdentifier)); } - private static String iconFromResourceLocation(ResourceLocation location) { + public static String iconFromResourceLocation(ResourceLocation location) { return location.toString().replace(':', '.').replace('/', '_'); } diff --git a/src/main/java/org/geysermc/packgenerator/mixin/EntityRenderDispatcherAccessor.java b/src/main/java/org/geysermc/packgenerator/mixin/EntityRenderDispatcherAccessor.java new file mode 100644 index 0000000..1168069 --- /dev/null +++ b/src/main/java/org/geysermc/packgenerator/mixin/EntityRenderDispatcherAccessor.java @@ -0,0 +1,13 @@ +package org.geysermc.packgenerator.mixin; + +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.client.resources.model.EquipmentAssetManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EntityRenderDispatcher.class) +public interface EntityRenderDispatcherAccessor { + + @Accessor + EquipmentAssetManager getEquipmentAssets(); +} diff --git a/src/main/java/org/geysermc/packgenerator/pack/BedrockPack.java b/src/main/java/org/geysermc/packgenerator/pack/BedrockPack.java index 79ac6f7..7c70dae 100644 --- a/src/main/java/org/geysermc/packgenerator/pack/BedrockPack.java +++ b/src/main/java/org/geysermc/packgenerator/pack/BedrockPack.java @@ -4,6 +4,7 @@ import net.fabricmc.loader.api.FabricLoader; import net.minecraft.world.item.ItemStack; import org.geysermc.packgenerator.CodecUtil; import org.geysermc.packgenerator.PackConstants; +import org.geysermc.packgenerator.mapping.attachable.AttachableMapper; import org.geysermc.packgenerator.mapping.geyser.GeyserMappings; import org.geysermc.packgenerator.pack.attachable.BedrockAttachable; @@ -16,6 +17,7 @@ import java.util.UUID; public class BedrockPack { private static final Path EXPORT_DIRECTORY = FabricLoader.getInstance().getGameDir().resolve("geyser"); private static final Path PACK_DIRECTORY = Path.of("pack"); + private static final Path ATTACHABLES_DIRECTORY = Path.of("attachables"); private static final Path MAPPINGS_FILE = Path.of("geyser_mappings.json"); private static final Path MANIFEST_FILE = Path.of("manifest.json"); @@ -44,13 +46,19 @@ public class BedrockPack { } public void map(ItemStack stack) { - mappings.map(stack, mapping -> itemTextures.withItemTexture(mapping, mapping.bedrockIdentifier().getPath())); + mappings.map(stack, mapping -> { + itemTextures.withItemTexture(mapping, mapping.bedrockIdentifier().getPath()); + AttachableMapper.mapItem(stack, mapping.bedrockIdentifier()).ifPresent(attachables::add); + }); } public void save() throws IOException { CodecUtil.trySaveJson(GeyserMappings.CODEC, mappings, exportPath.resolve(MAPPINGS_FILE)); CodecUtil.trySaveJson(PackManifest.CODEC, manifest, packPath.resolve(MANIFEST_FILE)); CodecUtil.trySaveJson(BedrockTextureAtlas.CODEC, BedrockTextureAtlas.itemAtlas(name, itemTextures), packPath.resolve(ITEM_ATLAS_FILE)); + for (BedrockAttachable attachable : attachables) { + attachable.save(packPath.resolve(ATTACHABLES_DIRECTORY)); + } } private static Path createPackDirectory(String name) throws IOException { diff --git a/src/main/java/org/geysermc/packgenerator/pack/BedrockVersion.java b/src/main/java/org/geysermc/packgenerator/pack/BedrockVersion.java index 085b533..b4666c3 100644 --- a/src/main/java/org/geysermc/packgenerator/pack/BedrockVersion.java +++ b/src/main/java/org/geysermc/packgenerator/pack/BedrockVersion.java @@ -10,7 +10,7 @@ public record BedrockVersion(int major, int minor, int patch) { .xmap(list -> BedrockVersion.of(list.getFirst(), list.get(1), list.getLast()), version -> List.of(version.major, version.minor, version.patch)); public static final Codec STRING_CODEC = Codec.STRING.comapFlatMap(string -> { - String[] segments = string.split("\."); + String[] segments = string.split("\\."); if (segments.length != 3) { return DataResult.error(() -> "Semantic version must consist of 3 versions"); } diff --git a/src/main/java/org/geysermc/packgenerator/pack/attachable/BedrockAttachable.java b/src/main/java/org/geysermc/packgenerator/pack/attachable/BedrockAttachable.java index 7bf7ebf..93169a3 100644 --- a/src/main/java/org/geysermc/packgenerator/pack/attachable/BedrockAttachable.java +++ b/src/main/java/org/geysermc/packgenerator/pack/attachable/BedrockAttachable.java @@ -5,11 +5,15 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.StringRepresentable; import net.minecraft.world.entity.EquipmentSlot; +import org.geysermc.packgenerator.CodecUtil; import org.geysermc.packgenerator.PackConstants; +import org.geysermc.packgenerator.mapping.geyser.GeyserMapping; import org.geysermc.packgenerator.pack.BedrockTextures; import org.geysermc.packgenerator.pack.BedrockVersion; import org.jetbrains.annotations.NotNull; +import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.EnumMap; import java.util.HashMap; @@ -39,6 +43,11 @@ public record BedrockAttachable(BedrockVersion formatVersion, AttachableInfo inf .build(); } + public void save(Path attachablesPath) throws IOException { + // Get a save attachable path by using Geyser's way of getting icons + CodecUtil.trySaveJson(CODEC, this, attachablesPath.resolve(GeyserMapping.iconFromResourceLocation(info.identifier))); + } + public static class Builder { private final ResourceLocation identifier; private final EnumMap materials = new EnumMap<>(DisplaySlot.class); diff --git a/src/main/resources/geyser-mappings-generator.mixins.json b/src/main/resources/geyser-mappings-generator.mixins.json index 391cd9a..13f0d14 100644 --- a/src/main/resources/geyser-mappings-generator.mixins.json +++ b/src/main/resources/geyser-mappings-generator.mixins.json @@ -8,6 +8,7 @@ "BlockModelWrapperMixin", "BlockModelWrapperMixin$UnbakedMixin", "ConditionalItemModelAccessor", + "EntityRenderDispatcherAccessor", "SelectItemModelAccessor", "SelectItemModelMixin", "SelectItemModelMixin$UnbakedSwitchMixin"