diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java index 51ad6bf9..c2b3de68 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java @@ -1,22 +1,37 @@ package org.embeddedt.modernfix.forge.dynresources; import com.google.common.collect.ForwardingMap; +import com.google.common.collect.Sets; +import com.google.common.graph.GraphBuilder; +import com.google.common.graph.MutableGraph; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.fml.ModContainer; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.forgespi.language.IModInfo; import net.minecraftforge.registries.ForgeRegistries; import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; import org.jetbrains.annotations.Nullable; import java.util.HashSet; import java.util.Map; +import java.util.Optional; import java.util.Set; +/** + * Stores a list of all known default block/item models in the game, and provides a namespaced version + * of the model registry that emulates vanilla keySet behavior. + */ public class ModelBakeEventHelper { - public static Map wrapRegistry(Map modelRegistry) { - Set topLevelModelLocations = new HashSet<>(modelRegistry.keySet()); + private final Map modelRegistry; + private final Set topLevelModelLocations; + private final MutableGraph dependencyGraph; + public ModelBakeEventHelper(Map modelRegistry) { + this.modelRegistry = modelRegistry; + this.topLevelModelLocations = new HashSet<>(modelRegistry.keySet()); for(Block block : ForgeRegistries.BLOCKS) { for(BlockState state : block.getStateDefinition().getPossibleStates()) { topLevelModelLocations.add(ModelLocationCache.get(state)); @@ -25,6 +40,28 @@ public class ModelBakeEventHelper { for(Item item : ForgeRegistries.ITEMS) { topLevelModelLocations.add(ModelLocationCache.get(item)); } + this.dependencyGraph = GraphBuilder.undirected().build(); + ModList.get().forEachModContainer((id, mc) -> { + this.dependencyGraph.addNode(id); + }); + for(String id : this.dependencyGraph.nodes()) { + Optional mContainer = ModList.get().getModContainerById(id); + if(mContainer.isPresent()) { + for(IModInfo.ModVersion version : mContainer.get().getModInfo().getDependencies()) { + this.dependencyGraph.putEdge(id, version.getModId()); + } + } + } + } + + public Map wrapRegistry(String modId) { + final Set modIdsToInclude = new HashSet<>(); + modIdsToInclude.add(modId); + try { + modIdsToInclude.addAll(this.dependencyGraph.adjacentNodes(modId)); + } catch(IllegalArgumentException ignored) { /* sanity check */ } + modIdsToInclude.remove("minecraft"); + Set ourModelLocations = Sets.filter(this.topLevelModelLocations, loc -> modIdsToInclude.contains(loc.getNamespace())); return new ForwardingMap() { @Override protected Map delegate() { @@ -33,18 +70,12 @@ public class ModelBakeEventHelper { @Override public Set keySet() { - return topLevelModelLocations; + return ourModelLocations; } @Override public boolean containsKey(@Nullable Object key) { - return topLevelModelLocations.contains(key) || super.containsKey(key); - } - - @Override - public BakedModel put(ResourceLocation key, BakedModel value) { - topLevelModelLocations.add(key); - return super.put(key, value); + return ourModelLocations.contains(key) || super.containsKey(key); } }; } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ForgeHooksClientMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ForgeHooksClientMixin.java index 1e130b7f..887a9e04 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ForgeHooksClientMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ForgeHooksClientMixin.java @@ -3,11 +3,18 @@ package org.embeddedt.modernfix.forge.mixin.perf.dynamic_resources; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.event.ModelBakeEvent; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.ModContainer; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.ModLoader; +import net.minecraftforge.fml.common.ObfuscationReflectionHelper; import org.embeddedt.modernfix.forge.dynresources.ModelBakeEventHelper; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Redirect; +import java.lang.reflect.Method; import java.util.Map; @Mixin(ForgeHooksClient.class) @@ -15,8 +22,21 @@ public class ForgeHooksClientMixin { /** * Generate a more realistic keySet that contains every item and block model location, to help with mod compat. */ - @ModifyVariable(method = "onModelBake", at = @At("HEAD"), ordinal = 0, argsOnly = true) - private static Map generateModelKeySet(Map modelRegistry) { - return ModelBakeEventHelper.wrapRegistry(modelRegistry); + @Redirect(method = "onModelBake", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/ModLoader;postEvent(Lnet/minecraftforge/eventbus/api/Event;)V")) + private static void postNamespacedKeySetEvent(ModLoader loader, Event event) { + if(!ModLoader.isLoadingStateValid()) + return; + ModelBakeEvent bakeEvent = ((ModelBakeEvent)event); + ModelBakeEventHelper helper = new ModelBakeEventHelper(bakeEvent.getModelRegistry()); + Method acceptEv = ObfuscationReflectionHelper.findMethod(ModContainer.class, "acceptEvent", Event.class); + ModList.get().forEachModContainer((id, mc) -> { + Map newRegistry = helper.wrapRegistry(id); + ModelBakeEvent postedEvent = new ModelBakeEvent(bakeEvent.getModelManager(), newRegistry, bakeEvent.getModelLoader()); + try { + acceptEv.invoke(mc, postedEvent); + } catch(ReflectiveOperationException e) { + e.printStackTrace(); + } + }); } }