diff --git a/src/main/java/com/minelittlepony/api/model/armour/IArmour.java b/src/main/java/com/minelittlepony/api/model/armour/IArmour.java index f3785e71..4bdcbb4c 100644 --- a/src/main/java/com/minelittlepony/api/model/armour/IArmour.java +++ b/src/main/java/com/minelittlepony/api/model/armour/IArmour.java @@ -3,8 +3,16 @@ package com.minelittlepony.api.model.armour; import net.minecraft.item.Item; import net.minecraft.util.registry.Registry; -import com.minelittlepony.api.model.IModelWrapper; +import org.jetbrains.annotations.Nullable; +import com.minelittlepony.api.model.IModelWrapper; +import com.minelittlepony.api.pony.IPonyData; + +/** + * Wrapper for an armour model and texture. + * + * @param The type of the contained armour model. + */ public interface IArmour extends IModelWrapper { /** * Registers a custom armour for the supplied item. @@ -19,7 +27,10 @@ public interface IArmour extends IModelWrapper { /** * Gets the armour model to render for the given layer. + *

+ * Return null to preserve the default behaviour or override and return your custom model. */ + @Nullable V getModel(ArmourLayer layer); /** @@ -30,4 +41,9 @@ public interface IArmour extends IModelWrapper { default IArmourTextureResolver getTextureResolver(IArmourTextureResolver defaultResolver) { return defaultResolver; } + + @Override + default IArmour applyMetadata(IPonyData meta) { + return this; + } } diff --git a/src/main/java/com/minelittlepony/api/model/armour/IArmourTextureResolver.java b/src/main/java/com/minelittlepony/api/model/armour/IArmourTextureResolver.java index 196dcd63..cbd7158f 100644 --- a/src/main/java/com/minelittlepony/api/model/armour/IArmourTextureResolver.java +++ b/src/main/java/com/minelittlepony/api/model/armour/IArmourTextureResolver.java @@ -7,6 +7,11 @@ import net.minecraft.util.Identifier; import org.jetbrains.annotations.Nullable; +/** + * A resolver for looking up the texture for a piece of armour. + *

+ * This is for modders who want to override the default implementation found in {@link DefaultArmourTextureResolver}. + */ public interface IArmourTextureResolver { /** diff --git a/src/main/java/com/minelittlepony/client/model/armour/DefaultArmourTextureResolver.java b/src/main/java/com/minelittlepony/client/model/armour/DefaultArmourTextureResolver.java index 4e0917be..310c45a2 100644 --- a/src/main/java/com/minelittlepony/client/model/armour/DefaultArmourTextureResolver.java +++ b/src/main/java/com/minelittlepony/client/model/armour/DefaultArmourTextureResolver.java @@ -1,14 +1,16 @@ package com.minelittlepony.client.model.armour; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.texture.TextureManager; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.EquipmentSlot; import net.minecraft.item.ArmorItem; import net.minecraft.item.ItemStack; -import net.minecraft.resource.ResourceManager; +import net.minecraft.nbt.NbtElement; import net.minecraft.util.Identifier; - import com.google.common.base.Strings; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.minelittlepony.api.model.armour.ArmourLayer; import com.minelittlepony.api.model.armour.ArmourVariant; import com.minelittlepony.api.model.armour.IArmourTextureResolver; @@ -16,96 +18,109 @@ import com.minelittlepony.util.ResourceUtil; import org.jetbrains.annotations.Nullable; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +/** + * The default texture resolver used by Mine Little Pony. + *

+ * The search order is as follows: + *

+ * namespace:textures/models/armor/material_layer_[outer|inner](_overlay)(_custom_[0-9]+)_pony.png + * namespace:textures/models/armor/material_layer_[outer|inner](_overlay)(_custom_[0-9]+).png + * namespace:textures/models/armor/material_layer_[1|2](_overlay)(_custom_[0-9]+)_pony.png + * namespace:textures/models/armor/material_layer_[1|2](_overlay)(_custom_[0-9]+).png + * namespace:textures/models/armor/material_layer_[outer|inner](_overlay)_pony.png + * namespace:textures/models/armor/material_layer_[outer|inner](_overlay).png + * namespace:textures/models/armor/material_layer_[1|2](_overlay)_pony.png + * namespace:textures/models/armor/material_layer_[1|2](_overlay).png + *

+ * - if namespace is "minecraft" will be rewritten to "minelittlepony" + *

+ * For how modders can customise both the model and texture please refer to {@link IArmour} and {@link IArmourTextureResolver}. + * + * @see IArmour + * @see IArmourTextureResolver + */ public class DefaultArmourTextureResolver implements IArmourTextureResolver { - private final Map HUMAN_ARMOUR = new HashMap<>(); - private final Map PONY_ARMOUR = new HashMap<>(); + private final Cache cache = CacheBuilder.newBuilder() + .expireAfterAccess(30, TimeUnit.SECONDS) + .build(); @Override - public Identifier getTexture(LivingEntity entity, ItemStack itemstack, EquipmentSlot slot, ArmourLayer layer, @Nullable String type) { - type = Strings.nullToEmpty(type); + public Identifier getTexture(LivingEntity entity, ItemStack stack, EquipmentSlot slot, ArmourLayer layer, @Nullable String type) { + Identifier material = new Identifier(((ArmorItem) stack.getItem()).getMaterial().getName()); + String custom = getCustom(stack); - ArmorItem item = (ArmorItem) itemstack.getItem(); - String texture = item.getMaterial().getName(); + try { + return cache.get(String.format("%s#%s#%s#%s", material, layer, type, custom), () -> { + String typed = Strings.nullToEmpty(type); + String extra = typed.isEmpty() ? "" : "_" + typed; - String domain = "minecraft"; + Identifier texture; - int idx = texture.indexOf(':'); - if (idx > -1) { - domain = texture.substring(0, idx); - texture = texture.substring(idx + 1); + if (!"none".equals(custom)) { + texture = resolveNewOrOld(material, layer, custom + extra, typed); + if (texture != null) { + return texture; + } + } + + texture = resolveNewOrOld(material, layer, extra, typed); + if (texture != null) { + return texture; + } + + return TextureManager.MISSING_IDENTIFIER; + }); + } catch (ExecutionException ignored) { + return TextureManager.MISSING_IDENTIFIER; + } + } + + private String getCustom(ItemStack stack) { + if (stack.hasTag() && stack.getTag().contains("CustomModelData", NbtElement.NUMBER_TYPE)) { + return "custom_" + stack.getTag().getInt("CustomModelData"); + } + return "none"; + } + + @Nullable + private Identifier resolveNewOrOld(Identifier material, ArmourLayer layer, String extra, String type) { + Identifier texture = resolveHumanOrPony(ResourceUtil.format("%s:textures/models/armor/%s_layer_%s%s.png", material.getNamespace(), material.getPath(), layer, extra), type); + + if (texture != null) { + return texture; } - String customType = type.isEmpty() ? "" : "_" + type; - - String res = ResourceUtil.format("%s:textures/models/armor/%s_layer_%s%s.png", domain, texture, layer, customType); - String oldRes = ResourceUtil.format("%s:textures/models/armor/%s_layer_%d%s.png", domain, texture, layer.getLegacyId(), customType); - - Identifier human = getArmorTexture(res, type); - Identifier pony = ponifyResource(human); - - Identifier oldHuman = getArmorTexture(oldRes, type); - Identifier oldPony = ponifyResource(oldHuman); - - return resolve(oldPony, pony, oldHuman, human); + return resolveHumanOrPony(ResourceUtil.format("%s:textures/models/armor/%s_layer_%d%s.png", material.getNamespace(), material.getPath(), layer.getLegacyId(), extra), type); } - private Identifier resolve(Identifier... resources) { - // check resource packs for either texture. + @Nullable + private Identifier resolveHumanOrPony(String res, String type) { + Identifier human = new Identifier(res); - ResourceManager manager = MinecraftClient.getInstance().getResourceManager(); - - for (Identifier i : resources) { - if (manager.containsResource(i)) { - return i; - } + String domain = human.getNamespace(); + if ("minecraft".equals(domain)) { + domain = "minelittlepony"; // it's a vanilla armor. I provide these. } - return resources[resources.length - 1]; + Identifier pony = new Identifier(domain, human.getPath().replace(".png", "_pony.png")); + + if (isValid(pony)) { + return pony; + } + + if (isValid(human)) { + return human; + } + + return null; } - private Identifier ponifyResource(Identifier human) { - return PONY_ARMOUR.computeIfAbsent(human, key -> { - String domain = key.getNamespace(); - if ("minecraft".equals(domain)) { - domain = "minelittlepony"; // it's a vanilla armor. I provide these. - } - - return new Identifier(domain, key.getPath().replace(".png", "_pony.png")); - }); - } - - private Identifier getArmorTexture(String def, String type) { - return HUMAN_ARMOUR.computeIfAbsent(def + "#" + type, s -> { - Identifier defId = new Identifier(def); - - if (type.isEmpty() || type.equals(def)) { - return defId; - } - - Identifier modId = new Identifier(type); - - Path modPath = Paths.get(modId.getPath()).getParent(); - - if (modPath == null) { - return defId; - } - - Path path = modPath.resolve(Paths.get(defId.getPath()).getFileName()); - - Identifier interemId = new Identifier(modId.getNamespace(), path.toString().replace('\\', '/')); - - if (MinecraftClient.getInstance().getResourceManager().containsResource(interemId)) { - return interemId; - } - - return modId; - }); + private final boolean isValid(Identifier texture) { + return MinecraftClient.getInstance().getResourceManager().containsResource(texture); } @Override diff --git a/src/main/java/com/minelittlepony/client/render/entity/feature/ArmourFeature.java b/src/main/java/com/minelittlepony/client/render/entity/feature/ArmourFeature.java index 8d9ad9ed..e56c6fe5 100644 --- a/src/main/java/com/minelittlepony/client/render/entity/feature/ArmourFeature.java +++ b/src/main/java/com/minelittlepony/client/render/entity/feature/ArmourFeature.java @@ -62,6 +62,9 @@ public class ArmourFeature & IP armour.applyMetadata(pony.getBody().getMetadata()); V model = armour.getModel(layer); + if (model == null) { + model = pony.getArmor().getModel(layer); + } if (model.prepareToRender(armorSlot, layer)) { pony.getBody().copyAttributes(model); diff --git a/src/main/java/com/minelittlepony/client/render/entity/npc/VillagerPonyRenderer.java b/src/main/java/com/minelittlepony/client/render/entity/npc/VillagerPonyRenderer.java index 38432d6e..0008fe3a 100644 --- a/src/main/java/com/minelittlepony/client/render/entity/npc/VillagerPonyRenderer.java +++ b/src/main/java/com/minelittlepony/client/render/entity/npc/VillagerPonyRenderer.java @@ -21,5 +21,4 @@ public class VillagerPonyRenderer extends AbstractNpcRenderer