Begin reimplementing dynamic resources
Currently only unbaked models & blockstate definitions are dynamic
This commit is contained in:
parent
23a5f2985e
commit
9b35236b85
|
|
@ -0,0 +1,12 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import net.minecraft.core.IdMapper;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(IdMapper.class)
|
||||
public interface IdMapperAccessor<T> {
|
||||
@Accessor("tToId")
|
||||
Reference2IntMap<T> getReferenceMap();
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
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.dynresources.DynamicModelSystem;
|
||||
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.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mixin(BlockStateModelLoader.class)
|
||||
@ClientOnlyMixin
|
||||
public abstract class MixinBlockStateModelLoader {
|
||||
@Shadow
|
||||
protected static BlockStateModelLoader.LoadedModels lambda$loadBlockStates$1(Map.Entry<Identifier, List<Resource>> entry, Function<Identifier, StateDefinition<Block, BlockState>> locationToBlockStateMapper) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ModifyArg(method = "loadBlockStates", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenCompose(Ljava/util/function/Function;)Ljava/util/concurrent/CompletableFuture;"))
|
||||
private static Function<Map<Identifier, List<Resource>>, ? extends CompletionStage<BlockStateModelLoader.LoadedModels>> skipAOTBlockStateLoad(Function<Map<Identifier, List<Resource>>, ? extends CompletionStage<Map<Identifier, BlockStateModelLoader.LoadedModels>>> original, @Local(ordinal = 0) Function<Identifier, StateDefinition<Block, BlockState>> mapper) {
|
||||
return resourceMap -> CompletableFuture.completedFuture(DynamicModelSystem.createDynamicBlockStateLoadedModels(resourceMap, (id, resources) -> {
|
||||
return lambda$loadBlockStates$1(Map.entry(id, resources), mapper);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.dynresources.DynamicModelSystem;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mixin(ModelManager.class)
|
||||
@ClientOnlyMixin
|
||||
public class MixinModelManager {
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason Instead of loading all unbaked models from the resource packs at once, create a dynamic map backed by
|
||||
* a cache that loads them on demand
|
||||
*/
|
||||
@ModifyArg(method = "loadBlockModels", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenCompose(Ljava/util/function/Function;)Ljava/util/concurrent/CompletableFuture;"))
|
||||
private static Function<Map<Identifier, Resource>, ? extends CompletionStage<Map<Identifier, UnbakedModel>>> skipAOTUnbakedModelLoad(Function<Map<Identifier, Resource>, ? extends CompletionStage<Map<Identifier, UnbakedModel>>> original) {
|
||||
return resourceMap -> CompletableFuture.completedFuture(DynamicModelSystem.createDynamicUnbakedModelMap(resourceMap));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package org.embeddedt.modernfix.dynresources;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.Maps;
|
||||
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.FileToIdConverter;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.neoforged.neoforge.client.model.UnbakedModelParser;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.common.mixin.perf.dynamic_resources.IdMapperAccessor;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DynamicModelSystem {
|
||||
private static final FileToIdConverter MODEL_LISTER = FileToIdConverter.json("models");
|
||||
private static final FileToIdConverter BLOCKSTATE_LISTER = FileToIdConverter.json("blockstates");
|
||||
|
||||
public static final boolean DEBUG_DYNAMIC_MODEL_LOADING = true; // Boolean.getBoolean("modernfix.debugDynamicModelLoading");
|
||||
|
||||
public static Map<Identifier, UnbakedModel> createDynamicUnbakedModelMap(Map<Identifier, Resource> resourceMap) {
|
||||
LoadingCache<Identifier, UnbakedModel> unbakedModelCache = CacheBuilder.newBuilder().softValues().maximumSize(1000).build(new CacheLoader<>() {
|
||||
@Override
|
||||
public UnbakedModel load(Identifier key) throws Exception {
|
||||
var resource = resourceMap.get(MODEL_LISTER.idToFile(key));
|
||||
if (resource == null) {
|
||||
throw new IllegalArgumentException("Model " + key + " does not exist in map");
|
||||
}
|
||||
if (DEBUG_DYNAMIC_MODEL_LOADING) {
|
||||
ModernFix.LOGGER.info("Loading unbaked model {}", key);
|
||||
}
|
||||
try (Reader reader = resource.openAsReader()) {
|
||||
return UnbakedModelParser.parse(reader);
|
||||
}
|
||||
}
|
||||
});
|
||||
Set<Identifier> unbakedIdSet = resourceMap.keySet().stream().map(MODEL_LISTER::fileToId).collect(Collectors.toUnmodifiableSet());
|
||||
return Maps.asMap(unbakedIdSet, key -> key != null ? unbakedModelCache.getUnchecked(key) : null);
|
||||
}
|
||||
|
||||
public interface SingleBlockStateEntryLoader {
|
||||
BlockStateModelLoader.LoadedModels loadEntry(Identifier identifier, List<Resource> blockstateResources);
|
||||
}
|
||||
|
||||
public static BlockStateModelLoader.LoadedModels createDynamicBlockStateLoadedModels(Map<Identifier, List<Resource>> resourceMap, SingleBlockStateEntryLoader entryLoader) {
|
||||
LoadingCache<Identifier, BlockStateModelLoader.LoadedModels> definitionCache = CacheBuilder.newBuilder().softValues().maximumSize(1000).build(new CacheLoader<>() {
|
||||
@Override
|
||||
public BlockStateModelLoader.LoadedModels load(Identifier key) throws Exception {
|
||||
if (DEBUG_DYNAMIC_MODEL_LOADING) {
|
||||
ModernFix.LOGGER.info("Loading blockstate definition for {}", key);
|
||||
}
|
||||
var file = BLOCKSTATE_LISTER.idToFile(key);
|
||||
var resources = resourceMap.getOrDefault(file, List.of());
|
||||
return entryLoader.loadEntry(file, resources);
|
||||
}
|
||||
});
|
||||
Set<BlockState> allStates = ((IdMapperAccessor<BlockState>)Block.BLOCK_STATE_REGISTRY).getReferenceMap().keySet();
|
||||
return new BlockStateModelLoader.LoadedModels(Maps.asMap(allStates, state -> {
|
||||
var identifier = state.getBlock().builtInRegistryHolder().getKey().identifier();
|
||||
var loadedModels = definitionCache.getUnchecked(identifier);
|
||||
return loadedModels.models().get(state);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -53,3 +53,5 @@ public net.minecraft.server.level.ChunkMap pendingUnloads
|
|||
public net.minecraft.world.level.levelgen.DensityFunctions$MulOrAdd$Type
|
||||
public net.minecraft.client.renderer.entity.EnderDragonRenderer$DragonModel entity
|
||||
public net.minecraft.client.KeyMapping ALL
|
||||
|
||||
public net.minecraft.client.resources.model.BlockStateModelLoader$LoadedBlockModelDefinition
|
||||
Loading…
Reference in New Issue
Block a user