162 lines
6.1 KiB
Java
162 lines
6.1 KiB
Java
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<String, KeyValueCondition.Terms> tests) implements Condition {
|
|
static final Logger LOGGER = LogUtils.getLogger();
|
|
public static final Codec<KeyValueCondition> CODEC = ExtraCodecs.nonEmptyMap(Codec.unboundedMap(Codec.STRING, KeyValueCondition.Terms.CODEC))
|
|
.xmap(KeyValueCondition::new, KeyValueCondition::tests);
|
|
|
|
@Override
|
|
public <O, S extends StateHolder<O, S>> Predicate<S> instantiate(StateDefinition<O, S> stateDefinition) {
|
|
List<Predicate<S>> list = new ArrayList(this.tests.size());
|
|
this.tests.forEach((string, terms) -> list.add(instantiate(stateDefinition, string, terms)));
|
|
return Util.allOf(list);
|
|
}
|
|
|
|
private static <O, S extends StateHolder<O, S>> Predicate<S> instantiate(StateDefinition<O, S> 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<KeyValueCondition.Term> entries) {
|
|
private static final char SEPARATOR = '|';
|
|
private static final Joiner JOINER = Joiner.on('|');
|
|
private static final Splitter SPLITTER = Splitter.on('|');
|
|
private static final Codec<String> 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<KeyValueCondition.Terms> CODEC = Codec.withAlternative(Codec.STRING, LEGACY_REPRESENTATION_CODEC)
|
|
.comapFlatMap(KeyValueCondition.Terms::parse, KeyValueCondition.Terms::toString);
|
|
|
|
public Terms(List<KeyValueCondition.Term> entries) {
|
|
if (entries.isEmpty()) {
|
|
throw new IllegalArgumentException("Empty value for property");
|
|
} else {
|
|
this.entries = entries;
|
|
}
|
|
}
|
|
|
|
public static DataResult<KeyValueCondition.Terms> parse(String text) {
|
|
List<KeyValueCondition.Term> 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 <O, S extends StateHolder<O, S>, T extends Comparable<T>> Predicate<S> instantiate(O owner, Property<T> property) {
|
|
Predicate<T> predicate = Util.anyOf(Lists.transform(this.entries, term -> this.instantiate(owner, property, term)));
|
|
List<T> 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<T> list2;
|
|
if (j <= k) {
|
|
bl = false;
|
|
list2 = list;
|
|
} else {
|
|
bl = true;
|
|
List<T> 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 extends Comparable<T>> T getValueOrThrow(Object owner, Property<T> property, String value) {
|
|
Optional<T> 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 <T extends Comparable<T>> Predicate<T> instantiate(Object owner, Property<T> property, KeyValueCondition.Term term) {
|
|
T comparable = this.getValueOrThrow(owner, property, term.value);
|
|
return term.negated ? comparable2 -> !comparable2.equals(comparable) : comparable2 -> comparable2.equals(comparable);
|
|
}
|
|
}
|
|
}
|