From 1b10ed3f66eff42e786995598ec49664bf984e10 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 3 May 2023 10:14:43 -0400 Subject: [PATCH] Keep custom models loaded permanently on Fabric --- .../modernfix/util/LayeredForwardingMap.java | 124 ++++++++++++++++++ .../dynamic_resources/ModelBakeryMixin.java | 10 +- 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 common/src/main/java/org/embeddedt/modernfix/util/LayeredForwardingMap.java diff --git a/common/src/main/java/org/embeddedt/modernfix/util/LayeredForwardingMap.java b/common/src/main/java/org/embeddedt/modernfix/util/LayeredForwardingMap.java new file mode 100644 index 00000000..99cc73a5 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/util/LayeredForwardingMap.java @@ -0,0 +1,124 @@ +package org.embeddedt.modernfix.util; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * Simple forwarding map implementation that allows layering multiple maps together, with the last layer being + * mutable. + */ +public class LayeredForwardingMap implements Map { + private final Map[] layers; + + public LayeredForwardingMap(Map[] layers) { + if(layers.length < 1) + throw new IllegalArgumentException(); + for(Map layer : layers) { + if(layer == null) + throw new IllegalArgumentException(); + } + this.layers = layers; + } + + @Override + public int size() { + return 1; + } + + @Override + public boolean isEmpty() { + for(Map map : layers) { + if(!map.isEmpty()) + return false; + } + return true; + } + + @Override + public boolean containsKey(Object key) { + for(Map map : layers) { + if(map.containsKey(key)) + return true; + } + return false; + } + + @Override + public boolean containsValue(Object value) { + for(Map map : layers) { + if(map.containsValue(value)) + return true; + } + return false; + } + + @Override + public V get(Object key) { + for(Map map : layers) { + V value = map.get(key); + if(value != null) + return value; + } + return null; + } + + @Nullable + @Override + public V put(K key, V value) { + if(value == null) + throw new IllegalArgumentException(); + return layers[layers.length - 1].put(key, value); + } + + @Override + public V remove(Object key) { + for(Map map : layers) { + map.remove(key); + } + return null; + } + + @Override + public void putAll(@NotNull Map m) { + for(V value : m.values()) { + if(value == null) + throw new IllegalArgumentException(); + } + layers[layers.length - 1].putAll(m); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Set keySet() { + Set keys = new ObjectOpenHashSet<>(); + for(Map map : layers) { + keys.addAll(map.keySet()); + } + return Collections.unmodifiableSet(keys); + } + + @NotNull + @Override + public Collection values() { + Set keys = keySet(); + List vals = new ArrayList<>(); + for(K key : keys) { + vals.add(get(key)); + } + return vals; + } + + @NotNull + @Override + public Set> entrySet() { + throw new UnsupportedOperationException(); + } +} diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java index 0d6d6839..97e66b89 100644 --- a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java +++ b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java @@ -8,6 +8,8 @@ import com.google.common.collect.ImmutableList; import com.mojang.math.Transformation; import net.minecraft.client.renderer.block.model.BlockModel; import net.minecraft.client.renderer.block.model.ItemModelGenerator; +import net.minecraft.client.renderer.block.model.MultiVariant; +import net.minecraft.client.renderer.block.model.multipart.MultiPart; import net.minecraft.client.renderer.texture.AtlasSet; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureManager; @@ -25,6 +27,7 @@ import org.embeddedt.modernfix.annotation.ClientOnlyMixin; import org.embeddedt.modernfix.duck.IExtendedModelBakery; import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider; import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers; +import org.embeddedt.modernfix.util.LayeredForwardingMap; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; @@ -145,7 +148,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { this.loadedModels.put(MISSING_MODEL_LOCATION, this.missingModel); this.bakedCache = loadedBakedModels.asMap(); ConcurrentMap unbakedCacheBackingMap = loadedModels.asMap(); - this.unbakedCache = new ForwardingMap() { + Map mutableBackingMap = new ForwardingMap() { @Override protected Map delegate() { return unbakedCacheBackingMap; @@ -157,6 +160,11 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { return super.put(key, value); } }; + // discard unwrapped models + int oldSize = this.unbakedCache.size(); + this.unbakedCache.entrySet().removeIf(entry -> entry.getValue() instanceof BlockModel || entry.getValue() instanceof MultiVariant || entry.getValue() instanceof MultiPart); + ModernFix.LOGGER.info("{} models evicted, {} custom models loaded permanently", oldSize - this.unbakedCache.size(), this.unbakedCache.size()); + this.unbakedCache = new LayeredForwardingMap<>(new Map[] { this.unbakedCache, mutableBackingMap }); this.bakedTopLevelModels = new DynamicBakedModelProvider((ModelBakery)(Object)this, bakedCache); // ensure missing model is a permanent override