Only provide each mod its own model list in ModelBakeEvent

This commit is contained in:
embeddedt 2023-07-06 21:12:59 -04:00
parent 1b6880ed9f
commit 16d317af97
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
2 changed files with 65 additions and 14 deletions

View File

@ -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<ResourceLocation, BakedModel> wrapRegistry(Map<ResourceLocation, BakedModel> modelRegistry) {
Set<ResourceLocation> topLevelModelLocations = new HashSet<>(modelRegistry.keySet());
private final Map<ResourceLocation, BakedModel> modelRegistry;
private final Set<ResourceLocation> topLevelModelLocations;
private final MutableGraph<String> dependencyGraph;
public ModelBakeEventHelper(Map<ResourceLocation, BakedModel> 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<? extends ModContainer> mContainer = ModList.get().getModContainerById(id);
if(mContainer.isPresent()) {
for(IModInfo.ModVersion version : mContainer.get().getModInfo().getDependencies()) {
this.dependencyGraph.putEdge(id, version.getModId());
}
}
}
}
public Map<ResourceLocation, BakedModel> wrapRegistry(String modId) {
final Set<String> modIdsToInclude = new HashSet<>();
modIdsToInclude.add(modId);
try {
modIdsToInclude.addAll(this.dependencyGraph.adjacentNodes(modId));
} catch(IllegalArgumentException ignored) { /* sanity check */ }
modIdsToInclude.remove("minecraft");
Set<ResourceLocation> ourModelLocations = Sets.filter(this.topLevelModelLocations, loc -> modIdsToInclude.contains(loc.getNamespace()));
return new ForwardingMap<ResourceLocation, BakedModel>() {
@Override
protected Map<ResourceLocation, BakedModel> delegate() {
@ -33,18 +70,12 @@ public class ModelBakeEventHelper {
@Override
public Set<ResourceLocation> 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);
}
};
}

View File

@ -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<ResourceLocation, BakedModel> generateModelKeySet(Map<ResourceLocation, BakedModel> 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<ResourceLocation, BakedModel> newRegistry = helper.wrapRegistry(id);
ModelBakeEvent postedEvent = new ModelBakeEvent(bakeEvent.getModelManager(), newRegistry, bakeEvent.getModelLoader());
try {
acceptEv.invoke(mc, postedEvent);
} catch(ReflectiveOperationException e) {
e.printStackTrace();
}
});
}
}