From f2de12329ecb62565fcf2645ce23b8b768c73c80 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 22 Jan 2023 20:23:41 -0500 Subject: [PATCH] Add model predicate flattening --- .../core/config/ModernFixEarlyConfig.java | 1 + .../AndConditionMixin.java | 32 ++++++++ .../OrConditionMixin.java | 32 ++++++++ .../PropertyValueConditionMixin.java | 77 +++++++++++++++++++ .../modernfix/predicate/AllPredicate.java | 22 ++++++ .../modernfix/predicate/AnyPredicate.java | 22 ++++++ .../StatePropertyPredicateHelper.java | 33 ++++++++ .../predicate/all/AllMatchOneBoolean.java | 47 +++++++++++ .../predicate/all/AllMatchOneObject.java | 38 +++++++++ .../predicate/any/AllMatchAnyObject.java | 39 ++++++++++ .../predicate/single/SingleMatchAny.java | 63 +++++++++++++++ .../predicate/single/SingleMatchOne.java | 36 +++++++++ src/main/resources/modernfix.mixins.json | 7 +- 13 files changed, 447 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/AndConditionMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/OrConditionMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/PropertyValueConditionMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/predicate/AllPredicate.java create mode 100644 src/main/java/org/embeddedt/modernfix/predicate/AnyPredicate.java create mode 100644 src/main/java/org/embeddedt/modernfix/predicate/StatePropertyPredicateHelper.java create mode 100644 src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneBoolean.java create mode 100644 src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneObject.java create mode 100644 src/main/java/org/embeddedt/modernfix/predicate/any/AllMatchAnyObject.java create mode 100644 src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchAny.java create mode 100644 src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchOne.java diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index 2b6c451c..deb2f06a 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -35,6 +35,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.parallel_blockstate_cache_rebuild", true); this.addMixinRule("perf.compress_biome_container", true); this.addMixinRule("perf.nuke_empty_chunk_sections", true); + this.addMixinRule("perf.flatten_model_predicates", true); this.addMixinRule("perf.deduplicate_location", true); this.addMixinRule("safety", true); this.addMixinRule("launch.transformer_cache", false); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/AndConditionMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/AndConditionMixin.java new file mode 100644 index 00000000..a8c55fad --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/AndConditionMixin.java @@ -0,0 +1,32 @@ +package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates; + +import com.google.common.collect.Streams; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.client.renderer.model.multipart.AndCondition; +import net.minecraft.client.renderer.model.multipart.ICondition; +import net.minecraft.state.StateContainer; +import org.embeddedt.modernfix.predicate.StatePropertyPredicateHelper; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.function.Predicate; +import java.util.stream.Collectors; + +@Mixin(AndCondition.class) +public class AndConditionMixin { + @Shadow @Final private Iterable conditions; + + /** + * @author JellySquid + * @reason Flatten predicates + */ + @Overwrite + public Predicate getPredicate(StateContainer stateManager) { + return StatePropertyPredicateHelper.allMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> { + return multipartModelSelector.getPredicate(stateManager); + }).collect(Collectors.toList())); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/OrConditionMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/OrConditionMixin.java new file mode 100644 index 00000000..d9a0682b --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/OrConditionMixin.java @@ -0,0 +1,32 @@ +package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates; + +import com.google.common.collect.Streams; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.client.renderer.model.multipart.ICondition; +import net.minecraft.client.renderer.model.multipart.OrCondition; +import net.minecraft.state.StateContainer; +import org.embeddedt.modernfix.predicate.StatePropertyPredicateHelper; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.function.Predicate; +import java.util.stream.Collectors; + +@Mixin(OrCondition.class) +public class OrConditionMixin { + @Shadow @Final private Iterable conditions; + + /** + * @author JellySquid + * @reason Flatten predicates + */ + @Overwrite + public Predicate getPredicate(StateContainer stateManager) { + return StatePropertyPredicateHelper.anyMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> { + return multipartModelSelector.getPredicate(stateManager); + }).collect(Collectors.toList())); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/PropertyValueConditionMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/PropertyValueConditionMixin.java new file mode 100644 index 00000000..b9a2ef35 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/PropertyValueConditionMixin.java @@ -0,0 +1,77 @@ +package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates; + +import com.google.common.base.Splitter; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.client.renderer.model.multipart.PropertyValueCondition; +import net.minecraft.state.Property; +import net.minecraft.state.StateContainer; +import org.embeddedt.modernfix.predicate.single.SingleMatchAny; +import org.embeddedt.modernfix.predicate.single.SingleMatchOne; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +@Mixin(PropertyValueCondition.class) +public class PropertyValueConditionMixin { + @Shadow @Final private String key; + + @Shadow @Final private String value; + + @Shadow @Final private static Splitter PIPE_SPLITTER; + + /** + * @author JellySquid + * @reason De-duplication + */ + @Overwrite + public Predicate getPredicate(StateContainer stateManager) { + Property property = stateManager.getProperty(this.key); + + if (property == null) { + throw new RuntimeException(String.format("Unknown property '%s' on '%s'", this.key, stateManager.getOwner().toString())); + } + + String valueString = this.value; + boolean negate = !valueString.isEmpty() && valueString.charAt(0) == '!'; + + if (negate) { + valueString = valueString.substring(1); + } + + List split = PIPE_SPLITTER.splitToList(valueString); + + if (split.isEmpty()) { + throw new RuntimeException(String.format("Empty value '%s' for property '%s' on '%s'", this.value, this.key, stateManager.getOwner().toString())); + } + + Predicate predicate; + + if (split.size() == 1) { + predicate = new SingleMatchOne(property, this.getPropertyValue(stateManager, property, valueString)); + } else { + predicate = SingleMatchAny.create(property, split.stream() + .map(str -> this.getPropertyValue(stateManager, property, str)) + .collect(Collectors.toList())); + } + + return negate ? predicate.negate() : predicate; + } + + private Object getPropertyValue(StateContainer stateFactory, Property property, String valueString) { + Object value = property.getValue(valueString) + .orElse(null); + + if (value == null) { + throw new RuntimeException(String.format("Unknown value '%s' for property '%s' on '%s' in '%s'", + valueString, this.key, stateFactory.getOwner().toString(), this.value)); + } + + return value; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/AllPredicate.java b/src/main/java/org/embeddedt/modernfix/predicate/AllPredicate.java new file mode 100644 index 00000000..e741e58a --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/predicate/AllPredicate.java @@ -0,0 +1,22 @@ +package org.embeddedt.modernfix.predicate; + +import java.util.function.Predicate; + +public class AllPredicate implements Predicate { + private final Predicate[] predicates; + + public AllPredicate(Predicate[] predicates) { + this.predicates = predicates; + } + + @Override + public boolean test(T t) { + for (Predicate predicate : this.predicates) { + if (!predicate.test(t)) { + return false; + } + } + + return true; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/AnyPredicate.java b/src/main/java/org/embeddedt/modernfix/predicate/AnyPredicate.java new file mode 100644 index 00000000..7c04f5fb --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/predicate/AnyPredicate.java @@ -0,0 +1,22 @@ +package org.embeddedt.modernfix.predicate; + +import java.util.function.Predicate; + +public class AnyPredicate implements Predicate { + private final Predicate[] predicates; + + public AnyPredicate(Predicate[] predicates) { + this.predicates = predicates; + } + + @Override + public boolean test(T t) { + for (Predicate predicate : this.predicates) { + if (predicate.test(t)) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/StatePropertyPredicateHelper.java b/src/main/java/org/embeddedt/modernfix/predicate/StatePropertyPredicateHelper.java new file mode 100644 index 00000000..730e9977 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/predicate/StatePropertyPredicateHelper.java @@ -0,0 +1,33 @@ +package org.embeddedt.modernfix.predicate; + +import me.jellysquid.mods.hydrogen.common.state.all.AllMatchOneBoolean; +import net.minecraft.block.BlockState; +import org.embeddedt.modernfix.predicate.all.AllMatchOneObject; +import org.embeddedt.modernfix.predicate.any.AllMatchAnyObject; +import org.embeddedt.modernfix.predicate.single.SingleMatchAny; +import org.embeddedt.modernfix.predicate.single.SingleMatchOne; + +import java.util.List; +import java.util.function.Predicate; + +public class StatePropertyPredicateHelper { + @SuppressWarnings("unchecked") + public static Predicate allMatch(List> predicates) { + if (SingleMatchOne.areOfType(predicates)) { + if (SingleMatchOne.valuesMatchType(predicates, Boolean.class)) { + return new AllMatchOneBoolean(predicates); + } + + return new AllMatchOneObject(predicates); + } else if (SingleMatchAny.areOfType(predicates)) { + return new AllMatchAnyObject(predicates); + } + + return new AllPredicate<>(predicates.toArray(new Predicate[0])); + } + + @SuppressWarnings("unchecked") + public static Predicate anyMatch(List> predicates) { + return new AnyPredicate<>(predicates.toArray(new Predicate[0])); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneBoolean.java b/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneBoolean.java new file mode 100644 index 00000000..87490e98 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneBoolean.java @@ -0,0 +1,47 @@ +package me.jellysquid.mods.hydrogen.common.state.all; + +import net.minecraft.block.BlockState; +import net.minecraft.state.Property; +import org.embeddedt.modernfix.predicate.single.SingleMatchOne; + +import java.util.List; +import java.util.function.Predicate; + +public class AllMatchOneBoolean implements Predicate { + private final Property[] properties; + private final boolean[] values; + + public AllMatchOneBoolean(List> list) { + int size = list.size(); + + this.properties = new Property[size]; + this.values = new boolean[size]; + + for (int i = 0; i < size; i++) { + SingleMatchOne predicate = (SingleMatchOne) list.get(i); + + this.properties[i] = predicate.property; + this.values[i] = (boolean) predicate.value; + } + } + + public static boolean canReplace(List> list) { + return list.stream() + .allMatch(p -> { + return p instanceof SingleMatchOne && ((SingleMatchOne) p).value instanceof Boolean; + }); + } + + @Override + public boolean test(BlockState blockState) { + for (int i = 0; i < this.properties.length; i++) { + Boolean value = (Boolean) blockState.getValue(this.properties[i]); + + if (value != this.values[i]) { + return false; + } + } + + return true; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneObject.java b/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneObject.java new file mode 100644 index 00000000..1522d5ed --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneObject.java @@ -0,0 +1,38 @@ +package org.embeddedt.modernfix.predicate.all; + +import net.minecraft.block.BlockState; +import net.minecraft.state.Property; +import org.embeddedt.modernfix.predicate.single.SingleMatchOne; + +import java.util.List; +import java.util.function.Predicate; + +public class AllMatchOneObject implements Predicate { + private final Property[] properties; + private final Object[] values; + + public AllMatchOneObject(List> list) { + int size = list.size(); + + this.properties = new Property[size]; + this.values = new Object[size]; + + for (int i = 0; i < size; i++) { + SingleMatchOne predicate = (SingleMatchOne) list.get(i); + + this.properties[i] = predicate.property; + this.values[i] = predicate.value; + } + } + + @Override + public boolean test(BlockState blockState) { + for (int i = 0; i < this.properties.length; i++) { + if (blockState.getValue(this.properties[i]) != this.values[i]) { + return false; + } + } + + return true; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/any/AllMatchAnyObject.java b/src/main/java/org/embeddedt/modernfix/predicate/any/AllMatchAnyObject.java new file mode 100644 index 00000000..04fa3533 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/predicate/any/AllMatchAnyObject.java @@ -0,0 +1,39 @@ +package org.embeddedt.modernfix.predicate.any; + +import net.minecraft.block.BlockState; +import net.minecraft.state.Property; +import org.apache.commons.lang3.ArrayUtils; +import org.embeddedt.modernfix.predicate.single.SingleMatchAny; + +import java.util.List; +import java.util.function.Predicate; + +public class AllMatchAnyObject implements Predicate { + private final Property[] properties; + private final Object[][] values; + + public AllMatchAnyObject(List> list) { + int size = list.size(); + + this.properties = new Property[size]; + this.values = new Object[size][]; + + for (int i = 0; i < size; i++) { + SingleMatchAny predicate = (SingleMatchAny) list.get(i); + + this.properties[i] = predicate.property; + this.values[i] = predicate.values; + } + } + + @Override + public boolean test(BlockState blockState) { + for (int i = 0; i < this.properties.length; i++) { + if (!ArrayUtils.contains(this.values[i], blockState.getValue(this.properties[i]))) { + return false; + } + } + + return true; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchAny.java b/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchAny.java new file mode 100644 index 00000000..72a8c6de --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchAny.java @@ -0,0 +1,63 @@ +package org.embeddedt.modernfix.predicate.single; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.block.BlockState; +import net.minecraft.state.Property; +import org.apache.commons.lang3.ArrayUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; + +public class SingleMatchAny implements Predicate { + public static final ObjectOpenHashSet PREDICATES = new ObjectOpenHashSet<>(); + + public final Property property; + public final Object[] values; + + private SingleMatchAny(Property property, List values) { + this.property = property; + this.values = values.toArray(); + } + + public static SingleMatchAny create(Property property, List values) { + return PREDICATES.addOrGet(new SingleMatchAny(property, values)); + } + + public static boolean areOfType(List> predicates) { + return predicates.stream() + .allMatch(p -> { + return p instanceof SingleMatchAny; + }); + } + + public static boolean valuesMatchType(List> predicates, Class type) { + return predicates.stream() + .allMatch(p -> { + return p instanceof SingleMatchAny && + Arrays.stream(((SingleMatchAny) p).values).allMatch(t -> type.isInstance(p)); + }); + } + + @Override + public boolean test(BlockState blockState) { + return ArrayUtils.contains(this.values, blockState.getValue(this.property)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SingleMatchAny that = (SingleMatchAny) o; + return Objects.equals(property, that.property) && + Arrays.equals(values, that.values); + } + + @Override + public int hashCode() { + int result = Objects.hash(property); + result = 31 * result + Arrays.hashCode(values); + return result; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchOne.java b/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchOne.java new file mode 100644 index 00000000..6bd5c389 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchOne.java @@ -0,0 +1,36 @@ +package org.embeddedt.modernfix.predicate.single; + +import net.minecraft.block.BlockState; +import net.minecraft.state.Property; + +import java.util.List; +import java.util.function.Predicate; + +public class SingleMatchOne implements Predicate { + public final Property property; + public final Object value; + + public SingleMatchOne(Property property, Object value) { + this.property = property; + this.value = value; + } + + public static boolean areOfType(List> predicates) { + return predicates.stream() + .allMatch(p -> { + return p instanceof SingleMatchOne; + }); + } + + public static boolean valuesMatchType(List> predicates, Class type) { + return predicates.stream() + .allMatch(p -> { + return p instanceof SingleMatchOne && type.isInstance(((SingleMatchOne) p).value); + }); + } + + @Override + public boolean test(BlockState blockState) { + return blockState.getValue(this.property) == this.value; + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 7beab9d1..55843f36 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -7,7 +7,6 @@ "refmap": "modernfix.refmap.json", "mixins": [ "bugfix.edge_chunk_not_saved.ChunkManagerMixin", - "feature.measure_time.MinecraftMixin", "perf.remove_biome_temperature_cache.BiomeMixin", "perf.resourcepacks.ModFileResourcePackMixin", "perf.resourcepacks.VanillaPackMixin", @@ -28,6 +27,7 @@ "perf.nuke_empty_chunk_sections.MixinChunk" ], "client": [ + "feature.measure_time.MinecraftMixin", "perf.skip_first_datapack_reload.MinecraftMixin", "bugfix.concurrency.RenderTypeMixin", "bugfix.concurrency.MinecraftMixin", @@ -40,7 +40,10 @@ "perf.async_jei.PluginCallerMixin", "perf.async_jei.RecipeManagerInternalMixin", "perf.thread_priorities.IntegratedServerMixin", - "safety.BlockColorsMixin" + "safety.BlockColorsMixin", + "perf.flatten_model_predicates.AndConditionMixin", + "perf.flatten_model_predicates.OrConditionMixin", + "perf.flatten_model_predicates.PropertyValueConditionMixin" ], "injectors": { "defaultRequire": 1