package net.minecraft.client.renderer.block.model.multipart; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.function.Predicate; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.Util; import net.minecraft.util.ExtraCodecs; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.StateHolder; import net.minecraft.world.level.block.state.properties.Property; import org.slf4j.Logger; @Environment(EnvType.CLIENT) public record KeyValueCondition(Map tests) implements Condition { static final Logger LOGGER = LogUtils.getLogger(); public static final Codec CODEC = ExtraCodecs.nonEmptyMap(Codec.unboundedMap(Codec.STRING, KeyValueCondition.Terms.CODEC)) .xmap(KeyValueCondition::new, KeyValueCondition::tests); @Override public > Predicate instantiate(StateDefinition stateDefinition) { List> list = new ArrayList(this.tests.size()); this.tests.forEach((string, terms) -> list.add(instantiate(stateDefinition, string, terms))); return Util.allOf(list); } private static > Predicate instantiate(StateDefinition stateDefinition, String property, KeyValueCondition.Terms terms) { Property property2 = stateDefinition.getProperty(property); if (property2 == null) { throw new IllegalArgumentException(String.format(Locale.ROOT, "Unknown property '%s' on '%s'", property, stateDefinition.getOwner())); } else { return terms.instantiate(stateDefinition.getOwner(), property2); } } @Environment(EnvType.CLIENT) public record Term(String value, boolean negated) { private static final String NEGATE = "!"; public Term(String value, boolean negated) { if (value.isEmpty()) { throw new IllegalArgumentException("Empty term"); } else { this.value = value; this.negated = negated; } } public static KeyValueCondition.Term parse(String text) { return text.startsWith("!") ? new KeyValueCondition.Term(text.substring(1), true) : new KeyValueCondition.Term(text, false); } public String toString() { return this.negated ? "!" + this.value : this.value; } } @Environment(EnvType.CLIENT) public record Terms(List entries) { private static final char SEPARATOR = '|'; private static final Joiner JOINER = Joiner.on('|'); private static final Splitter SPLITTER = Splitter.on('|'); private static final Codec LEGACY_REPRESENTATION_CODEC = Codec.either(Codec.INT, Codec.BOOL) .flatComapMap(either -> either.map(String::valueOf, String::valueOf), string -> DataResult.error(() -> "This codec can't be used for encoding")); public static final Codec CODEC = Codec.withAlternative(Codec.STRING, LEGACY_REPRESENTATION_CODEC) .comapFlatMap(KeyValueCondition.Terms::parse, KeyValueCondition.Terms::toString); public Terms(List entries) { if (entries.isEmpty()) { throw new IllegalArgumentException("Empty value for property"); } else { this.entries = entries; } } public static DataResult parse(String text) { List list = SPLITTER.splitToStream(text).map(KeyValueCondition.Term::parse).toList(); if (list.isEmpty()) { return DataResult.error(() -> "Empty value for property"); } else { for (KeyValueCondition.Term term : list) { if (term.value.isEmpty()) { return DataResult.error(() -> "Empty term in value '" + text + "'"); } } return DataResult.success(new KeyValueCondition.Terms(list)); } } public String toString() { return JOINER.join(this.entries); } public , T extends Comparable> Predicate instantiate(O owner, Property property) { Predicate predicate = Util.anyOf(Lists.transform(this.entries, term -> this.instantiate(owner, property, term))); List list = new ArrayList(property.getPossibleValues()); int i = list.size(); list.removeIf(predicate.negate()); int j = list.size(); if (j == 0) { KeyValueCondition.LOGGER.warn("Condition {} for property {} on {} is always false", this, property.getName(), owner); return stateHolder -> false; } else { int k = i - j; if (k == 0) { KeyValueCondition.LOGGER.warn("Condition {} for property {} on {} is always true", this, property.getName(), owner); return stateHolder -> true; } else { boolean bl; List list2; if (j <= k) { bl = false; list2 = list; } else { bl = true; List list3 = new ArrayList(property.getPossibleValues()); list3.removeIf(predicate); list2 = list3; } if (list2.size() == 1) { T comparable = (T)list2.getFirst(); return stateHolder -> { T comparable2 = stateHolder.getValue(property); return comparable.equals(comparable2) ^ bl; }; } else { return stateHolder -> { T comparablex = stateHolder.getValue(property); return list2.contains(comparablex) ^ bl; }; } } } } private > T getValueOrThrow(Object owner, Property property, String value) { Optional optional = property.getValue(value); if (optional.isEmpty()) { throw new RuntimeException(String.format(Locale.ROOT, "Unknown value '%s' for property '%s' on '%s' in '%s'", value, property, owner, this)); } else { return (T)optional.get(); } } private > Predicate instantiate(Object owner, Property property, KeyValueCondition.Term term) { T comparable = this.getValueOrThrow(owner, property, term.value); return term.negated ? comparable2 -> !comparable2.equals(comparable) : comparable2 -> comparable2.equals(comparable); } } }