Start rewriting dynamic resources
This commit is contained in:
parent
09ea5e1dc9
commit
545fb386a0
|
|
@ -1,78 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.color.block.BlockColors;
|
||||
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
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.ModernFix;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.duck.IBlockStateModelLoader;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers;
|
||||
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<Block, BlockState> stateDefinition);
|
||||
|
||||
@Shadow @Mutable @Final private Object2IntMap<BlockState> modelGroups;
|
||||
|
||||
private ImmutableList<BlockState> filteredStates;
|
||||
|
||||
@Inject(method = "<init>", 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(ModelResourceLocation location) {
|
||||
var optionalBlock = BuiltInRegistries.BLOCK.getOptional(location.id());
|
||||
if(optionalBlock.isPresent()) {
|
||||
try {
|
||||
// Only filter states if we are in a world and not in the loading overlay
|
||||
filteredStates = (Minecraft.getInstance().getOverlay() == null && Minecraft.getInstance().level != null) ? ModelBakeryHelpers.getBlockStatesForMRL(optionalBlock.get().getStateDefinition(), location) : null;
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Exception filtering states on {}", location, e);
|
||||
filteredStates = null;
|
||||
}
|
||||
try {
|
||||
this.loadBlockStateDefinitions(location.id(), optionalBlock.get().getStateDefinition());
|
||||
} finally {
|
||||
filteredStates = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "loadAllBlockStates", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/DefaultedRegistry;iterator()Ljava/util/Iterator;"))
|
||||
private Iterator<?> skipIteratingBlocks(DefaultedRegistry instance) {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
@Redirect(method = "loadBlockStateDefinitions", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;"))
|
||||
private ImmutableList<BlockState> getFilteredStates(StateDefinition<Block, BlockState> instance) {
|
||||
return this.filteredStates != null ? this.filteredStates : instance.getPossibleStates();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,91 +1,38 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||
import net.minecraft.client.renderer.ItemModelShaper;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||
import org.embeddedt.modernfix.util.DynamicInt2ObjectMap;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ItemModelShaper.class)
|
||||
@ClientOnlyMixin
|
||||
public abstract class ItemModelShaperMixin {
|
||||
|
||||
@Shadow public abstract ModelManager getModelManager();
|
||||
|
||||
@Shadow @Final @Mutable private Int2ObjectMap<BakedModel> shapesCache;
|
||||
|
||||
private Map<Item, ModelResourceLocation> overrideLocationsVanilla;
|
||||
|
||||
public ItemModelShaperMixin() {
|
||||
super();
|
||||
}
|
||||
|
||||
private static final ModelResourceLocation SENTINEL_VANILLA = new ModelResourceLocation(ResourceLocation.fromNamespaceAndPath("modernfix", "sentinel"), "sentinel");
|
||||
|
||||
private final DynamicModelCache<Item> mfix$itemModelCache = new DynamicModelCache<>(k -> this.mfix$getModelForItem((Item)k), true);
|
||||
@Shadow @Final @Mutable private Map<ResourceLocation, BakedModel> modelToBakedModel;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void replaceLocationMap(CallbackInfo ci) {
|
||||
overrideLocationsVanilla = new HashMap<>();
|
||||
this.shapesCache = new DynamicInt2ObjectMap<>(index -> getModelManager().getModel(ModelLocationCache.get(Item.byId(index))));
|
||||
private void initializeLazyCache(CallbackInfo ci) {
|
||||
this.modelToBakedModel = new Object2ObjectLinkedOpenHashMap<>(this.modelToBakedModel);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private ModelResourceLocation mfix$getLocation(Item item) {
|
||||
ModelResourceLocation map = overrideLocationsVanilla.getOrDefault(item, SENTINEL_VANILLA);
|
||||
if(map == SENTINEL_VANILLA) {
|
||||
/* generate the appropriate location from our cache */
|
||||
map = ModelLocationCache.get(item);
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason Prevent all baked item models from being cached forever. We can safely mutate the map here as vanilla
|
||||
* also uses computeIfAbsent, which means multithreaded access is not safe in vanilla either.
|
||||
*/
|
||||
@Inject(method = "getItemModel(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/BakedModel;", at = @At(value = "RETURN"))
|
||||
private void limitCacheSize(ResourceLocation resourceLocation, CallbackInfoReturnable<BakedModel> cir) {
|
||||
var map = modelToBakedModel;
|
||||
if (map instanceof Object2ObjectLinkedOpenHashMap<ResourceLocation, BakedModel> linkedMap && linkedMap.size() > 1000) {
|
||||
linkedMap.removeFirst();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
private BakedModel mfix$getModelForItem(Item item) {
|
||||
ModelResourceLocation map = mfix$getLocation(item);
|
||||
return map == null ? null : getModelManager().getModel(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason Get the stored location for that item and meta, and get the model
|
||||
* from that location from the model manager.
|
||||
**/
|
||||
@Overwrite
|
||||
public BakedModel getItemModel(Item item) {
|
||||
return this.mfix$itemModelCache.get(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason Don't get all models during init (with dynamic loading, that would
|
||||
* generate them all). Just store location instead.
|
||||
**/
|
||||
@Overwrite
|
||||
public void register(Item item, ModelResourceLocation location) {
|
||||
overrideLocationsVanilla.put(item, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason Disable cache rebuilding (with dynamic loading, that would generate
|
||||
* all models).
|
||||
**/
|
||||
@Overwrite
|
||||
public void rebuildCache() {
|
||||
this.mfix$itemModelCache.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import net.minecraft.client.renderer.ItemModelShaper;
|
||||
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(ItemRenderer.class)
|
||||
@ClientOnlyMixin
|
||||
public class ItemRendererMixin {
|
||||
/**
|
||||
* Don't waste space putting all these locations into the cache, compute them on demand later.
|
||||
*/
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemModelShaper;register(Lnet/minecraft/world/item/Item;Lnet/minecraft/client/resources/model/ModelResourceLocation;)V"))
|
||||
private void skipDefaultRegistration(ItemModelShaper shaper, Item item, ModelResourceLocation mrl) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,233 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
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.BlockModelRotation;
|
||||
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.ModernFixClient;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
||||
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;
|
||||
|
||||
@Unique
|
||||
private static final boolean DEBUG_MODEL_LOADS = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
|
||||
|
||||
/**
|
||||
* Bake a model using the provided texture getter and location. The model is stored in {@link ModelBakeryMixin#bakedTopLevelModels}.
|
||||
*/
|
||||
@Shadow(aliases = "lambda$bakeModels$6") protected abstract void method_61072(ModelBakery.TextureGetter getter, ModelResourceLocation location, UnbakedModel model);
|
||||
|
||||
@Shadow @Mutable @Final private Map<ModelResourceLocation, BakedModel> bakedTopLevelModels;
|
||||
@Shadow @Mutable @Final public Map<ModelResourceLocation, UnbakedModel> topLevelModels;
|
||||
@Shadow @Mutable @Final private Map<ResourceLocation, UnbakedModel> unbakedCache;
|
||||
@Shadow @Mutable @Final public Map<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
|
||||
|
||||
@Shadow protected abstract void loadItemModelAndDependencies(ResourceLocation resourceLocation);
|
||||
|
||||
|
||||
@Shadow @Final public static ModelResourceLocation MISSING_MODEL_VARIANT;
|
||||
|
||||
private final Map<ModelResourceLocation, BakedModel> mfix$emulatedBakedRegistry = new DynamicOverridableMap<>(ModelResourceLocation.class, this::loadBakedModelDynamic);
|
||||
|
||||
@Override
|
||||
public UnbakedModel mfix$loadUnbakedModelDynamic(ModelResourceLocation location) {
|
||||
if(location.equals(MISSING_MODEL_VARIANT)) {
|
||||
return missingModel;
|
||||
}
|
||||
modelBakeryLock.lock();
|
||||
try {
|
||||
UnbakedModel existing = this.topLevelModels.get(location);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
if(DEBUG_MODEL_LOADS) {
|
||||
ModernFix.LOGGER.info("Loading model {}", location);
|
||||
}
|
||||
if(location.variant().equals("inventory")) {
|
||||
this.loadItemModelAndDependencies(location.id());
|
||||
} else {
|
||||
((IBlockStateModelLoader)dynamicLoader).loadSpecificBlock(location);
|
||||
}
|
||||
return this.topLevelModels.getOrDefault(location, this.missingModel);
|
||||
} finally {
|
||||
modelBakeryLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnbakedModel mfix$getMissingModel() {
|
||||
return 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 = mfix$loadUnbakedModelDynamic(location);
|
||||
if(prototype == missingModel) {
|
||||
model = bakedMissingModel;
|
||||
} else {
|
||||
prototype.resolveDependencies(this::getModel);
|
||||
if(DEBUG_MODEL_LOADS) {
|
||||
ModernFix.LOGGER.info("Baking model {}", location);
|
||||
}
|
||||
this.method_61072(this.textureGetter, location, prototype);
|
||||
model = bakedTopLevelModels.remove(location);
|
||||
if(model == null) {
|
||||
ModernFix.LOGGER.error("Failed to load model " + location);
|
||||
model = bakedMissingModel;
|
||||
}
|
||||
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
|
||||
model = integration.onBakedModelLoad(location, prototype, model, BlockModelRotation.X0_Y0, (ModelBakery)(Object)this, this.textureGetter);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
modelBakeryLock.unlock();
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(method = "<init>", 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;
|
||||
}
|
||||
|
||||
@WrapOperation(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/BlockStateModelLoader;loadAllBlockStates()V"))
|
||||
private void noInitialBlockStateLoad(BlockStateModelLoader instance, Operation<Void> original) {
|
||||
dynamicLoader = instance;
|
||||
original.call(instance);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", 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);
|
||||
}
|
||||
|
||||
private boolean inInitialLoad = true;
|
||||
|
||||
@Inject(method = "bakeModels", at = @At("RETURN"))
|
||||
private void onInitialBakeFinish(ModelBakery.TextureGetter textureGetter, CallbackInfo ci) {
|
||||
var permanentMRLs = new ObjectOpenHashSet<>(this.bakedTopLevelModels.keySet());
|
||||
((LRUMap<ModelResourceLocation, BakedModel>)this.bakedTopLevelModels).setPermanentEntries(permanentMRLs);
|
||||
ModernFix.LOGGER.info("Dynamic model bakery initial baking finished, with {} permanent top level baked models", this.bakedTopLevelModels.size());
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void onInitialLoadFinish(BlockColors blockColors, ProfilerFiller profilerFiller, Map map, Map map2, CallbackInfo ci) {
|
||||
var permanentMRLs = new ObjectOpenHashSet<>(this.topLevelModels.keySet());
|
||||
((LRUMap<ModelResourceLocation, UnbakedModel>)this.topLevelModels).setPermanentEntries(permanentMRLs);
|
||||
ModernFix.LOGGER.info("Dynamic model bakery loading finished, 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$finishLoading() {
|
||||
inInitialLoad = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mfix$tick() {
|
||||
if(inInitialLoad) {
|
||||
return;
|
||||
}
|
||||
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<ModelResourceLocation, BakedModel> getBakedTopLevelModels() {
|
||||
return this.mfix$emulatedBakedRegistry;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import net.minecraft.client.resources.model.ModelDiscovery;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(ModelDiscovery.class)
|
||||
@ClientOnlyMixin
|
||||
public class ModelDiscoveryMixin {
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason nothing is mandatory at launch, we load things dynamically
|
||||
*/
|
||||
@Overwrite
|
||||
private static Set<ModelResourceLocation> listMandatoryModels() {
|
||||
return new HashSet<>();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +1,24 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
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.ModelBakery;
|
||||
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
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.CacheUtil;
|
||||
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.Coerce;
|
||||
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;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
|
@ -42,9 +29,6 @@ import java.util.concurrent.Executor;
|
|||
public class ModelManagerMixin implements IExtendedModelManager {
|
||||
@Shadow private Map<ResourceLocation, BakedModel> bakedRegistry;
|
||||
|
||||
@Unique
|
||||
private Runnable tickHandler = () -> {};
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void injectDummyBakedRegistry(CallbackInfo ci) {
|
||||
if(this.bakedRegistry == null) {
|
||||
|
|
@ -54,60 +38,21 @@ public class ModelManagerMixin implements IExtendedModelManager {
|
|||
|
||||
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockModels(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||
private CompletableFuture<Map<ResourceLocation, BlockModel>> deferBlockModelLoad(ResourceManager manager, Executor executor) {
|
||||
var cache = CacheUtil.<ResourceLocation, BlockModel>simpleCacheForLambda(location -> loadSingleBlockModel(manager, location), 100L);
|
||||
return CompletableFuture.completedFuture(new LambdaMap<>(location -> cache.getUnchecked(location)));
|
||||
return CompletableFuture.completedFuture(Map.of());
|
||||
}
|
||||
|
||||
// TODO - make blockstate unbaked model loading lazy
|
||||
/*
|
||||
@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<Map<ResourceLocation, List<BlockStateModelLoader.LoadedJson>>> deferBlockStateLoad(ResourceManager manager, Executor executor) {
|
||||
var cache = CacheUtil.<ResourceLocation, List<BlockStateModelLoader.LoadedJson>>simpleCacheForLambda(location -> loadSingleBlockState(manager, location), 100L);
|
||||
return CompletableFuture.completedFuture(new LambdaMap<>(location -> cache.getUnchecked(location)));
|
||||
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockStates(Lnet/minecraft/client/resources/model/BlockStateModelLoader;Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||
private CompletableFuture<BlockStateModelLoader.LoadedModels> deferBlockStateLoad(BlockStateModelLoader blockStateModelLoader, ResourceManager resourceManager, Executor executor) {
|
||||
return CompletableFuture.completedFuture(new BlockStateModelLoader.LoadedModels(Map.of()));
|
||||
}
|
||||
*/
|
||||
|
||||
@Redirect(method = "loadModels", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;"))
|
||||
private ImmutableList<BlockState> skipCollection(StateDefinition<Block, BlockState> definition) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
private BlockModel loadSingleBlockModel(ResourceManager manager, ResourceLocation location) {
|
||||
return manager.getResource(location).map(resource -> {
|
||||
try (BufferedReader reader = resource.openAsReader()) {
|
||||
return BlockModel.fromStream(reader);
|
||||
} catch(IOException e) {
|
||||
ModernFix.LOGGER.error("Couldn't load model", e);
|
||||
return null;
|
||||
}
|
||||
}).orElse(null);
|
||||
}
|
||||
|
||||
/*
|
||||
private List<BlockStateModelLoader.LoadedJson> loadSingleBlockState(ResourceManager manager, ResourceLocation location) {
|
||||
return manager.getResourceStack(location).stream().map(resource -> {
|
||||
try (BufferedReader reader = resource.openAsReader()) {
|
||||
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<ResourceLocation, AtlasSet.StitchResult> map, ModelBakery modelBakery, Object2IntMap<BlockState> object2IntMap, CallbackInfoReturnable<?> cir) {
|
||||
tickHandler = ((IExtendedModelBakery)modelBakery)::mfix$tick;
|
||||
}
|
||||
|
||||
@Inject(method = "apply", at = @At("RETURN"))
|
||||
private void freezeBakery(@Coerce Object reloadState, ProfilerFiller profilerFiller, CallbackInfo ci, @Local(ordinal = 0) ModelBakery bakery) {
|
||||
((IExtendedModelBakery)bakery).mfix$finishLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mfix$tick() {
|
||||
tickHandler.run();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ accessible field net/minecraft/client/renderer/texture/Stitcher$Holder width I
|
|||
accessible field net/minecraft/client/renderer/texture/Stitcher$Holder height I
|
||||
accessible field net/minecraft/network/syncher/EntityDataAccessor id I
|
||||
mutable field net/minecraft/network/syncher/EntityDataAccessor id I
|
||||
accessible method net/minecraft/Util makeExecutor (Ljava/lang/String;)Ljava/util/concurrent/ExecutorService;
|
||||
accessible field net/minecraft/server/level/ChunkMap updatingChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
||||
accessible field net/minecraft/server/level/ChunkMap visibleChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
||||
accessible field net/minecraft/server/level/ChunkMap pendingUnloads Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
||||
|
|
@ -43,15 +42,10 @@ accessible class net/minecraft/client/resources/model/ModelBakery$BakedCacheKey
|
|||
accessible method net/minecraft/client/resources/model/ModelBakery$BakedCacheKey <init> (Lnet/minecraft/resources/ResourceLocation;Lcom/mojang/math/Transformation;Z)V
|
||||
accessible class net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl
|
||||
accessible method net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl <init> (Lnet/minecraft/client/resources/model/ModelBakery;Lnet/minecraft/client/resources/model/ModelBakery$TextureGetter;Lnet/minecraft/client/resources/model/ModelResourceLocation;)V
|
||||
accessible method net/minecraft/client/resources/model/ModelBakery getModel (Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/UnbakedModel;
|
||||
accessible class net/minecraft/world/level/chunk/PalettedContainer$Data
|
||||
accessible field net/minecraft/server/MinecraftServer resources Lnet/minecraft/server/MinecraftServer$ReloadableResources;
|
||||
accessible class net/minecraft/server/MinecraftServer$ReloadableResources
|
||||
accessible method net/minecraft/client/gui/screens/Screen addRenderableWidget (Lnet/minecraft/client/gui/components/events/GuiEventListener;)Lnet/minecraft/client/gui/components/events/GuiEventListener;
|
||||
accessible field net/minecraft/client/KeyMapping ALL Ljava/util/Map;
|
||||
accessible field net/minecraft/client/renderer/block/model/multipart/MultiPart definition Lnet/minecraft/world/level/block/state/StateDefinition;
|
||||
accessible field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
|
||||
mutable field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
|
||||
accessible field net/minecraft/client/renderer/entity/EnderDragonRenderer$DragonModel entity Lnet/minecraft/world/entity/boss/enderdragon/EnderDragon;
|
||||
accessible method net/minecraft/world/level/block/state/StateDefinition appendPropertyCodec (Lcom/mojang/serialization/MapCodec;Ljava/util/function/Supplier;Ljava/lang/String;Lnet/minecraft/world/level/block/state/properties/Property;)Lcom/mojang/serialization/MapCodec;
|
||||
accessible class net/minecraft/client/multiplayer/SessionSearchTrees$Key
|
||||
Loading…
Reference in New Issue
Block a user