From 2aacadaa82314680a8e7964866aeeb78951ff6b0 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 2 May 2023 20:58:52 -0400 Subject: [PATCH 1/5] Fix incorrectly mapped mixin --- .../forge/mixin/feature/measure_time/MinecraftMixin_Forge.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/measure_time/MinecraftMixin_Forge.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/measure_time/MinecraftMixin_Forge.java index 62eecc35..ee5a3927 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/measure_time/MinecraftMixin_Forge.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/measure_time/MinecraftMixin_Forge.java @@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(Minecraft.class) @ClientOnlyMixin public class MinecraftMixin_Forge { - @Inject(method = "doWorldLoad", at = @At("HEAD"), remap = false) + @Inject(method = "doWorldLoad", at = @At("HEAD")) private void recordWorldLoadStart(CallbackInfo ci) { ModernFixClient.worldLoadStartTime = System.nanoTime(); } From 3ad3da36801fe57726f048278d7778048e72344f Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 2 May 2023 21:03:40 -0400 Subject: [PATCH 2/5] Fix resource cache not being generated on Forge --- .../forge/mixin/perf/resourcepacks/ModFileResourcePackMixin.java | 1 + 1 file changed, 1 insertion(+) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcepacks/ModFileResourcePackMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcepacks/ModFileResourcePackMixin.java index 44b8675f..08c67bba 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcepacks/ModFileResourcePackMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcepacks/ModFileResourcePackMixin.java @@ -48,6 +48,7 @@ public abstract class ModFileResourcePackMixin { @Inject(method = "hasResource(Ljava/lang/String;)Z", at = @At(value = "HEAD"), cancellable = true) private void useCacheForExistence(String path, CallbackInfoReturnable cir) { + this.generateResourceCache(); if(cacheEngine != null) cir.setReturnValue(this.cacheEngine.hasResource(path)); } From 8f71606faddc9d722001e3b1c40b7ee1a428a867 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 2 May 2023 22:00:49 -0400 Subject: [PATCH 3/5] Fix entity renderer crash in some modpacks --- .../EntityRenderDispatcherMixin.java | 38 ++++++ .../EntityRenderersMixin.java | 107 +-------------- .../modernfix/entity/EntityRendererMap.java | 125 ++++++++++++++++++ 3 files changed, 166 insertions(+), 104 deletions(-) create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_entity_renderers/EntityRenderDispatcherMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/entity/EntityRendererMap.java diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_entity_renderers/EntityRenderDispatcherMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_entity_renderers/EntityRenderDispatcherMixin.java new file mode 100644 index 00000000..4d3f3b41 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_entity_renderers/EntityRenderDispatcherMixin.java @@ -0,0 +1,38 @@ +package org.embeddedt.modernfix.common.mixin.perf.dynamic_entity_renderers; + +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.entity.EntityRendererMap; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Map; + +@Mixin(EntityRenderDispatcher.class) +@ClientOnlyMixin +public class EntityRenderDispatcherMixin { + @Shadow private Map, EntityRenderer> renderers; + + private EntityRendererMap mfix$dynamicRenderers; + + @Inject(method = "getRenderer", at = @At("RETURN"), cancellable = true) + private void checkNullness(T entity, CallbackInfoReturnable> cir) { + // apparently some mods yeet the renderers map and cause issues + if(cir.getReturnValue() == null) + cir.setReturnValue((EntityRenderer)mfix$dynamicRenderers.get(entity.getType())); + } + + @Redirect(method = "onResourceManagerReload", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/renderer/entity/EntityRenderDispatcher;renderers:Ljava/util/Map;")) + private void setRendererField(EntityRenderDispatcher instance, Map, EntityRenderer> incomingMap) { + this.renderers = incomingMap; + this.mfix$dynamicRenderers = (EntityRendererMap)incomingMap; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_entity_renderers/EntityRenderersMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_entity_renderers/EntityRenderersMixin.java index a5c78050..a6d93ca0 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_entity_renderers/EntityRenderersMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_entity_renderers/EntityRenderersMixin.java @@ -1,18 +1,12 @@ package org.embeddedt.modernfix.common.mixin.perf.dynamic_entity_renderers; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.EntityRenderers; -import net.minecraft.core.Registry; import net.minecraft.world.entity.EntityType; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.annotation.ClientOnlyMixin; -import org.embeddedt.modernfix.entity.ErroredEntityRenderer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.embeddedt.modernfix.entity.EntityRendererMap; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -20,10 +14,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import java.util.Collection; import java.util.Map; -import java.util.Set; -import java.util.concurrent.ExecutionException; @Mixin(EntityRenderers.class) @ClientOnlyMixin @@ -32,99 +23,7 @@ public class EntityRenderersMixin { @Inject(method = "createEntityRenderers", at = @At("HEAD"), cancellable = true) private static void createDynamicRendererLoader(EntityRendererProvider.Context context, CallbackInfoReturnable, EntityRenderer>> cir) { - Map, EntityRendererProvider> rendererProviders = PROVIDERS; - LoadingCache, EntityRenderer> rendererMap = CacheBuilder.newBuilder() - .build(new CacheLoader<>() { - @Override - public EntityRenderer load(EntityType key) throws Exception { - EntityRendererProvider provider = rendererProviders.get(key); - synchronized(EntityRenderers.class) { - EntityRenderer renderer; - try { - if(provider == null) - throw new RuntimeException("Provider not registered"); - renderer = provider.create(context); - ModernFix.LOGGER.info("Loaded entity {}", Registry.ENTITY_TYPE.getKey(key)); - } catch(RuntimeException e) { - ModernFix.LOGGER.error("Failed to create entity model for " + Registry.ENTITY_TYPE.getKey(key) + ":", e); - renderer = new ErroredEntityRenderer<>(context); - } - return renderer; - } - } - }); - cir.setReturnValue(new Map, EntityRenderer>() { - @Override - public int size() { - return rendererProviders.size(); - } - - @Override - public boolean isEmpty() { - return rendererProviders.isEmpty(); - } - - @Override - public boolean containsKey(Object o) { - return rendererProviders.containsKey(o); - } - - @Override - public boolean containsValue(Object o) { - return false; - } - - @Override - public EntityRenderer get(Object o) { - try { - return rendererMap.get((EntityType)o); - } catch(ExecutionException e) { - throw new RuntimeException(e); - } - } - - @Nullable - @Override - public EntityRenderer put(EntityType entityType, EntityRenderer entityRenderer) { - EntityRenderer old = rendererMap.getIfPresent(entityType); - rendererMap.put(entityType, entityRenderer); - return old; - } - - @Override - public EntityRenderer remove(Object o) { - EntityRenderer r = rendererMap.getIfPresent(o); - rendererMap.invalidate(o); - return r; - } - - @Override - public void putAll(@NotNull Map, ? extends EntityRenderer> map) { - rendererMap.putAll(map); - } - - @Override - public void clear() { - rendererMap.invalidateAll(); - } - - @NotNull - @Override - public Set> keySet() { - return rendererProviders.keySet(); - } - - @NotNull - @Override - public Collection> values() { - return rendererMap.asMap().values(); - } - - @NotNull - @Override - public Set, EntityRenderer>> entrySet() { - return rendererMap.asMap().entrySet(); - } - }); + cir.setReturnValue(new EntityRendererMap(PROVIDERS, context)); + ModernFix.LOGGER.info("Dynamic entity renderer hook setup"); } } diff --git a/common/src/main/java/org/embeddedt/modernfix/entity/EntityRendererMap.java b/common/src/main/java/org/embeddedt/modernfix/entity/EntityRendererMap.java new file mode 100644 index 00000000..df280415 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/entity/EntityRendererMap.java @@ -0,0 +1,125 @@ +package org.embeddedt.modernfix.entity; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.entity.EntityRenderers; +import net.minecraft.core.Registry; +import net.minecraft.world.entity.EntityType; +import org.embeddedt.modernfix.ModernFix; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +public class EntityRendererMap implements Map, EntityRenderer> { + private final Map, EntityRendererProvider> rendererProviders; + private final LoadingCache, EntityRenderer> rendererMap; + private final EntityRendererProvider.Context context; + + public EntityRendererMap(Map, EntityRendererProvider> rendererProviders, EntityRendererProvider.Context context) { + this.rendererProviders = rendererProviders; + this.context = context; + this.rendererMap = CacheBuilder.newBuilder().build(new RenderConstructor()); + } + + class RenderConstructor extends CacheLoader, EntityRenderer> { + @Override + public EntityRenderer load(EntityType key) throws Exception { + EntityRendererProvider provider = rendererProviders.get(key); + synchronized(EntityRenderers.class) { + EntityRenderer renderer; + try { + if(provider == null) + throw new RuntimeException("Provider not registered"); + renderer = provider.create(context); + ModernFix.LOGGER.info("Loaded entity {}", Registry.ENTITY_TYPE.getKey(key)); + } catch(RuntimeException e) { + ModernFix.LOGGER.error("Failed to create entity model for " + Registry.ENTITY_TYPE.getKey(key) + ":", e); + renderer = new ErroredEntityRenderer<>(context); + } + return renderer; + } + } + } + + @Override + public int size() { + return rendererProviders.size(); + } + + @Override + public boolean isEmpty() { + return rendererProviders.isEmpty(); + } + + @Override + public boolean containsKey(Object o) { + return rendererProviders.containsKey(o); + } + + @Override + public boolean containsValue(Object o) { + return false; + } + + @Override + public EntityRenderer get(Object o) { + try { + EntityRenderer renderer = rendererMap.get((EntityType)o); + if(renderer == null) + throw new AssertionError("Returned entity renderer should never be null"); + return renderer; + } catch(ExecutionException e) { + throw new RuntimeException(e); + } + } + + @Nullable + @Override + public EntityRenderer put(EntityType entityType, EntityRenderer entityRenderer) { + EntityRenderer old = rendererMap.getIfPresent(entityType); + rendererMap.put(entityType, entityRenderer); + return old; + } + + @Override + public EntityRenderer remove(Object o) { + EntityRenderer r = rendererMap.getIfPresent(o); + rendererMap.invalidate(o); + return r; + } + + @Override + public void putAll(@NotNull Map, ? extends EntityRenderer> map) { + rendererMap.putAll(map); + } + + @Override + public void clear() { + rendererMap.invalidateAll(); + } + + @NotNull + @Override + public Set> keySet() { + return rendererProviders.keySet(); + } + + @NotNull + @Override + public Collection> values() { + return rendererMap.asMap().values(); + } + + @NotNull + @Override + public Set, EntityRenderer>> entrySet() { + return rendererMap.asMap().entrySet(); + } +} 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 4/5] 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 From 9d7f897daee7d4235900e8ca3cc90a9f649c7fe3 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 3 May 2023 10:32:32 -0400 Subject: [PATCH 5/5] Get Continuity working on Fabric --- .../modernfix/util/LayeredForwardingMap.java | 16 ++++++++++++---- .../perf/dynamic_resources/ModelBakeryMixin.java | 4 ++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/common/src/main/java/org/embeddedt/modernfix/util/LayeredForwardingMap.java b/common/src/main/java/org/embeddedt/modernfix/util/LayeredForwardingMap.java index 99cc73a5..cf9f3528 100644 --- a/common/src/main/java/org/embeddedt/modernfix/util/LayeredForwardingMap.java +++ b/common/src/main/java/org/embeddedt/modernfix/util/LayeredForwardingMap.java @@ -7,8 +7,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; /** - * Simple forwarding map implementation that allows layering multiple maps together, with the last layer being - * mutable. + * Simple forwarding map implementation that allows layering multiple maps together. */ public class LayeredForwardingMap implements Map { private final Map[] layers; @@ -70,7 +69,14 @@ public class LayeredForwardingMap implements Map { public V put(K key, V value) { if(value == null) throw new IllegalArgumentException(); - return layers[layers.length - 1].put(key, value); + V originalValue = null; + for(Map map : layers) { + V oldVal = map.remove(key); + if(originalValue == null) + originalValue = oldVal; + map.put(key, value); + } + return originalValue; } @Override @@ -87,7 +93,9 @@ public class LayeredForwardingMap implements Map { if(value == null) throw new IllegalArgumentException(); } - layers[layers.length - 1].putAll(m); + for(Map map : layers) { + map.putAll(m); + } } @Override 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 97e66b89..e902b4f0 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 @@ -295,6 +295,10 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { } else ibakedmodel = iunbakedmodel.bake((ModelBakery) (Object) this, textureGetter, arg2, arg); } + if(ibakedmodel == null) { + ModernFix.LOGGER.error("Model {} returned null baked model", arg); + ibakedmodel = bakedMissingModel; + } // TODO event this.bakedCache.put(triple, ibakedmodel); cir.setReturnValue(ibakedmodel);