More changes to texture suppliers

This commit is contained in:
Sollace 2022-12-15 00:35:30 +00:00
parent 96cfd13b96
commit 8c1a705b9e
12 changed files with 227 additions and 215 deletions

View file

@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit;
public class PonyManager implements IPonyManager, SimpleSynchronousResourceReloadListener {
private static final Identifier ID = new Identifier("minelittlepony", "background_ponies");
public static final Identifier BACKGROUND_PONIES = new Identifier("minelittlepony", "textures/entity/pony");
public static final Identifier BACKGROUND_ZOMPONIES = new Identifier("minelittlepony", "textures/entity/zompony");
private final PonyConfig config;

View file

@ -28,10 +28,10 @@ abstract class AbstractNpcRenderer<T extends MobEntity & VillagerDataContainer>
private final NpcClothingFeature<T, ClientPonyModel<T>, AbstractNpcRenderer<T>> clothing;
public AbstractNpcRenderer(EntityRendererFactory.Context context, String type, TextureSupplier<String> formatter) {
public AbstractNpcRenderer(EntityRendererFactory.Context context, String type, TextureSupplier<T> textureSupplier, TextureSupplier<String> formatter) {
super(context, ModelType.getPlayerModel(Race.EARTH).getKey(false));
entityType = type;
baseTextures = new SillyPonyTextures<>(new CustomPonyTextures<>(new PonyTextures<>(formatter)), formatter);
baseTextures = new SillyPonyTextureSupplier<>(textureSupplier, formatter);
clothing = new NpcClothingFeature<>(this, entityType);
addFeature(clothing);
}
@ -39,7 +39,7 @@ abstract class AbstractNpcRenderer<T extends MobEntity & VillagerDataContainer>
@Override
public boolean shouldRender(ClientPonyModel<T> model, T entity, Wearable wearable, IGear gear) {
boolean special = PonyTextures.isBestPony(entity);
boolean special = SillyPonyTextureSupplier.isBestPony(entity);
if (wearable == Wearable.SADDLE_BAGS_BOTH) {
VillagerProfession profession = entity.getVillagerData().getProfession();
@ -52,7 +52,7 @@ abstract class AbstractNpcRenderer<T extends MobEntity & VillagerDataContainer>
}
if (wearable == Wearable.MUFFIN) {
return PonyTextures.isCrownPony(entity);
return SillyPonyTextureSupplier.isCrownPony(entity);
}
return super.shouldRender(model, entity, wearable, gear);
@ -85,6 +85,6 @@ abstract class AbstractNpcRenderer<T extends MobEntity & VillagerDataContainer>
@Override
public Identifier getTexture(T villager) {
return baseTextures.supplyTexture(villager);
return baseTextures.apply(villager);
}
}

View file

@ -6,8 +6,8 @@ import net.minecraft.entity.passive.VillagerEntity;
import net.minecraft.util.math.MathHelper;
import com.minelittlepony.client.model.ClientPonyModel;
import com.minelittlepony.client.render.entity.npc.textures.PonyTextures;
import com.minelittlepony.client.render.entity.npc.textures.TextureSupplier;
import com.minelittlepony.client.pony.PonyManager;
import com.minelittlepony.client.render.entity.npc.textures.*;
public class VillagerPonyRenderer extends AbstractNpcRenderer<VillagerEntity> {
@ -15,13 +15,15 @@ public class VillagerPonyRenderer extends AbstractNpcRenderer<VillagerEntity> {
private static final TextureSupplier<String> FORMATTER = TextureSupplier.formatted("minelittlepony", "textures/entity/villager/%s.png");
public VillagerPonyRenderer(EntityRendererFactory.Context context) {
super(context, TYPE, FORMATTER);
super(context, TYPE,
TextureSupplier.ofPool(PonyManager.BACKGROUND_PONIES,
PlayerTextureSupplier.create(ProfessionTextureSupplier.create(FORMATTER))), FORMATTER);
}
@Override
protected void initializeModel(ClientPonyModel<VillagerEntity> model) {
model.onSetModelAngles((m, move, swing, ticks, entity) -> {
m.getAttributes().visualHeight += PonyTextures.isCrownPony(entity) ? 0.3F : -0.1F;
m.getAttributes().visualHeight += SillyPonyTextureSupplier.isCrownPony(entity) ? 0.3F : -0.1F;
boolean isHeadRolling = entity instanceof MerchantEntity && ((MerchantEntity)entity).getHeadRollingTimeLeft() > 0;

View file

@ -7,8 +7,8 @@ import net.minecraft.entity.mob.ZombieVillagerEntity;
import com.minelittlepony.client.model.ClientPonyModel;
import com.minelittlepony.client.model.IMobModel;
import com.minelittlepony.client.render.entity.npc.textures.PonyTextures;
import com.minelittlepony.client.render.entity.npc.textures.TextureSupplier;
import com.minelittlepony.client.pony.PonyManager;
import com.minelittlepony.client.render.entity.npc.textures.*;
public class ZomponyVillagerRenderer extends AbstractNpcRenderer<ZombieVillagerEntity> {
@ -16,13 +16,17 @@ public class ZomponyVillagerRenderer extends AbstractNpcRenderer<ZombieVillagerE
private static final TextureSupplier<String> FORMATTER = TextureSupplier.formatted("minelittlepony", "textures/entity/zombie_villager/zombie_%s.png");
public ZomponyVillagerRenderer(EntityRendererFactory.Context context) {
super(context, TYPE, FORMATTER);
super(context, TYPE,
TextureSupplier.ofPool(PonyManager.BACKGROUND_ZOMPONIES,
TextureSupplier.ofPool(PonyManager.BACKGROUND_PONIES,
PlayerTextureSupplier.create(ProfessionTextureSupplier.create(FORMATTER)))),
FORMATTER);
}
@Override
protected void initializeModel(ClientPonyModel<ZombieVillagerEntity> model) {
model.onSetModelAngles((m, move, swing, ticks, entity) -> {
m.getAttributes().visualHeight += PonyTextures.isCrownPony(entity) ? 0.3F : -0.1F;
m.getAttributes().visualHeight += SillyPonyTextureSupplier.isCrownPony(entity) ? 0.3F : -0.1F;
if (m.rightArmPose == ArmPose.EMPTY) {
IMobModel.rotateUndeadArms(m, move, ticks);

View file

@ -1,86 +0,0 @@
package com.minelittlepony.client.render.entity.npc.textures;
import net.minecraft.block.entity.SkullBlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.config.PonyLevel;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.client.SkinsProxy;
import com.minelittlepony.client.pony.PonyManager;
import com.mojang.authlib.GameProfile;
import java.util.*;
public class CustomPonyTextures<T extends LivingEntity> implements TextureSupplier<T> {
private final TextureSupplier<T> fallback;
private final Map<String, Entry> customNameCache = new HashMap<>();
public CustomPonyTextures(TextureSupplier<T> fallback) {
this.fallback = fallback;
}
@Override
public Identifier supplyTexture(T entity) {
Identifier override = getCustomTexture(entity);
if (override != null) {
return override;
}
return fallback.supplyTexture(entity);
}
@Nullable
private Identifier getCustomTexture(T entity) {
if (!entity.hasCustomName()) {
return null;
}
String key = entity.getCustomName().getString() + "_" + entity.getUuidAsString();
if (!customNameCache.containsKey(key)) {
customNameCache.put(key, new Entry(entity));
}
return customNameCache.get(key).getTexture();
}
class Entry {
private final UUID uuid;
private final Identifier texture;
@Nullable
private GameProfile profile;
Entry(T entity) {
uuid = entity.getUuid();
texture = MineLittlePony.getInstance().getVariatedTextures()
.get(PonyManager.BACKGROUND_PONIES)
.getByName(entity.getCustomName().getString(), uuid)
.orElse(null);
if (texture == null) {
SkullBlockEntity.loadProperties(new GameProfile(null, entity.getCustomName().getString()), resolved -> {
profile = resolved;
});
}
}
public Identifier getTexture() {
if (profile != null) {
Identifier skin = SkinsProxy.instance.getSkinTexture(profile);
if (skin != null) {
if (IPony.getManager().getPony(skin).race().isHuman()) {
if (PonyConfig.getInstance().ponyLevel.get() == PonyLevel.PONIES) {
return IPony.getManager().getBackgroundPony(uuid).texture();
}
}
return skin;
}
}
return texture;
}
}
}

View file

@ -0,0 +1,59 @@
package com.minelittlepony.client.render.entity.npc.textures;
import net.minecraft.block.entity.SkullBlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.config.PonyLevel;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.client.SkinsProxy;
import com.minelittlepony.util.FunctionUtil;
import com.mojang.authlib.GameProfile;
import java.util.*;
import java.util.function.Function;
public class PlayerTextureSupplier {
public static <T extends LivingEntity> TextureSupplier<T> create(TextureSupplier<T> fallback) {
Function<T, Entry> customNameCache = FunctionUtil.memoize(Entry::new, entity -> entity.getCustomName().getString() + "_" + entity.getUuidAsString());
return entity -> {
Identifier override = entity.hasCustomName() ? customNameCache.apply(entity).getTexture() : null;
if (override != null) {
return override;
}
return fallback.apply(entity);
};
}
static final class Entry {
private final UUID uuid;
@Nullable
private GameProfile profile;
Entry(LivingEntity entity) {
uuid = entity.getUuid();
SkullBlockEntity.loadProperties(new GameProfile(null, entity.getCustomName().getString()), resolved -> {
profile = resolved;
});
}
@Nullable
public Identifier getTexture() {
if (profile != null) {
Identifier skin = SkinsProxy.instance.getSkinTexture(profile);
if (skin != null) {
if (IPony.getManager().getPony(skin).race().isHuman()) {
if (PonyConfig.getInstance().ponyLevel.get() == PonyLevel.PONIES) {
return IPony.getManager().getBackgroundPony(uuid).texture();
}
}
return skin;
}
}
return null;
}
}
}

View file

@ -1,87 +0,0 @@
package com.minelittlepony.client.render.entity.npc.textures;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.LivingEntity;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import net.minecraft.village.VillagerData;
import net.minecraft.village.VillagerDataContainer;
import net.minecraft.village.VillagerProfession;
import net.minecraft.village.VillagerType;
import com.minelittlepony.util.ResourceUtil;
import java.util.*;
/**
* Cached pool of villager textures.
*/
public class PonyTextures<T extends LivingEntity & VillagerDataContainer> implements TextureSupplier<T> {
private final TextureSupplier<String> formatter;
private final Identifier fallback;
private final Map<String, Identifier> cache = new HashMap<>();
private final ResourceManager resourceManager = MinecraftClient.getInstance().getResourceManager();
/**
* Creates a new profession cache
*
* @param formatter Formatter used when creating new textures
* @param keyMapper Mapper to convert integer ids into a string value for format insertion
* @param fallback The default if any generated textures fail to load. This is stored in place of failing textures.
*/
public PonyTextures(TextureSupplier<String> formatter) {
this.formatter = formatter;
this.fallback = formatter.supplyTexture("villager_pony");
}
@Override
public Identifier supplyTexture(T entity) {
VillagerData t = entity.getVillagerData();
return getTexture(t.getType(), t.getProfession());
}
private Identifier getTexture(final VillagerType type, final VillagerProfession profession) {
String key = ResourceUtil.format("pony/%s/%s", type, profession);
if (cache.containsKey(key)) {
return cache.get(key); // People often complain that villagers cause lag,
// so let's do better than Mojang and rather NOT go
// through all the lambda generations if we can avoid it.
}
Identifier result = verifyTexture(formatter.supplyTexture(key)).orElseGet(() -> {
if (type == VillagerType.PLAINS) {
// if texture loading fails, use the fallback.
return fallback;
}
return getTexture(VillagerType.PLAINS, profession);
});
cache.put(key, result);
return result;
}
protected Optional<Identifier> verifyTexture(Identifier texture) {
return resourceManager.getResource(texture).map(i -> texture);
}
public static boolean isBestPony(LivingEntity entity) {
if (!entity.hasCustomName()) {
return false;
}
String name = entity.getCustomName().getString();
return "Derpy".equals(name) || (entity.isBaby() && "Dinky".equals(name));
}
public static boolean isCrownPony(LivingEntity entity) {
return isBestPony(entity) && entity.getUuid().getLeastSignificantBits() % 20 == 0;
}
}

View file

@ -0,0 +1,68 @@
package com.minelittlepony.client.render.entity.npc.textures;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Identifier;
import net.minecraft.village.*;
import com.minelittlepony.util.ResourceUtil;
import java.util.*;
public class ProfessionTextureSupplier<T extends VillagerDataContainer> implements TextureSupplier<T> {
public static <T extends VillagerDataContainer> TextureSupplier<T> create(TextureSupplier<String> formatter) {
return TextureSupplier.memoize(new ProfessionTextureSupplier<>(formatter), ProfessionTextureSupplier::getKey);
}
private final TextureSupplier<String> formatter;
private final Identifier fallback;
public ProfessionTextureSupplier(TextureSupplier<String> formatter) {
this.formatter = formatter;
this.fallback = formatter.apply("villager_pony");
}
@Override
public Identifier apply(T container) {
return apply(container.getVillagerData());
}
public Identifier apply(VillagerData t) {
return getTexture(t.getType(), t.getProfession());
}
public static String getKey(VillagerDataContainer container) {
VillagerData t = container.getVillagerData();
return ResourceUtil.format("pony/%s/%s", t.getType(), t.getProfession());
}
private Identifier getTexture(final VillagerType type, final VillagerProfession profession) {
String key = ResourceUtil.format("pony/%s/%s", type, profession);
return verifyTexture(formatter.apply(key)).orElseGet(() -> {
if (type == VillagerType.PLAINS) {
// if texture loading fails, use the fallback.
return fallback;
}
return getTexture(VillagerType.PLAINS, profession);
});
}
protected Optional<Identifier> verifyTexture(Identifier texture) {
return MinecraftClient.getInstance().getResourceManager().getResource(texture).map(i -> texture);
}
public static boolean isBestPony(LivingEntity entity) {
if (!entity.hasCustomName()) {
return false;
}
String name = entity.getCustomName().getString();
return "Derpy".equals(name) || (entity.isBaby() && "Dinky".equals(name));
}
public static boolean isCrownPony(LivingEntity entity) {
return isBestPony(entity) && entity.getUuid().getLeastSignificantBits() % 20 == 0;
}
}

View file

@ -0,0 +1,36 @@
package com.minelittlepony.client.render.entity.npc.textures;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Identifier;
import net.minecraft.village.VillagerDataContainer;
public class SillyPonyTextureSupplier<T extends LivingEntity & VillagerDataContainer> implements TextureSupplier<T> {
private final TextureSupplier<T> fallback;
private final Identifier egg;
private final Identifier egg2;
public SillyPonyTextureSupplier(TextureSupplier<T> fallback, TextureSupplier<String> formatter) {
this.fallback = fallback;
this.egg = formatter.apply("silly_pony");
this.egg2 = formatter.apply("tiny_silly_pony");
}
@Override
public Identifier apply(T entity) {
return isBestPony(entity) ? (entity.isBaby() ? egg2 : egg) : fallback.apply(entity);
}
public static boolean isBestPony(LivingEntity entity) {
if (!entity.hasCustomName()) {
return false;
}
String name = entity.getCustomName().getString();
return "Derpy".equals(name) || (entity.isBaby() && "Dinky".equals(name));
}
public static boolean isCrownPony(LivingEntity entity) {
return isBestPony(entity) && entity.getUuid().getLeastSignificantBits() % 20 == 0;
}
}

View file

@ -1,27 +0,0 @@
package com.minelittlepony.client.render.entity.npc.textures;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Identifier;
import net.minecraft.village.VillagerDataContainer;
public class SillyPonyTextures<T extends LivingEntity & VillagerDataContainer> implements TextureSupplier<T> {
private final TextureSupplier<T> fallback;
private final Identifier egg;
private final Identifier egg2;
public SillyPonyTextures(TextureSupplier<T> fallback, TextureSupplier<String> formatter) {
this.fallback = fallback;
this.egg = formatter.supplyTexture("silly_pony");
this.egg2 = formatter.supplyTexture("tiny_silly_pony");
}
@Override
public Identifier supplyTexture(T entity) {
if (PonyTextures.isBestPony(entity)) {
return entity.isBaby() ? egg2 : egg;
}
return fallback.supplyTexture(entity);
}
}

View file

@ -1,18 +1,48 @@
package com.minelittlepony.client.render.entity.npc.textures;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Identifier;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.util.FunctionUtil;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* A texture pool for generating multiple associated textures.
*/
@FunctionalInterface
public interface TextureSupplier<T> {
public interface TextureSupplier<T> extends Function<T, Identifier> {
/**
* Supplies a new texture. May be generated for returned from a pool indexed by the given key.
*/
Identifier supplyTexture(T key);
@Override
Identifier apply(T key);
static TextureSupplier<String> formatted(String domain, String path) {
return key -> new Identifier(domain, String.format(path, key));
}
static <T extends LivingEntity> TextureSupplier<T> ofPool(Identifier poolId, TextureSupplier<T> fallback) {
final Function<T, Identifier> cache = FunctionUtil.memoize(entity -> {
return MineLittlePony.getInstance().getVariatedTextures()
.get(poolId)
.getByName(entity.getCustomName().getString(), entity.getUuid())
.orElse(null);
}, entity -> entity.getCustomName().getString() + "_" + entity.getUuidAsString());
return entity -> {
Identifier override = entity.hasCustomName() ? cache.apply(entity) : null;
if (override != null) {
return override;
}
return fallback.apply(entity);
};
}
static <A> TextureSupplier<A> memoize(Function<A, Identifier> func, Function<A, String> keyFunc) {
final Map<String, Identifier> cache = new ConcurrentHashMap<>();
return a -> cache.computeIfAbsent(keyFunc.apply(a), k -> func.apply(a));
}
}

View file

@ -0,0 +1,12 @@
package com.minelittlepony.util;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
public interface FunctionUtil {
static <A, B> Function<A, B> memoize(Function<A, B> func, Function<A, String> keyFunc) {
final Map<String, B> cache = new ConcurrentHashMap<>();
return a -> cache.computeIfAbsent(keyFunc.apply(a), k -> func.apply(a));
}
}