package net.minecraft.client.renderer.block.model; import com.google.common.collect.Lists; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; import it.unimi.dsi.fastutil.objects.ObjectIterator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.stream.Collectors; import javax.annotation.Nullable; import net.minecraft.client.resources.model.Material; import net.minecraft.client.resources.model.ModelDebugName; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.slf4j.Logger; @OnlyIn(Dist.CLIENT) public class TextureSlots { public static final TextureSlots EMPTY = new TextureSlots(Map.of()); private static final char REFERENCE_CHAR = '#'; private final Map resolvedValues; TextureSlots(Map p_376574_) { this.resolvedValues = p_376574_; } @Nullable public Material getMaterial(String p_375817_) { if (isTextureReference(p_375817_)) { p_375817_ = p_375817_.substring(1); } return this.resolvedValues.get(p_375817_); } private static boolean isTextureReference(String p_376119_) { return p_376119_.charAt(0) == '#'; } public static TextureSlots.Data parseTextureMap(JsonObject p_376737_, ResourceLocation p_378464_) { TextureSlots.Data.Builder textureslots$data$builder = new TextureSlots.Data.Builder(); for (Entry entry : p_376737_.entrySet()) { parseEntry(p_378464_, entry.getKey(), entry.getValue().getAsString(), textureslots$data$builder); } return textureslots$data$builder.build(); } private static void parseEntry(ResourceLocation p_375809_, String p_376233_, String p_378647_, TextureSlots.Data.Builder p_376019_) { if (isTextureReference(p_378647_)) { p_376019_.addReference(p_376233_, p_378647_.substring(1)); } else { ResourceLocation resourcelocation = ResourceLocation.tryParse(p_378647_); if (resourcelocation == null) { throw new JsonParseException(p_378647_ + " is not valid resource location"); } p_376019_.addTexture(p_376233_, new Material(p_375809_, resourcelocation)); } } @OnlyIn(Dist.CLIENT) public record Data(Map values) { public static final TextureSlots.Data EMPTY = new TextureSlots.Data(Map.of()); @OnlyIn(Dist.CLIENT) public static class Builder { private final Map textureMap = new HashMap<>(); public TextureSlots.Data.Builder addReference(String p_375509_, String p_378243_) { this.textureMap.put(p_375509_, new TextureSlots.Reference(p_378243_)); return this; } public TextureSlots.Data.Builder addTexture(String p_376424_, Material p_377345_) { this.textureMap.put(p_376424_, new TextureSlots.Value(p_377345_)); return this; } public TextureSlots.Data build() { return this.textureMap.isEmpty() ? TextureSlots.Data.EMPTY : new TextureSlots.Data(Map.copyOf(this.textureMap)); } } } @OnlyIn(Dist.CLIENT) record Reference(String target) implements TextureSlots.SlotContents { } @OnlyIn(Dist.CLIENT) public static class Resolver { private static final Logger LOGGER = LogUtils.getLogger(); private final List entries = new ArrayList<>(); public TextureSlots.Resolver addLast(TextureSlots.Data p_375723_) { this.entries.addLast(p_375723_); return this; } public TextureSlots.Resolver addFirst(TextureSlots.Data p_377819_) { this.entries.addFirst(p_377819_); return this; } public TextureSlots resolve(ModelDebugName p_375822_) { if (this.entries.isEmpty()) { return TextureSlots.EMPTY; } else { Object2ObjectMap object2objectmap = new Object2ObjectArrayMap<>(); Object2ObjectMap object2objectmap1 = new Object2ObjectArrayMap<>(); for (TextureSlots.Data textureslots$data : Lists.reverse(this.entries)) { textureslots$data.values.forEach((p_376922_, p_376306_) -> { switch (p_376306_) { case TextureSlots.Value textureslots$value: object2objectmap1.remove(p_376922_); object2objectmap.put(p_376922_, textureslots$value.material()); break; case TextureSlots.Reference textureslots$reference: object2objectmap.remove(p_376922_); object2objectmap1.put(p_376922_, textureslots$reference); break; default: throw new MatchException(null, null); } }); } if (object2objectmap1.isEmpty()) { return new TextureSlots(object2objectmap); } else { boolean flag = true; while (flag) { flag = false; ObjectIterator> objectiterator = Object2ObjectMaps.fastIterator( object2objectmap1 ); while (objectiterator.hasNext()) { it.unimi.dsi.fastutil.objects.Object2ObjectMap.Entry entry = objectiterator.next(); Material material = object2objectmap.get(entry.getValue().target); if (material != null) { object2objectmap.put(entry.getKey(), material); objectiterator.remove(); flag = true; } } } if (!object2objectmap1.isEmpty()) { LOGGER.warn( "Unresolved texture references in {}:\n{}", p_375822_.debugName(), object2objectmap1.entrySet() .stream() .map(p_378552_ -> "\t#" + p_378552_.getKey() + "-> #" + p_378552_.getValue().target + "\n") .collect(Collectors.joining()) ); } return new TextureSlots(object2objectmap); } } } } @OnlyIn(Dist.CLIENT) public sealed interface SlotContents permits TextureSlots.Value, TextureSlots.Reference { } @OnlyIn(Dist.CLIENT) record Value(Material material) implements TextureSlots.SlotContents { } }