From ab8810b7fe18390136ada19f4b5e9fa077f231dd Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 29 May 2024 20:37:34 -0400 Subject: [PATCH] 1.21-pre1 - rewrite dynamic resources --- .../modernfix/api/helpers/ModelHelpers.java | 8 +- .../BlockElementFaceDeserializerMixin.java | 23 -- .../BlockStateModelLoaderMixin.java | 55 +++ .../dynamic_resources/ItemOverridesMixin.java | 26 -- .../MinecraftMixin_ModelTicking.java | 22 ++ .../dynamic_resources/ModelBakeryMixin.java | 180 ++++++++++ .../dynamic_resources/ModelManagerMixin.java | 28 +- .../duck/IBlockStateModelLoader.java | 7 + .../modernfix/duck/IExtendedModelBaker.java | 9 - .../modernfix/duck/IExtendedModelBakery.java | 14 +- .../modernfix/duck/IExtendedModelManager.java | 5 + .../DynamicBakedModelProvider.java | 240 ------------- .../org/embeddedt/modernfix/util/LRUMap.java | 45 +++ .../ItemOverridesFabricMixin.java | 31 -- .../LoaderInstanceMixin.java | 19 - .../ModelBakerImplMixin.java | 185 ---------- .../dynamic_resources/ModelBakeryMixin.java | 328 ------------------ gradle.properties | 4 +- 18 files changed, 346 insertions(+), 883 deletions(-) delete mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockElementFaceDeserializerMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockStateModelLoaderMixin.java delete mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemOverridesMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/MinecraftMixin_ModelTicking.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ModelBakeryMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/duck/IBlockStateModelLoader.java delete mode 100644 common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBaker.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelManager.java delete mode 100644 common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/util/LRUMap.java delete mode 100644 fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ItemOverridesFabricMixin.java delete mode 100644 fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/LoaderInstanceMixin.java delete mode 100644 fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java delete mode 100644 fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java diff --git a/common/src/main/java/org/embeddedt/modernfix/api/helpers/ModelHelpers.java b/common/src/main/java/org/embeddedt/modernfix/api/helpers/ModelHelpers.java index 4e05c197..13b689a6 100644 --- a/common/src/main/java/org/embeddedt/modernfix/api/helpers/ModelHelpers.java +++ b/common/src/main/java/org/embeddedt/modernfix/api/helpers/ModelHelpers.java @@ -7,10 +7,8 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; -import org.embeddedt.modernfix.duck.IExtendedModelBakery; import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers; import org.embeddedt.modernfix.util.DynamicMap; -import org.jetbrains.annotations.Nullable; import java.util.Map; import java.util.Optional; @@ -25,7 +23,7 @@ public final class ModelHelpers { * @return a list of all blockstates related to the model */ public static ImmutableList getBlockStateForLocation(ModelResourceLocation location) { - Optional blockOpt = BuiltInRegistries.BLOCK.getOptional(ResourceLocation.fromNamespaceAndPath(location.getNamespace(), location.getPath())); + Optional blockOpt = BuiltInRegistries.BLOCK.getOptional(location.id()); if(blockOpt.isPresent()) return ModelBakeryHelpers.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), location); else @@ -58,6 +56,8 @@ public final class ModelHelpers { * @return an appropriate ModelBaker */ public static ModelBaker adaptBakery(ModelBakery bakery) { + throw new UnsupportedOperationException("TODO"); + /* return new ModelBaker() { @Override public UnbakedModel getModel(ResourceLocation resourceLocation) { @@ -70,5 +70,7 @@ public final class ModelHelpers { return ((IExtendedModelBakery)bakery).bakeDefault(resourceLocation, modelState); } }; + + */ } } diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockElementFaceDeserializerMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockElementFaceDeserializerMixin.java deleted file mode 100644 index 17404372..00000000 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockElementFaceDeserializerMixin.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonElement; -import net.minecraft.client.renderer.block.model.BlockElementFace; -import org.embeddedt.modernfix.annotation.ClientOnlyMixin; -import org.embeddedt.modernfix.dynamicresources.UVController; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.lang.reflect.Type; - -@Mixin(BlockElementFace.Deserializer.class) -@ClientOnlyMixin -public class BlockElementFaceDeserializerMixin { - - @Redirect(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/renderer/block/model/BlockElementFace;", - at = @At(value = "INVOKE", target = "Lcom/google/gson/JsonDeserializationContext;deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;)Ljava/lang/Object;", ordinal = 0, remap = false)) - private Object skipUvsForInitialLoad(JsonDeserializationContext context, JsonElement element, Type type) { - return UVController.useDummyUv.get() ? UVController.dummyUv : context.deserialize(element, type); - } -} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockStateModelLoaderMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockStateModelLoaderMixin.java new file mode 100644 index 00000000..76d3e406 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockStateModelLoaderMixin.java @@ -0,0 +1,55 @@ +package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntMaps; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.resources.model.BlockStateModelLoader; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.DefaultedRegistry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.duck.IBlockStateModelLoader; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +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.CallbackInfo; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.function.BiConsumer; + +@Mixin(BlockStateModelLoader.class) +@ClientOnlyMixin +public abstract class BlockStateModelLoaderMixin implements IBlockStateModelLoader { + @Shadow protected abstract void loadBlockStateDefinitions(ResourceLocation resourceLocation, StateDefinition stateDefinition); + + @Shadow @Mutable @Final private Object2IntMap modelGroups; + + @Inject(method = "", at = @At("RETURN")) + private void makeModelGroupsSynchronized(Map map, ProfilerFiller profilerFiller, UnbakedModel unbakedModel, BlockColors blockColors, BiConsumer biConsumer, CallbackInfo ci) { + this.modelGroups = Object2IntMaps.synchronize(this.modelGroups); + } + + @Override + public void loadSpecificBlock(ResourceLocation location) { + var optionalBlock = BuiltInRegistries.BLOCK.getOptional(location); + if(optionalBlock.isPresent()) { + this.loadBlockStateDefinitions(location, optionalBlock.get().getStateDefinition()); + } + } + + @Redirect(method = "loadAllBlockStates", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/DefaultedRegistry;iterator()Ljava/util/Iterator;")) + private Iterator skipIteratingBlocks(DefaultedRegistry instance) { + return Collections.emptyIterator(); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemOverridesMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemOverridesMixin.java deleted file mode 100644 index 6309b2a5..00000000 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemOverridesMixin.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.client.renderer.block.model.ItemOverrides; -import net.minecraft.client.resources.model.ModelBaker; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.resources.ResourceLocation; -import org.embeddedt.modernfix.annotation.ClientOnlyMixin; -import org.embeddedt.modernfix.duck.IExtendedModelBaker; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -@Mixin(ItemOverrides.class) -@ClientOnlyMixin -public class ItemOverridesMixin { - @WrapOperation(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBaker;getModel(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/UnbakedModel;")) - private UnbakedModel preventThrowForMissing(ModelBaker instance, ResourceLocation resourceLocation, Operation original) { - boolean prevState = ((IExtendedModelBaker)instance).throwOnMissingModel(false); - try { - return original.call(instance, resourceLocation); - } finally { - ((IExtendedModelBaker)instance).throwOnMissingModel(prevState); - } - } -} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/MinecraftMixin_ModelTicking.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/MinecraftMixin_ModelTicking.java new file mode 100644 index 00000000..97be5723 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/MinecraftMixin_ModelTicking.java @@ -0,0 +1,22 @@ +package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.model.ModelManager; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.duck.IExtendedModelManager; +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.callback.CallbackInfo; + +@Mixin(Minecraft.class) +@ClientOnlyMixin +public abstract class MinecraftMixin_ModelTicking { + @Shadow public abstract ModelManager getModelManager(); + + @Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/tutorial/Tutorial;tick()V")) + private void tickModels(CallbackInfo ci) { + ((IExtendedModelManager)this.getModelManager()).mfix$tick(); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ModelBakeryMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ModelBakeryMixin.java new file mode 100644 index 00000000..b3c48f73 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ModelBakeryMixin.java @@ -0,0 +1,180 @@ +package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.BlockStateModelLoader; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.DefaultedRegistry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.profiling.ProfilerFiller; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.duck.IBlockStateModelLoader; +import org.embeddedt.modernfix.duck.IExtendedModelBakery; +import org.embeddedt.modernfix.util.DynamicOverridableMap; +import org.embeddedt.modernfix.util.LRUMap; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +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.CallbackInfo; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; + +@Mixin(ModelBakery.class) +@ClientOnlyMixin +public abstract class ModelBakeryMixin implements IExtendedModelBakery { + @Unique + private BlockStateModelLoader dynamicLoader; + + @Unique + private final ReentrantLock modelBakeryLock = new ReentrantLock(); + + @Unique + private ModelBakery.TextureGetter textureGetter; + + @Unique + private BakedModel bakedMissingModel; + + @Shadow abstract UnbakedModel getModel(ResourceLocation resourceLocation); + + @Shadow @Final private UnbakedModel missingModel; + + /** + * Bake a model using the provided texture getter and location. The model is stored in {@link ModelBakeryMixin#bakedTopLevelModels}. + */ + @Shadow protected abstract void method_61072(ModelBakery.TextureGetter getter, ModelResourceLocation location, UnbakedModel model); + + @Shadow @Mutable @Final private Map bakedTopLevelModels; + @Shadow @Mutable @Final public Map topLevelModels; + @Shadow @Mutable @Final private Map unbakedCache; + @Shadow @Mutable @Final public Map bakedCache; + + @Shadow protected abstract void loadItemModelAndDependencies(ResourceLocation resourceLocation); + + + @Shadow @Final public static ModelResourceLocation MISSING_MODEL_VARIANT; + + private final Map mfix$emulatedBakedRegistry = new DynamicOverridableMap<>(this::loadBakedModelDynamic); + + @Unique + private UnbakedModel loadUnbakedModelDynamic(ModelResourceLocation location) { + if(location.equals(MISSING_MODEL_VARIANT)) { + return missingModel; + } else if(location.variant().equals("inventory")) { + this.loadItemModelAndDependencies(location.id()); + } else { + ((IBlockStateModelLoader)dynamicLoader).loadSpecificBlock(location.id()); + } + return this.topLevelModels.getOrDefault(location, this.missingModel); + } + + @Unique + private BakedModel loadBakedModelDynamic(ModelResourceLocation location) { + if(location.equals(MISSING_MODEL_VARIANT)) { + return bakedMissingModel; + } + BakedModel model; + modelBakeryLock.lock(); + try { + model = bakedTopLevelModels.get(location); + if(model == null) { + UnbakedModel prototype = loadUnbakedModelDynamic(location); + prototype.resolveParents(this::getModel); + this.method_61072(this.textureGetter, location, prototype); + model = bakedTopLevelModels.remove(location); + if(model == null) { + ModernFix.LOGGER.error("Failed to load model " + location); + model = bakedMissingModel; + } + } + } finally { + modelBakeryLock.unlock(); + } + return model; + } + + @ModifyExpressionValue(method = "", at = @At(value = "CONSTANT", args = "stringValue=missing_model")) + private String replaceBackingMaps(String original) { + this.unbakedCache = new LRUMap<>(this.unbakedCache); + this.bakedCache = new LRUMap<>(this.bakedCache); + this.topLevelModels = new LRUMap<>(this.topLevelModels); + this.bakedTopLevelModels = new LRUMap<>(this.bakedTopLevelModels); + return original; + } + + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/BlockStateModelLoader;loadAllBlockStates()V")) + private void noInitialBlockStateLoad(BlockStateModelLoader instance) { + dynamicLoader = instance; + } + + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/DefaultedRegistry;keySet()Ljava/util/Set;")) + private Set skipLoadingItems(DefaultedRegistry instance) { + return Collections.emptySet(); + } + + + @Inject(method = "bakeModels", at = @At("HEAD")) + private void storeTextureGetterAndBakeMissing(ModelBakery.TextureGetter textureGetter, CallbackInfo ci) { + this.textureGetter = textureGetter; + this.method_61072(textureGetter, MISSING_MODEL_VARIANT, Objects.requireNonNull(this.topLevelModels.get(MISSING_MODEL_VARIANT))); + this.bakedMissingModel = this.bakedTopLevelModels.get(MISSING_MODEL_VARIANT); + } + + @Inject(method = "", at = @At("RETURN")) + private void onInitialLoadFinish(BlockColors blockColors, ProfilerFiller profilerFiller, Map map, Map map2, CallbackInfo ci) { + var permanentMRLs = new ObjectOpenHashSet<>(this.topLevelModels.keySet()); + ((LRUMap)this.topLevelModels).setPermanentEntries(permanentMRLs); + ((LRUMap)this.bakedTopLevelModels).setPermanentEntries(permanentMRLs); + ModernFix.LOGGER.info("Dynamic model bakery initialized, with {} permanent top level models", this.topLevelModels.size()); + } + + @Unique + private int tickCount; + + @Unique + private static final int MAXIMUM_CACHE_SIZE = 1000; + + private void runCleanup() { + ((LRUMap)this.unbakedCache).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE); + ((LRUMap)this.bakedCache).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE); + ((LRUMap)this.topLevelModels).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE); + ((LRUMap)this.bakedTopLevelModels).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE); + } + + @Override + public void mfix$tick() { + tickCount++; + if((tickCount % 200) == 0) { + if(modelBakeryLock.tryLock()) { + try { + runCleanup(); + } finally { + modelBakeryLock.unlock(); + } + } + } + } + + /** + * @author embeddedt + * @reason We provide a fake baked registry to the rest of Minecraft, that dynamically loads models. + */ + @Overwrite + public Map getBakedTopLevelModels() { + return this.mfix$emulatedBakedRegistry; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ModelManagerMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ModelManagerMixin.java index 392ba1c7..da555772 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ModelManagerMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ModelManagerMixin.java @@ -2,24 +2,31 @@ package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources; import com.google.common.collect.ImmutableList; import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.AtlasSet; import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.BlockStateModelLoader; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.client.resources.model.ModelManager; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.util.GsonHelper; +import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.duck.IExtendedModelBakery; +import org.embeddedt.modernfix.duck.IExtendedModelManager; import org.embeddedt.modernfix.util.LambdaMap; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; 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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.io.BufferedReader; import java.io.IOException; @@ -33,9 +40,12 @@ import java.util.stream.Collectors; @Mixin(ModelManager.class) @ClientOnlyMixin -public class ModelManagerMixin { +public class ModelManagerMixin implements IExtendedModelManager { @Shadow private Map bakedRegistry; + @Unique + private Runnable tickHandler = () -> {}; + @Inject(method = "", at = @At("RETURN")) private void injectDummyBakedRegistry(CallbackInfo ci) { if(this.bakedRegistry == null) { @@ -49,7 +59,7 @@ public class ModelManagerMixin { } @Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockStates(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;")) - private CompletableFuture>> deferBlockStateLoad(ResourceManager manager, Executor executor) { + private CompletableFuture>> deferBlockStateLoad(ResourceManager manager, Executor executor) { return CompletableFuture.completedFuture(new LambdaMap<>(location -> loadSingleBlockState(manager, location))); } @@ -69,14 +79,24 @@ public class ModelManagerMixin { }).orElse(null); } - private List loadSingleBlockState(ResourceManager manager, ResourceLocation location) { + private List loadSingleBlockState(ResourceManager manager, ResourceLocation location) { return manager.getResourceStack(location).stream().map(resource -> { try (BufferedReader reader = resource.openAsReader()) { - return new ModelBakery.LoadedJson(resource.sourcePackId(), GsonHelper.parse(reader)); + return new BlockStateModelLoader.LoadedJson(resource.sourcePackId(), GsonHelper.parse(reader)); } catch(IOException e) { ModernFix.LOGGER.error("Couldn't load blockstate", e); return null; } }).filter(Objects::nonNull).collect(Collectors.toList()); } + + @Inject(method = "loadModels", at = @At("RETURN")) + private void storeTicker(ProfilerFiller profilerFiller, Map map, ModelBakery modelBakery, CallbackInfoReturnable cir) { + tickHandler = ((IExtendedModelBakery)modelBakery)::mfix$tick; + } + + @Override + public void mfix$tick() { + tickHandler.run(); + } } diff --git a/common/src/main/java/org/embeddedt/modernfix/duck/IBlockStateModelLoader.java b/common/src/main/java/org/embeddedt/modernfix/duck/IBlockStateModelLoader.java new file mode 100644 index 00000000..3d208ac6 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/duck/IBlockStateModelLoader.java @@ -0,0 +1,7 @@ +package org.embeddedt.modernfix.duck; + +import net.minecraft.resources.ResourceLocation; + +public interface IBlockStateModelLoader { + void loadSpecificBlock(ResourceLocation location); +} diff --git a/common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBaker.java b/common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBaker.java deleted file mode 100644 index b50f65d4..00000000 --- a/common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBaker.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.embeddedt.modernfix.duck; - -public interface IExtendedModelBaker { - /** - * Causes the ModelBaker to throw when it finds a missing model instead of proceeding with the bake. - * @return the previous value of this flag - */ - boolean throwOnMissingModel(boolean flag); -} diff --git a/common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBakery.java b/common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBakery.java index b6fb8947..255b15c2 100644 --- a/common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBakery.java +++ b/common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBakery.java @@ -1,17 +1,5 @@ package org.embeddedt.modernfix.duck; -import com.google.common.collect.ImmutableList; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.ModelResourceLocation; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; - public interface IExtendedModelBakery { - ImmutableList getBlockStatesForMRL(StateDefinition stateDefinition, ModelResourceLocation location); - BakedModel bakeDefault(ResourceLocation modelLocation, ModelState state); - UnbakedModel mfix$getUnbakedMissingModel(); + void mfix$tick(); } diff --git a/common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelManager.java b/common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelManager.java new file mode 100644 index 00000000..1bef1b89 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelManager.java @@ -0,0 +1,5 @@ +package org.embeddedt.modernfix.duck; + +public interface IExtendedModelManager { + void mfix$tick(); +} diff --git a/common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java b/common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java deleted file mode 100644 index 4c4de09f..00000000 --- a/common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java +++ /dev/null @@ -1,240 +0,0 @@ -package org.embeddedt.modernfix.dynamicresources; - -import com.google.common.collect.ImmutableSet; -import com.mojang.math.Transformation; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.ItemOverrides; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.client.resources.model.ModelResourceLocation; -import net.minecraft.core.Direction; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import org.embeddedt.modernfix.duck.IExtendedModelBakery; -import org.embeddedt.modernfix.ModernFix; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.function.BiFunction; -import java.util.stream.Collectors; - -public class DynamicBakedModelProvider implements Map { - /** - * The list of blacklisted resource locations that are never baked as top-level models. - * - * This is a hack to get around the fact that we don't really know exactly what models were supposed to end up - * in the baked registry ahead of time. - */ - private static final ImmutableSet BAKE_SKIPPED_TOPLEVEL = ImmutableSet.builder() - .add(ResourceLocation.fromNamespaceAndPath("custommachinery", "block/custom_machine_block")) - .build(); - public static DynamicBakedModelProvider currentInstance = null; - private final ModelBakery bakery; - private final Map bakedCache; - private final Map permanentOverrides; - private BakedModel missingModel; - private static final BakedModel SENTINEL = new BakedModel() { - @Override - public List getQuads(@Nullable BlockState state, @Nullable Direction direction, RandomSource random) { - return null; - } - - @Override - public boolean useAmbientOcclusion() { - return false; - } - - @Override - public boolean isGui3d() { - return false; - } - - @Override - public boolean usesBlockLight() { - return false; - } - - @Override - public boolean isCustomRenderer() { - return false; - } - - @Override - public TextureAtlasSprite getParticleIcon() { - return null; - } - - @Override - public ItemTransforms getTransforms() { - return null; - } - - @Override - public ItemOverrides getOverrides() { - return null; - } - }; - - public DynamicBakedModelProvider(ModelBakery bakery, Map cache) { - this.bakery = bakery; - this.bakedCache = cache; - this.permanentOverrides = Collections.synchronizedMap(new Object2ObjectOpenHashMap<>()); - if(currentInstance == null) - currentInstance = this; - } - - private static ModelBakery.BakedCacheKey vanillaKey(Object o) { - return new ModelBakery.BakedCacheKey((ResourceLocation)o, BlockModelRotation.X0_Y0.getRotation(), false); - } - @Override - public int size() { - return bakedCache.size(); - } - - @Override - public boolean isEmpty() { - return bakedCache.isEmpty(); - } - - @Override - public boolean containsKey(Object o) { - return permanentOverrides.getOrDefault(o, SENTINEL) != null; - } - - @Override - public boolean containsValue(Object o) { - return permanentOverrides.containsValue(o) || bakedCache.containsValue(o); - } - - private static boolean isVanillaTopLevelModel(ResourceLocation location) { - if(location instanceof ModelResourceLocation) { - try { - ModelResourceLocation mrl = (ModelResourceLocation)location; - ResourceLocation registryKey = ResourceLocation.fromNamespaceAndPath(mrl.getNamespace(), mrl.getPath()); - // check for standard inventory model - if(mrl.getVariant().equals("inventory") && BuiltInRegistries.ITEM.containsKey(registryKey)) - return true; - Optional blockOpt = BuiltInRegistries.BLOCK.getOptional(registryKey); - if(blockOpt.isPresent()) { - return ModelBakeryHelpers.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), mrl).size() > 0; - } - } catch(RuntimeException ignored) { - // can occur if the MRL is not valid for that blockstate, ignore - } - } - if(location.getNamespace().equals("minecraft") && location.getPath().equals("builtin/missing")) - return true; - return false; - } - - private BakedModel getMissingModel() { - BakedModel m = missingModel; - if(m == null) { - m = missingModel = ((IExtendedModelBakery)bakery).bakeDefault(ModelBakery.MISSING_MODEL_LOCATION, BlockModelRotation.X0_Y0); - } - return m; - } - - @Override - public BakedModel get(Object o) { - BakedModel model = permanentOverrides.getOrDefault(o, SENTINEL); - if(model != SENTINEL) - return model; - else { - try { - if(BAKE_SKIPPED_TOPLEVEL.contains((ResourceLocation)o)) - model = getMissingModel(); - else - model = ((IExtendedModelBakery)bakery).bakeDefault((ResourceLocation)o, BlockModelRotation.X0_Y0); - } catch(RuntimeException e) { - ModernFix.LOGGER.error("Exception baking {}: {}", o, e); - model = getMissingModel(); - } - if(model == getMissingModel()) { - // to correctly emulate the original map, we return null for missing models, unless they are top-level - model = isVanillaTopLevelModel((ResourceLocation)o) ? model : null; - permanentOverrides.put((ResourceLocation) o, model); - } - return model; - } - } - - @Override - public BakedModel put(ResourceLocation resourceLocation, BakedModel bakedModel) { - BakedModel m = permanentOverrides.put(resourceLocation, bakedModel); - if(m != null) - return m; - else - return bakedCache.get(vanillaKey(resourceLocation)); - } - - @Override - public BakedModel remove(Object o) { - BakedModel m = permanentOverrides.remove(o); - if(m != null) - return m; - return bakedCache.remove(vanillaKey(o)); - } - - @Override - public void putAll(@NotNull Map map) { - permanentOverrides.putAll(map); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @NotNull - @Override - public Set keySet() { - return bakedCache.keySet().stream().map(ModelBakery.BakedCacheKey::id).collect(Collectors.toSet()); - } - - @NotNull - @Override - public Collection values() { - return bakedCache.values(); - } - - @NotNull - @Override - public Set> entrySet() { - return bakedCache.entrySet().stream().map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getKey().id(), entry.getValue())).collect(Collectors.toSet()); - } - - @Nullable - @Override - public BakedModel replace(ResourceLocation key, BakedModel value) { - BakedModel existingOverride = permanentOverrides.get(key); - // as long as no valid override was put in (null can mean unable to load model, so we treat as invalid), replace - // the model - if(existingOverride == null) - return this.put(key, value); - else - return existingOverride; - } - - @Override - public void replaceAll(BiFunction function) { - Set overridenLocations = permanentOverrides.keySet(); - permanentOverrides.replaceAll(function); - boolean uvLock = BlockModelRotation.X0_Y0.isUvLocked(); - Transformation rotation = BlockModelRotation.X0_Y0.getRotation(); - bakedCache.replaceAll((loc, oldModel) -> { - if(loc.transformation() != rotation || loc.isUvLocked() != uvLock || overridenLocations.contains(loc.id())) - return oldModel; - else - return function.apply(loc.id(), oldModel); - }); - } -} diff --git a/common/src/main/java/org/embeddedt/modernfix/util/LRUMap.java b/common/src/main/java/org/embeddedt/modernfix/util/LRUMap.java new file mode 100644 index 00000000..1a842640 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/util/LRUMap.java @@ -0,0 +1,45 @@ +package org.embeddedt.modernfix.util; + +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; + +import java.util.Map; +import java.util.Set; + +public class LRUMap extends Object2ObjectLinkedOpenHashMap { + private Set permanentEntries = Set.of(); + public LRUMap(Map map) { + super(map); + } + + @Override + public V put(K k, V v) { + return putAndMoveToLast(k, v); + } + + @Override + public V get(Object k) { + return getAndMoveToLast((K)k); + } + + public void setPermanentEntries(Set permanentEntries) { + this.permanentEntries = permanentEntries; + } + + public void dropEntriesToMeetSize(int size) { + int prevSize = size(); + if(size() > size) { + var iterator = entrySet().iterator(); + while(size() > size && iterator.hasNext()) { + var entry = iterator.next(); + if(!this.permanentEntries.contains(entry.getKey())) { + iterator.remove(); + } + } + if(ModernFixPlatformHooks.INSTANCE.isDevEnv()) { + ModernFix.LOGGER.warn("Trimmed map from {} to {} entries", prevSize, size); + } + } + } +} diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ItemOverridesFabricMixin.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ItemOverridesFabricMixin.java deleted file mode 100644 index 140f0737..00000000 --- a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ItemOverridesFabricMixin.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.client.renderer.block.model.ItemOverrides; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.ModelBaker; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.resources.ResourceLocation; -import org.embeddedt.modernfix.annotation.ClientOnlyMixin; -import org.embeddedt.modernfix.duck.IExtendedModelBaker; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -@Mixin(ItemOverrides.class) -@ClientOnlyMixin -public class ItemOverridesFabricMixin { - /** - * @author embeddedt - * @reason servers insist on generating invalid item overrides that have missing models - */ - @WrapOperation(method = "bakeModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBaker;bake(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/resources/model/ModelState;)Lnet/minecraft/client/resources/model/BakedModel;")) - private BakedModel bake(ModelBaker instance, ResourceLocation resourceLocation, ModelState modelState, Operation original) { - boolean prevState = ((IExtendedModelBaker)instance).throwOnMissingModel(false); - try { - return original.call(instance, resourceLocation, modelState); - } finally { - ((IExtendedModelBaker)instance).throwOnMissingModel(prevState); - } - } -} diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/LoaderInstanceMixin.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/LoaderInstanceMixin.java deleted file mode 100644 index c9305264..00000000 --- a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/LoaderInstanceMixin.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources; - -import net.fabricmc.fabric.impl.client.model.ModelLoadingRegistryImpl; -import net.minecraft.client.resources.model.ModelBakery; -import org.embeddedt.modernfix.annotation.ClientOnlyMixin; -import org.embeddedt.modernfix.annotation.RequiresMod; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(ModelLoadingRegistryImpl.LoaderInstance.class) -@RequiresMod("fabric-models-v0") -@ClientOnlyMixin -public class LoaderInstanceMixin { - @Redirect(method = "finish", at = @At(value = "FIELD", target = "Lnet/fabricmc/fabric/impl/client/model/ModelLoadingRegistryImpl$LoaderInstance;loader:Lnet/minecraft/client/resources/model/ModelBakery;"), require = 0) - private void keepLoader(ModelLoadingRegistryImpl.LoaderInstance instance, ModelBakery value) { - /* allow loading models to happen later */ - } -} \ No newline at end of file diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java deleted file mode 100644 index d96b5aa5..00000000 --- a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java +++ /dev/null @@ -1,185 +0,0 @@ -package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources; - -import com.google.common.collect.ImmutableList; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.*; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import org.embeddedt.modernfix.ModernFix; -import org.embeddedt.modernfix.ModernFixClient; -import org.embeddedt.modernfix.annotation.ClientOnlyMixin; -import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration; -import org.embeddedt.modernfix.duck.IExtendedModelBaker; -import org.embeddedt.modernfix.duck.IExtendedModelBakery; -import org.embeddedt.modernfix.dynamicresources.ModelMissingException; -import org.spongepowered.asm.mixin.Final; -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.callback.CallbackInfoReturnable; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Function; - -@Mixin(value = ModelBakery.ModelBakerImpl.class, priority = 600) -@ClientOnlyMixin -public abstract class ModelBakerImplMixin implements IExtendedModelBaker { - private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading"); - @Shadow @Final private ModelBakery field_40571; - - @Shadow public abstract UnbakedModel getModel(ResourceLocation arg); - - @Shadow @Final private Function modelTextureGetter; - - private static final MethodHandle blockStateLoaderHandle; - static { - try { - blockStateLoaderHandle = MethodHandles.lookup().unreflect(ModelBakery.class.getDeclaredMethod( - FabricLoader.getInstance().getMappingResolver().mapMethodName( - "intermediary", - "net.minecraft.client.resources.model.ModelBakery", - "method_4716", - "(Lnet/minecraft/world/level/block/state/BlockState;)V" - ), - BlockState.class - )); - } catch(ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - private boolean throwIfMissing; - - @Override - public boolean throwOnMissingModel(boolean flag) { - boolean old = throwIfMissing; - throwIfMissing = flag; - return old; - } - - @Inject(method = "getModel", at = @At("HEAD"), cancellable = true) - private void obtainModel(ResourceLocation arg, CallbackInfoReturnable cir) { - if(debugDynamicModelLoading) - ModernFix.LOGGER.info("Baking {}", arg); - IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571; - if(arg instanceof ModelResourceLocation && arg != ModelBakery.MISSING_MODEL_LOCATION) { - // synchronized because we use topLevelModels - synchronized (this.field_40571) { - /* to emulate vanilla model loading, treat as top-level */ - Optional blockOpt = Objects.equals(((ModelResourceLocation)arg).getVariant(), "inventory") ? Optional.empty() : BuiltInRegistries.BLOCK.getOptional(ResourceLocation.fromNamespaceAndPath(arg.getNamespace(), arg.getPath())); - boolean invalidMRL = false; - if(blockOpt.isPresent()) { - /* load via lambda for mods that expect blockstate to get loaded */ - ImmutableList states; - try { - states = extendedBakery.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), (ModelResourceLocation)arg); - } catch(RuntimeException e) { - states = ImmutableList.of(); - invalidMRL = true; - // Fall back to getModel - cir.setReturnValue(this.field_40571.getModel(arg)); - } - for(BlockState state : states) { - try { - blockStateLoaderHandle.invokeExact(this.field_40571, state); - } catch(Throwable e) { - ModernFix.LOGGER.error("Error loading model", e); - } - } - } else { - this.field_40571.loadTopLevel((ModelResourceLocation)arg); - } - if(!invalidMRL) { - cir.setReturnValue(this.field_40571.topLevelModels.getOrDefault(arg, extendedBakery.mfix$getUnbakedMissingModel())); - // avoid leaks - this.field_40571.topLevelModels.clear(); - } - } - } else - cir.setReturnValue(this.field_40571.getModel(arg)); - UnbakedModel toReplace = cir.getReturnValue(); - if(true) { - for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) { - try { - toReplace = integration.onUnbakedModelPreBake(arg, toReplace, this.field_40571); - } catch(RuntimeException e) { - ModernFix.LOGGER.error("Exception firing model pre-bake event for {}", arg, e); - } - } - } - cir.setReturnValue(toReplace); - cir.getReturnValue().resolveParents(this.field_40571::getModel); - if(cir.getReturnValue() == extendedBakery.mfix$getUnbakedMissingModel()) { - if(arg != ModelBakery.MISSING_MODEL_LOCATION) { - if(debugDynamicModelLoading) - ModernFix.LOGGER.warn("Model {} not present", arg); - if(throwIfMissing) - throw new ModelMissingException(); - } - } - } - - @WrapOperation(method = "bake", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/UnbakedModel;bake(Lnet/minecraft/client/resources/model/ModelBaker;Ljava/util/function/Function;Lnet/minecraft/client/resources/model/ModelState;Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/BakedModel;")) - private BakedModel callBakedModelIntegration(UnbakedModel unbakedModel, ModelBaker baker, Function spriteGetter, ModelState state, ResourceLocation location, Operation operation) { - BakedModel model = operation.call(unbakedModel, baker, spriteGetter, state, location); - - for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) { - model = integration.onBakedModelLoad(location, unbakedModel, model, state, this.field_40571, spriteGetter); - } - - return model; - } - - /** - * @author embeddedt - * @reason emulate old function, to allow injectors to work - */ - /* - @Overwrite - public BakedModel bake(ResourceLocation arg, ModelState arg2) { - ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(arg, arg2.getRotation(), arg2.isUvLocked()); - BakedModel existing = this.field_40571.bakedCache.get(key); - if (existing != null) { - return existing; - } else { - synchronized (this.field_40571) { - if(debugDynamicModelLoading) - ModernFix.LOGGER.info("Baking {}", arg); - UnbakedModel iunbakedmodel = this.getModel(arg); - // TODO: make sure parent resolution doesn't re-run many times - iunbakedmodel.resolveParents(this::getModel); - BakedModel ibakedmodel = null; - if (iunbakedmodel instanceof BlockModel) { - BlockModel blockmodel = (BlockModel)iunbakedmodel; - if (blockmodel.getRootModel() == ModelBakery.GENERATION_MARKER) { - ibakedmodel = ModelBakery.ITEM_MODEL_GENERATOR.generateBlockModel(this.modelTextureGetter, blockmodel).bake((ModelBaker)this, blockmodel, this.modelTextureGetter, arg2, arg, false); - } - } - if(ibakedmodel == null) { - // leave the original assignment in the same spot so wrapping injectors work - // this means two bakes might happen for missing models, but not much we can do - ibakedmodel = iunbakedmodel.bake((ModelBaker)this, this.modelTextureGetter, arg2, arg); - IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571; - if(iunbakedmodel == extendedBakery.mfix$getUnbakedMissingModel()) { - // use a shared baked missing model - createBakedMissingModelIfNeeded(extendedBakery, iunbakedmodel, arg2, arg); - ibakedmodel = extendedBakery.getBakedMissingModel(); - } - } - this.field_40571.bakedCache.put(key, ibakedmodel); - return ibakedmodel; - } - } - } - - */ -} 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 deleted file mode 100644 index 39ac1fb3..00000000 --- a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java +++ /dev/null @@ -1,328 +0,0 @@ -package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.RemovalNotification; -import com.google.common.collect.ForwardingMap; -import com.google.common.collect.ImmutableList; -import net.minecraft.client.Minecraft; -import net.minecraft.client.color.block.BlockColors; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.*; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.Property; -import org.embeddedt.modernfix.ModernFix; -import org.embeddedt.modernfix.ModernFixClient; -import org.embeddedt.modernfix.annotation.ClientOnlyMixin; -import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration; -import org.embeddedt.modernfix.duck.IExtendedModelBaker; -import org.embeddedt.modernfix.duck.IExtendedModelBakery; -import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider; -import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers; -import org.objectweb.asm.Opcodes; -import org.slf4j.Logger; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -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.ModifyArg; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.*; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.*; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; - -/* low priority so that our injectors are added after other mods' */ -@Mixin(value = ModelBakery.class, priority = 1100) -@ClientOnlyMixin -public abstract class ModelBakeryMixin implements IExtendedModelBakery { - - private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading"); - - @Shadow @Final @Mutable public Map unbakedCache; - - @Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION; - - @Shadow @Final private Set loadingStack; - - @Shadow protected abstract void loadModel(ResourceLocation blockstateLocation) throws Exception; - - @Shadow @Final @Mutable - private Map bakedTopLevelModels; - - @Shadow @Final @Mutable private Map bakedCache; - - @Shadow @Final @Mutable private BlockColors blockColors; - @Shadow @Final private static Logger LOGGER; - - @Shadow - public abstract void loadTopLevel(ModelResourceLocation modelResourceLocation); - - @Shadow public abstract UnbakedModel getModel(ResourceLocation resourceLocation); - - private Cache loadedBakedModels; - - private Cache loadedModels; - - private HashMap smallLoadingCache = new HashMap<>(); - - private boolean ignoreModelLoad; - - // disable fabric recursion - @SuppressWarnings("unused") - private boolean fabric_enableGetOrLoadModelGuard; - - - @Redirect(method = "", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/resources/model/ModelBakery;blockColors:Lnet/minecraft/client/color/block/BlockColors;")) - private void replaceTopLevelBakedModels(ModelBakery bakery, BlockColors val) { - // we can handle recursion in getModel without issues - fabric_enableGetOrLoadModelGuard = false; - this.blockColors = val; - this.loadedBakedModels = CacheBuilder.newBuilder() - .expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS) - .maximumSize(ModelBakeryHelpers.MAX_BAKED_MODEL_COUNT) - .concurrencyLevel(8) - .removalListener(this::onModelRemoved) - .softValues() - .build(); - this.loadedModels = CacheBuilder.newBuilder() - .expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS) - .maximumSize(ModelBakeryHelpers.MAX_UNBAKED_MODEL_COUNT) - .concurrencyLevel(8) - .removalListener(this::onModelRemoved) - //.softValues() - .build(); - this.bakedCache = loadedBakedModels.asMap(); - ConcurrentMap unbakedCacheBackingMap = loadedModels.asMap(); - this.unbakedCache = new ForwardingMap() { - @Override - protected Map delegate() { - return unbakedCacheBackingMap; - } - - @Override - public UnbakedModel put(ResourceLocation key, UnbakedModel value) { - smallLoadingCache.put(key, value); - return super.put(key, value); - } - }; - this.bakedTopLevelModels = new DynamicBakedModelProvider((ModelBakery)(Object)this, bakedCache); - } - - @Inject(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", ordinal = 0)) - private void ignoreFutureModelLoads(CallbackInfo ci) { - this.ignoreModelLoad = true; - } - - private void onModelRemoved(RemovalNotification notification) { - if(!debugDynamicModelLoading) - return; - Object k = notification.getKey(); - if(k == null) - return; - ResourceLocation rl; - boolean baked = false; - if(k instanceof ResourceLocation) { - rl = (ResourceLocation)k; - } else { - rl = ((ModelBakery.BakedCacheKey)k).id(); - baked = true; - } - /* can fire when a model is replaced */ - if(!baked && this.loadedModels.getIfPresent(rl) != null) - return; - ModernFix.LOGGER.warn("Evicted {} model {}", baked ? "baked" : "unbaked", rl); - } - - private UnbakedModel missingModel; - - private Set blockStateFiles; - private Set modelFiles; - - @ModifyArg(method = "", at = @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 0), index = 1) - private Object captureMissingModel(Object model) { - this.missingModel = (UnbakedModel)model; - this.blockStateFiles = new HashSet<>(); - this.modelFiles = new HashSet<>(); - return this.missingModel; - } - - /** - * @author embeddedt - * @reason don't actually load most models - */ - @Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBakery;loadTopLevel(Lnet/minecraft/client/resources/model/ModelResourceLocation;)V")) - private void addTopLevelFile(ModelBakery bakery, ModelResourceLocation location) { - if(location == MISSING_MODEL_LOCATION || !this.ignoreModelLoad) { - loadTopLevel(location); - } - } - - @Redirect(method = "", at = @At(value = "INVOKE", target = "Ljava/util/Map;forEach(Ljava/util/function/BiConsumer;)V", ordinal = 0)) - private void fetchStaticDefinitions(Map> map, BiConsumer> func) { - /* no-op */ - } - - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;", ordinal = 0)) - private ImmutableList fetchBlocks(StateDefinition def) { - /* no-op */ - return ImmutableList.of(); - } - - /** - * Make a copy of the top-level model list to avoid CME if more models get loaded here. - */ - @Redirect(method = "", at = @At(value = "INVOKE", target = "Ljava/util/Map;values()Ljava/util/Collection;", ordinal = 0)) - private Collection copyTopLevelModelList(Map map) { - return new ArrayList<>(map.values()); - } - - private BiFunction textureGetter; - - @Inject(method = "bakeModels", at = @At("HEAD")) - private void captureGetter(BiFunction getter, CallbackInfo ci) { - this.ignoreModelLoad = false; - textureGetter = getter; - DynamicBakedModelProvider.currentInstance = (DynamicBakedModelProvider)this.bakedTopLevelModels; - } - - @Redirect(method = "bakeModels", at = @At(value = "INVOKE", target = "Ljava/util/Map;keySet()Ljava/util/Set;")) - private Set skipBake(Map instance) { - Set modelSet = new HashSet<>(instance.keySet()); - if(modelSet.size() > 0) - ModernFix.LOGGER.info("Early baking {} models", modelSet.size()); - return modelSet; - } - - /** - * Use the already loaded missing model instead of the cache entry (which will probably get evicted). - */ - @Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 1)) - private Object getMissingModel(Map map, Object rl) { - if(rl == MISSING_MODEL_LOCATION && map == unbakedCache) - return missingModel; - return unbakedCache.get(rl); - } - - @ModifyVariable(method = "cacheAndQueueDependencies", at = @At("HEAD"), argsOnly = true) - private UnbakedModel fireUnbakedEvent(UnbakedModel model, ResourceLocation location) { - for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) { - try { - model = integration.onUnbakedModelLoad(location, model, (ModelBakery)(Object)this); - } catch(RuntimeException e) { - ModernFix.LOGGER.error("Exception firing model load event for {}", location, e); - } - } - return model; - } - - private int mfix$nestedLoads = 0; - - /** - * @author embeddedt - * @reason synchronize - */ - @Inject(method = "getModel", at = @At("HEAD"), cancellable = true) - public void getOrLoadModelDynamic(ResourceLocation modelLocation, CallbackInfoReturnable cir) { - if(modelLocation.equals(MISSING_MODEL_LOCATION)) { - cir.setReturnValue(missingModel); - return; - } - UnbakedModel existing = this.unbakedCache.get(modelLocation); - if (existing != null) { - cir.setReturnValue(existing); - } else { - synchronized(this) { - if (this.loadingStack.contains(modelLocation)) { - throw new IllegalStateException("Circular reference while loading " + modelLocation); - } else { - this.loadingStack.add(modelLocation); - UnbakedModel iunbakedmodel = missingModel; - - while(!this.loadingStack.isEmpty()) { - ResourceLocation resourcelocation = this.loadingStack.iterator().next(); - - mfix$nestedLoads++; - try { - existing = this.unbakedCache.get(resourcelocation); - if (existing == null) { - if(debugDynamicModelLoading) - LOGGER.info("Loading {}", resourcelocation); - this.loadModel(resourcelocation); - } else - smallLoadingCache.put(resourcelocation, existing); - } catch (ModelBakery.BlockStateDefinitionException var9) { - LOGGER.warn(var9.getMessage()); - this.unbakedCache.put(resourcelocation, iunbakedmodel); - smallLoadingCache.put(resourcelocation, iunbakedmodel); - } catch (Exception var10) { - LOGGER.warn("Unable to load model: '{}' referenced from: {}: {}", resourcelocation, modelLocation, var10); - this.unbakedCache.put(resourcelocation, iunbakedmodel); - smallLoadingCache.put(resourcelocation, iunbakedmodel); - } finally { - mfix$nestedLoads--; - this.loadingStack.remove(resourcelocation); - } - } - - // We have to get the result from the temporary cache used for a model load - // As in pathological cases (e.g. Pedestals on 1.19) unbakedCache can lose - // the model immediately - UnbakedModel result = smallLoadingCache.getOrDefault(modelLocation, iunbakedmodel); - try { - // required as some mods (e.g. EBE) call bake directly on the returned model, without resolving parents themselves - result.resolveParents(this::getModel); - } catch(RuntimeException ignored) {} - // We are done with loading, so clear this cache to allow GC of any unneeded models - if(mfix$nestedLoads == 0) - smallLoadingCache.clear(); - cir.setReturnValue(result); - } - } - } - } - - private , V extends T> BlockState setPropertyGeneric(BlockState state, Property prop, Object o) { - return state.setValue(prop, (V)o); - } - @Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;")) - private ImmutableList loadOnlyRelevantBlockState(StateDefinition stateDefinition, ResourceLocation location) { - if(!(location instanceof ModelResourceLocation) || Minecraft.getInstance().level == null) - return stateDefinition.getPossibleStates(); - return ModelBakeryHelpers.getBlockStatesForMRL(stateDefinition, (ModelResourceLocation)location); - } - - @Override - public BakedModel bakeDefault(ResourceLocation modelLocation, ModelState state) { - ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(modelLocation, BlockModelRotation.X0_Y0.getRotation(), BlockModelRotation.X0_Y0.isUvLocked()); - BakedModel m = loadedBakedModels.getIfPresent(key); - if(m != null) - return m; - ModelBakery self = (ModelBakery) (Object) this; - ModelBaker theBaker = self.new ModelBakerImpl(textureGetter, modelLocation); - ((IExtendedModelBaker)theBaker).throwOnMissingModel(true); - synchronized(this) { m = theBaker.bake(modelLocation, state); } - if(m != null) - loadedBakedModels.put(key, m); - return m; - } - - @Override - public ImmutableList getBlockStatesForMRL(StateDefinition stateDefinition, ModelResourceLocation location) { - return loadOnlyRelevantBlockState(stateDefinition, location); - } - - public UnbakedModel mfix$getUnbakedMissingModel() { - return missingModel; - } -} diff --git a/gradle.properties b/gradle.properties index 21d8c10c..23babdbe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ junit_version=5.10.0-M1 mixinextras_version=0.3.2 mod_id=modernfix -minecraft_version=24w21a +minecraft_version=1.21-pre1 enabled_platforms=fabric forge_version=20.6.42-beta # parchment_version=2023.07.09 @@ -15,7 +15,7 @@ rei_version=13.0.678 ctm_version=1.20.1-1.1.8+4 kubejs_version=1902.6.0-build.142 rhino_version=1902.2.2-build.268 -supported_minecraft_versions=24w21a +supported_minecraft_versions=1.21-pre1 fabric_loader_version=0.15.11 fabric_api_version=0.98.2+1.21