diff --git a/build.gradle b/build.gradle index a21797df..a5137485 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id "dev.architectury.loom" version "1.0.312" + id "dev.architectury.loom" version "1.1-SNAPSHOT" id "maven-publish" id 'com.matthewprenger.cursegradle' version '1.4.0' id 'com.palantir.git-version' version '1.0.0' @@ -89,10 +89,12 @@ dependencies { // compile against the JEI API but do not include it at runtime modCompileOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}") - modRuntimeOnly("curse.maven:jei-238222:4405346") modCompileOnly("curse.maven:jeresources-240630:3951643") modCompileOnly "me.shedaniel:RoughlyEnoughItems-forge:${rei_version}" + modCompileOnly("dev.latvian.mods:kubejs-forge:${kubejs_version}") + modRuntimeOnly("curse.maven:ferritecore-429235:4117906") + modCompileOnly("team.chisel.ctm:CTM:${ctm_version}") } tasks.withType(JavaCompile) { @@ -135,10 +137,7 @@ jar { publishing { publications { mavenJava(MavenPublication) { - // add all the jars that should be included when publishing to maven - artifact(remapJar) { - builtBy remapJar - } + from components.java } } diff --git a/gradle.properties b/gradle.properties index 7043652c..fd60c215 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,3 +14,5 @@ parchment_version=2022.11.27 refined_storage_version=4392788 jei_version=11.6.0.1011 rei_version=9.1.591 +ctm_version=1.19.2-1.1.7+11 +kubejs_version=1902.6.0-build.142 diff --git a/src/main/java/org/embeddedt/modernfix/ModernFixClient.java b/src/main/java/org/embeddedt/modernfix/ModernFixClient.java index cb4cd32f..069ee8cc 100644 --- a/src/main/java/org/embeddedt/modernfix/ModernFixClient.java +++ b/src/main/java/org/embeddedt/modernfix/ModernFixClient.java @@ -3,6 +3,7 @@ package org.embeddedt.modernfix; import com.mojang.datafixers.util.Pair; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.DebugScreenOverlay; import net.minecraft.client.gui.screens.ConnectScreen; import net.minecraft.client.gui.screens.TitleScreen; import net.minecraftforge.client.event.CustomizeGuiOverlayEvent; @@ -10,12 +11,15 @@ import net.minecraftforge.client.event.ScreenEvent; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.world.entity.Entity; +import net.minecraftforge.client.gui.overlay.ForgeGui; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.level.LevelEvent; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.util.ObfuscationReflectionHelper; import net.minecraftforge.network.NetworkEvent; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.load.LoadEvents; @@ -37,6 +41,9 @@ public class ModernFixClient { private String brandingString = null; public ModernFixClient() { + // clear reserve as it's not needed + // TODO: port 1.18+ + // ObfuscationReflectionHelper.setPrivateValue(Minecraft.class, null, new byte[0], "field_71444_a"); if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.faster_singleplayer_load.ClientEvents")) { MinecraftForge.EVENT_BUS.register(new LoadEvents()); } @@ -82,6 +89,16 @@ public class ModernFixClient { } } + @SubscribeEvent + public void onDisconnect(LevelEvent.Unload event) { + if(event.getLevel().isClientSide()) { + DebugScreenOverlay overlay = ObfuscationReflectionHelper.getPrivateValue(ForgeGui.class, (ForgeGui)Minecraft.getInstance().gui, "debugOverlay"); + if(overlay != null) { + Minecraft.getInstance().tell(overlay::clearChunkCache); + } + } + } + /** * Check if the IDs match and remap them if not. * @return true if ID remap was needed diff --git a/src/main/java/org/embeddedt/modernfix/blockstate/FakeStateMap.java b/src/main/java/org/embeddedt/modernfix/blockstate/FakeStateMap.java new file mode 100644 index 00000000..42194e5d --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/blockstate/FakeStateMap.java @@ -0,0 +1,181 @@ +package org.embeddedt.modernfix.blockstate; + +import com.google.common.collect.ImmutableSet; +import net.minecraft.world.level.block.state.properties.Property; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * Fake "map" implementation used to hold the states. + * + * Intentionally throws on methods that would be inefficient so that we know + * if an incompatible mod is present. + */ +public class FakeStateMap implements Map, Comparable>, S> { + private final Map, Comparable>[] keys; + private final Object[] values; + private int usedSlots; + public FakeStateMap(int numStates) { + this.keys = new Map[numStates]; + this.values = new Object[numStates]; + this.usedSlots = 0; + } + + @Override + public int size() { + return usedSlots; + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean containsKey(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsValue(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public S get(Object o) { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public S put(Map, Comparable> propertyComparableMap, S s) { + keys[usedSlots] = propertyComparableMap; + values[usedSlots] = s; + usedSlots++; + return null; + } + + @Override + public S remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(@NotNull Map, Comparable>, ? extends S> map) { + for(Entry, Comparable>, ? extends S> entry : map.entrySet()) { + this.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void clear() { + for(int i = 0; i < this.keys.length; i++) { + this.keys[i] = null; + this.values[i] = null; + } + this.usedSlots = 0; + } + + @NotNull + @Override + public Set, Comparable>> keySet() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Collection values() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Set, Comparable>, S>> entrySet() { + return new Set, Comparable>, S>>() { + @Override + public int size() { + return usedSlots; + } + + @Override + public boolean isEmpty() { + return FakeStateMap.this.isEmpty(); + } + + @Override + public boolean contains(Object o) { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Iterator, Comparable>, S>> iterator() { + return new Iterator, Comparable>, S>>() { + int currentIdx = 0; + @Override + public boolean hasNext() { + return currentIdx < usedSlots; + } + + @Override + public Entry, Comparable>, S> next() { + if(currentIdx >= usedSlots) + throw new IndexOutOfBoundsException(); + Entry, Comparable>, S> entry = new AbstractMap.SimpleImmutableEntry<>(keys[currentIdx], (S)values[currentIdx]); + currentIdx++; + return entry; + } + }; + } + + @NotNull + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public T[] toArray(@NotNull T[] ts) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean add(Entry, Comparable>, S> mapSEntry) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(@NotNull Collection collection) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(@NotNull Collection, Comparable>, S>> collection) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(@NotNull Collection collection) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(@NotNull Collection collection) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + }; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index 06d25ef3..03ff7003 100644 --- a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java +++ b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java @@ -1,15 +1,24 @@ package org.embeddedt.modernfix.core; +import cpw.mods.modlauncher.api.INameMappingService; +import net.minecraftforge.fml.util.ObfuscationReflectionHelper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig; import org.embeddedt.modernfix.core.config.Option; +import org.embeddedt.modernfix.util.DummyList; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.*; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; +import org.spongepowered.asm.mixin.injection.struct.InjectionInfo; +import org.spongepowered.asm.mixin.injection.struct.InjectorGroupInfo; import java.io.File; +import java.lang.reflect.Field; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class ModernFixMixinPlugin implements IMixinConfigPlugin { private static final String MIXIN_PACKAGE_ROOT = "org.embeddedt.modernfix.mixin."; @@ -28,6 +37,18 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { this.logger.info("Loaded configuration file for ModernFix: {} options available, {} override(s) found", config.getOptionCount(), config.getOptionOverrideCount()); + + /* https://github.com/FabricMC/Mixin/pull/99 */ + try { + Field groupMembersField = InjectorGroupInfo.class.getDeclaredField("members"); + groupMembersField.setAccessible(true); + Field noGroupField = InjectorGroupInfo.Map.class.getDeclaredField("NO_GROUP"); + noGroupField.setAccessible(true); + InjectorGroupInfo noGroup = (InjectorGroupInfo)noGroupField.get(null); + groupMembersField.set(noGroup, new DummyList<>()); + } catch(RuntimeException | ReflectiveOperationException e) { + logger.error("Failed to patch mixin memory leak", e); + } } @Override @@ -99,6 +120,40 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { @Override public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - + if(mixinClassName.equals("org.embeddedt.modernfix.mixin.perf.compress_blockstate.BlockStateBaseMixin")) { + // Delete unused fields off BlockStateBase + Set fieldsToDelete = Stream.of( + "field_235702_f_", // isAir + "field_235703_g_", // material + "field_235705_i_", // destroySpeed + "field_235706_j_", // requiresCorrectToolForDrops + "field_235707_k_", // canOcclude + "field_235708_l_", // isRedstoneConductor + "field_235709_m_", // isSuffocating + "field_235710_n_", // isViewBlocking + "field_235711_o_", // hasPostProcess + "field_235712_p_" // emissiveRendering + ).map(name -> ObfuscationReflectionHelper.remapName(INameMappingService.Domain.FIELD, name)).collect(Collectors.toSet()); + targetClass.fields.removeIf(field -> { + if(fieldsToDelete.contains(field.name)) { + logger.info("Removing " + field.name); + return true; + } + return false; + }); + for(MethodNode m : targetClass.methods) { + if(m.name.equals("")) { + ListIterator iter = m.instructions.iterator(); + while(iter.hasNext()) { + AbstractInsnNode node = iter.next(); + if(node.getOpcode() == Opcodes.PUTFIELD) { + if(fieldsToDelete.contains(((FieldInsnNode)node).name)) { + iter.remove(); + } + } + } + } + } + } } } \ No newline at end of file diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index 5441ef4b..79624983 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -1,6 +1,7 @@ package org.embeddedt.modernfix.core.config; import net.minecraftforge.fml.loading.FMLLoader; +import net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator; import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -14,6 +15,10 @@ public class ModernFixEarlyConfig { private final Map options = new HashMap<>(); + private static boolean modPresent(String modId) { + return FMLLoader.getLoadingModList().getModFileById(modId) != null; + } + private ModernFixEarlyConfig() { // Defines the default rules which can be configured by the user or other mods. // You must manually add a rule for any new mixins not covered by an existing package rule. @@ -25,24 +30,23 @@ public class ModernFixEarlyConfig { this.addMixinRule("feature.direct_stack_trace", false); this.addMixinRule("perf.fast_registry_validation", true); this.addMixinRule("perf.skip_first_datapack_reload", true); - this.addMixinRule("perf.use_integrated_resources", true); - this.addMixinRule("perf.jeresources_startup", true); this.addMixinRule("perf.remove_biome_temperature_cache", true); this.addMixinRule("perf.reduce_blockstate_cache_rebuilds", true); - this.addMixinRule("perf.parallelize_model_loading", true); - this.addMixinRule("perf.parallelize_model_loading.multipart", false); + this.addMixinRule("perf.model_optimizations", true); + this.addMixinRule("perf.dynamic_resources", false); + /* Use a simpler ArrayMap if FerriteCore is using the map intelligently anyway */ + this.addMixinRule("perf.state_definition_construct", modPresent("ferritecore")); this.addMixinRule("perf.cache_strongholds", true); this.addMixinRule("perf.cache_upgraded_structures", true); + this.addMixinRule("perf.compress_blockstate", false); this.addMixinRule("bugfix.concurrency", true); this.addMixinRule("bugfix.edge_chunk_not_saved", true); - this.addMixinRule("perf.async_jei", true); this.addMixinRule("perf.thread_priorities", true); this.addMixinRule("perf.sync_executor_sleep", true); this.addMixinRule("perf.scan_cache", true); this.addMixinRule("perf.flatten_model_predicates", true); this.addMixinRule("perf.deduplicate_location", false); this.addMixinRule("perf.cache_blockstate_cache_arrays", true); - this.addMixinRule("perf.faster_baking", true); this.addMixinRule("perf.cache_model_materials", true); this.addMixinRule("perf.datapack_reload_exceptions", true); this.addMixinRule("perf.faster_texture_stitching", true); @@ -53,6 +57,8 @@ public class ModernFixEarlyConfig { this.addMixinRule("safety", true); this.addMixinRule("launch.transformer_cache", false); this.addMixinRule("launch.class_search_cache", true); + boolean isDevEnv = !FMLLoader.isProduction() && FMLLoader.getLoadingModList().getModFileById("modernfix").getFile().getLocator() instanceof ExplodedDirectoryLocator; + this.addMixinRule("devenv", isDevEnv); /* Mod compat */ disableIfModPresent("mixin.perf.thread_priorities", "smoothboot"); @@ -60,6 +66,7 @@ public class ModernFixEarlyConfig { disableIfModPresent("mixin.perf.compress_biome_container", "chocolate", "betterendforge"); disableIfModPresent("mixin.bugfix.mc218112", "performant"); disableIfModPresent("mixin.perf.faster_baking", "touhou_little_maid"); + disableIfModPresent("mixin.perf.reuse_datapacks", "tac"); } private void disableIfModPresent(String configName, String... ids) { diff --git a/src/main/java/org/embeddedt/modernfix/duck/rs/IFluidExternalStorageCache.java b/src/main/java/org/embeddedt/modernfix/duck/rs/IFluidExternalStorageCache.java new file mode 100644 index 00000000..87031bb2 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/duck/rs/IFluidExternalStorageCache.java @@ -0,0 +1,7 @@ +package org.embeddedt.modernfix.duck.rs; + +import net.minecraftforge.fluids.capability.IFluidHandler; + +public interface IFluidExternalStorageCache { + boolean initCache(IFluidHandler handler); +} diff --git a/src/main/java/org/embeddedt/modernfix/duck/rs/IItemExternalStorageCache.java b/src/main/java/org/embeddedt/modernfix/duck/rs/IItemExternalStorageCache.java new file mode 100644 index 00000000..133646a2 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/duck/rs/IItemExternalStorageCache.java @@ -0,0 +1,7 @@ +package org.embeddedt.modernfix.duck.rs; + +import net.minecraftforge.items.IItemHandler; + +public interface IItemExternalStorageCache { + boolean initCache(IItemHandler handler); +} diff --git a/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java new file mode 100644 index 00000000..30dea59d --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java @@ -0,0 +1,119 @@ +package org.embeddedt.modernfix.dynamicresources; + +import com.mojang.math.Transformation; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.resources.ResourceLocation; +import org.apache.commons.lang3.tuple.Triple; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.AbstractMap; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + +public class DynamicBakedModelProvider implements Map { + private final ModelBakery bakery; + private final Map, BakedModel> bakedCache; + private final Map permanentOverrides; + + public DynamicBakedModelProvider(ModelBakery bakery, Map, BakedModel> cache) { + this.bakery = bakery; + this.bakedCache = cache; + this.permanentOverrides = new Object2ObjectOpenHashMap<>(); + } + private static Triple vanillaKey(Object o) { + return Triple.of((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.containsKey(o) || bakedCache.containsKey(vanillaKey(o)); + } + + @Override + public boolean containsValue(Object o) { + return permanentOverrides.containsValue(o) || bakedCache.containsValue(o); + } + + @Override + public BakedModel get(Object o) { + BakedModel model = permanentOverrides.get(o); + return model != null ? model : bakery.bake((ResourceLocation)o, BlockModelRotation.X0_Y0); + } + + @Nullable + @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(Triple::getLeft).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().getLeft(), entry.getValue())).collect(Collectors.toSet()); + } + + @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.getMiddle() != rotation || loc.getRight() != uvLock || overridenLocations.contains(loc.getLeft())) + return oldModel; + else + return function.apply(loc.getLeft(), oldModel); + }); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicModelBakeEvent.java b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicModelBakeEvent.java new file mode 100644 index 00000000..f2bc894e --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicModelBakeEvent.java @@ -0,0 +1,49 @@ +package org.embeddedt.modernfix.dynamicresources; + +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.client.model.ForgeModelBakery; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.event.IModBusEvent; + +/** + * Fired when a model is baked dynamically. Intended to be used as a replacement for ModelBakeEvent + * if mods want to replace a model. + *

+ * Note that this event can fire many times for the same resource location, as models are unloaded + * if unused/under memory pressure. + */ +public class DynamicModelBakeEvent extends Event implements IModBusEvent { + private final ResourceLocation location; + private BakedModel model; + private final UnbakedModel unbakedModel; + private final ForgeModelBakery modelLoader; + public DynamicModelBakeEvent(ResourceLocation location, UnbakedModel unbakedModel, BakedModel model, ForgeModelBakery loader) { + this.location = location; + this.model = model; + this.unbakedModel = unbakedModel; + this.modelLoader = loader; + } + + public ResourceLocation getLocation() { + return this.location; + } + + public BakedModel getModel() { + return this.model; + } + + public UnbakedModel getUnbakedModel() { + return this.unbakedModel; + } + + public ForgeModelBakery getModelLoader() { + return this.modelLoader; + } + + public void setModel(BakedModel model) { + this.model = model; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicModelProvider.java b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicModelProvider.java new file mode 100644 index 00000000..67bd354c --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicModelProvider.java @@ -0,0 +1,41 @@ +package org.embeddedt.modernfix.dynamicresources; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * Handles loading models dynamically, rather than at startup time. + */ +public class DynamicModelProvider { + private final Map internalModels; + private final Cache> loadedModels = + CacheBuilder.newBuilder() + .expireAfterAccess(3, TimeUnit.MINUTES) + .maximumSize(1000) + .concurrencyLevel(8) + .softValues() + .build(); + + public DynamicModelProvider(Map initialModels) { + this.internalModels = initialModels; + } + + public UnbakedModel getModel(ResourceLocation location) { + try { + return loadedModels.get(location, () -> Optional.ofNullable(loadModel(location))).orElse(null); + } catch(ExecutionException e) { + throw new RuntimeException(e.getCause()); + } + } + + private UnbakedModel loadModel(ResourceLocation location) { + return null; /* TODO :) */ + } +} diff --git a/src/main/java/org/embeddedt/modernfix/dynamicresources/ModelLocationCache.java b/src/main/java/org/embeddedt/modernfix/dynamicresources/ModelLocationCache.java new file mode 100644 index 00000000..25886e20 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/dynamicresources/ModelLocationCache.java @@ -0,0 +1,39 @@ +package org.embeddedt.modernfix.dynamicresources; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.Util; +import net.minecraft.client.renderer.block.BlockModelShaper; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.core.Registry; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class ModelLocationCache { + private static final LoadingCache locationCache = CacheBuilder.newBuilder() + .maximumSize(10000) + .build(new CacheLoader() { + @Override + public ModelResourceLocation load(BlockState key) throws Exception { + return BlockModelShaper.stateToModelLocation(key); + } + }); + + public static ModelResourceLocation get(BlockState state) { + try { + return locationCache.get(state); + } catch(ExecutionException e) { + throw new RuntimeException(e.getCause()); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/dynamicresources/ResourcePackHandler.java b/src/main/java/org/embeddedt/modernfix/dynamicresources/ResourcePackHandler.java new file mode 100644 index 00000000..35df07b7 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/dynamicresources/ResourcePackHandler.java @@ -0,0 +1,55 @@ +package org.embeddedt.modernfix.dynamicresources; + +import dev.latvian.mods.kubejs.script.data.KubeJSResourcePack; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackResources; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraftforge.fml.ModList; +import org.embeddedt.modernfix.util.FileUtil; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ResourcePackHandler { + private static final List packHandlers = new ArrayList<>(); + public static Collection getExtraResources(ResourceManager manager, String path, Predicate matchPredicate) { + final String normalizedPath = FileUtil.normalize(path); + return manager.listPacks().flatMap(pack -> packHandlers.stream().flatMap(handler -> { + if(handler.shouldHandle(pack)) { + return handler.getExtraResources(pack, normalizedPath, matchPredicate); + } else + return Stream.of(); + })).collect(Collectors.toList()); + } + + interface PackHandler { + Stream getExtraResources(PackResources pack, String path, Predicate matchPredicate); + boolean shouldHandle(PackResources pack); + } + + static class KubeJSPackHandler implements PackHandler { + + @Override + public Stream getExtraResources(PackResources pack, String path, Predicate matchPredicate) { + KubeJSResourcePack p = (KubeJSResourcePack)pack; + return p.getCachedResources().keySet().stream() + .filter(l -> l.getPath().startsWith(path)) + .filter(l -> matchPredicate.test(l.getPath())); + } + + @Override + public boolean shouldHandle(PackResources pack) { + return pack instanceof KubeJSResourcePack; + } + } + + static { + if(ModList.get().isLoaded("kubejs")) { + packHandlers.add(new KubeJSPackHandler()); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/devenv/MinecraftMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/devenv/MinecraftMixin.java new file mode 100644 index 00000000..ce505efc --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/devenv/MinecraftMixin.java @@ -0,0 +1,16 @@ +package org.embeddedt.modernfix.mixin.devenv; + +import com.mojang.authlib.minecraft.UserApiService; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import net.minecraft.client.Minecraft; +import net.minecraft.client.main.GameConfig; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(Minecraft.class) +public class MinecraftMixin { + @Overwrite + private UserApiService createUserApiService(YggdrasilAuthenticationService yggdrasilAuthenticationService, GameConfig arg) { + return UserApiService.OFFLINE; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/devenv/MinecraftServerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/devenv/MinecraftServerMixin.java new file mode 100644 index 00000000..176ac2c6 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/devenv/MinecraftServerMixin.java @@ -0,0 +1,21 @@ +package org.embeddedt.modernfix.mixin.devenv; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerChunkCache; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +/* Disable waiting for spawn chunk load */ +@Mixin(MinecraftServer.class) +public class MinecraftServerMixin { + @Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;getTickingGenerated()I")) + private int noSpawnChunkWait(ServerChunkCache cache) { + return 441; + } + + @Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;waitUntilNextTick()V")) + private void noWaitTick(MinecraftServer server) { + + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java new file mode 100644 index 00000000..1397a848 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java @@ -0,0 +1,16 @@ +package org.embeddedt.modernfix.mixin.devenv; + +import com.mojang.text2speech.Narrator; +import com.mojang.text2speech.NarratorDummy; +import net.minecraft.client.gui.chat.NarratorChatListener; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(NarratorChatListener.class) +public class NarratorMixin { + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/mojang/text2speech/Narrator;getNarrator()Lcom/mojang/text2speech/Narrator;", remap = false)) + private Narrator useDummyNarrator() { + return new NarratorDummy(); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/InputConstantsMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/InputConstantsMixin.java new file mode 100644 index 00000000..d6a60cdb --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/InputConstantsMixin.java @@ -0,0 +1,19 @@ +package org.embeddedt.modernfix.mixin.perf.async_jei; + +import com.mojang.blaze3d.platform.InputConstants; +import com.mojang.blaze3d.systems.RenderSystem; +import org.lwjgl.glfw.GLFW; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(InputConstants.class) +public class InputConstantsMixin { + @Redirect(method = "isKeyDown", at = @At(value = "INVOKE", target = "Lorg/lwjgl/glfw/GLFW;glfwGetKey(JI)I", remap = false)) + private static int offThreadKeyFetch(long win, int k) { + if(RenderSystem.isOnRenderThreadOrInit()) + return GLFW.glfwGetKey(win, k); + else + return 0; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/compress_blockstate/BlockBehaviourMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/compress_blockstate/BlockBehaviourMixin.java new file mode 100644 index 00000000..2ee283ed --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/compress_blockstate/BlockBehaviourMixin.java @@ -0,0 +1,14 @@ +package org.embeddedt.modernfix.mixin.perf.compress_blockstate; + +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(BlockBehaviour.class) +public class BlockBehaviourMixin { + @Overwrite(remap = false) + protected boolean isAir(BlockState state) { + return state.getBlock().properties.isAir; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/compress_blockstate/BlockStateBaseMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/compress_blockstate/BlockStateBaseMixin.java new file mode 100644 index 00000000..c3eeabd6 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/compress_blockstate/BlockStateBaseMixin.java @@ -0,0 +1,102 @@ +package org.embeddedt.modernfix.mixin.perf.compress_blockstate; + +import com.google.common.collect.ImmutableMap; +import com.mojang.serialization.MapCodec; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateHolder; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.level.material.Material; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(BlockBehaviour.BlockStateBase.class) +public abstract class BlockStateBaseMixin extends StateHolder { + protected BlockStateBaseMixin(Block object, ImmutableMap, Comparable> immutableMap, MapCodec mapCodec) { + super(object, immutableMap, mapCodec); + } + + @Redirect(method = "*", at = @At( + value = "FIELD", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;material:Lnet/minecraft/world/level/material/Material;" + )) + private Material getMaterial(BlockBehaviour.BlockStateBase base) { + return this.owner.properties.material; + } + + @Redirect(method = "*", at = @At( + value = "FIELD", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;destroySpeed:F" + )) + private float getDestroyTime(BlockBehaviour.BlockStateBase base) { + return this.owner.properties.destroyTime; + } + + @Redirect(method = "*", at = @At( + value = "FIELD", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;requiresCorrectToolForDrops:Z" + )) + private boolean getRequiresTool(BlockBehaviour.BlockStateBase base) { + return this.owner.properties.requiresCorrectToolForDrops; + } + + @Redirect(method = "*", at = @At( + value = "FIELD", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;canOcclude:Z" + )) + private boolean getCanOcclude(BlockBehaviour.BlockStateBase base) { + return this.owner.properties.canOcclude; + } + + @Redirect(method = "*", at = @At( + value = "FIELD", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;isRedstoneConductor:Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;" + )) + private BlockBehaviour.StatePredicate getRedstoneConductor(BlockBehaviour.BlockStateBase base) { + return this.owner.properties.isRedstoneConductor; + } + + @Redirect(method = "*", at = @At( + value = "FIELD", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;isSuffocating:Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;" + )) + private BlockBehaviour.StatePredicate getSuffocating(BlockBehaviour.BlockStateBase base) { + return this.owner.properties.isSuffocating; + } + + @Redirect(method = "*", at = @At( + value = "FIELD", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;isViewBlocking:Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;" + )) + private BlockBehaviour.StatePredicate getViewBlocking(BlockBehaviour.BlockStateBase base) { + return this.owner.properties.isViewBlocking; + } + + @Redirect(method = "*", at = @At( + value = "FIELD", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;hasPostProcess:Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;" + )) + private BlockBehaviour.StatePredicate getPostProcess(BlockBehaviour.BlockStateBase base) { + return this.owner.properties.hasPostProcess; + } + + @Redirect(method = "*", at = @At( + value = "FIELD", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;emissiveRendering:Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;" + )) + private BlockBehaviour.StatePredicate getEmissiveRendering(BlockBehaviour.BlockStateBase base) { + return this.owner.properties.emissiveRendering; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/BlockModelShaperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/BlockModelShaperMixin.java new file mode 100644 index 00000000..91d45970 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/BlockModelShaperMixin.java @@ -0,0 +1,49 @@ +package org.embeddedt.modernfix.mixin.perf.dynamic_resources; + +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.Util; +import net.minecraft.client.renderer.block.BlockModelShaper; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.core.Registry; +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.properties.Property; +import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +@Mixin(BlockModelShaper.class) +public class BlockModelShaperMixin { + @Shadow @Final private ModelManager modelManager; + + /** + * @author embeddedt + * @reason no need to rebuild model cache, and location cache is done elsewhere + */ + @Overwrite + public void rebuildCache() { + } + + @Overwrite + public BakedModel getBlockModel(BlockState state) { + BakedModel model = modelManager.getModel(ModelLocationCache.get(state)); + if (model == null) { + model = modelManager.getMissingModel(); + } + return model; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ItemModelShaperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ItemModelShaperMixin.java new file mode 100644 index 00000000..0ca70841 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ItemModelShaperMixin.java @@ -0,0 +1,53 @@ +package org.embeddedt.modernfix.mixin.perf.dynamic_resources; + +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.world.item.Item; +import net.minecraftforge.client.ItemModelMesherForge; +import net.minecraftforge.registries.IRegistryDelegate; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Map; + +@Mixin(ItemModelMesherForge.class) +public abstract class ItemModelShaperMixin extends ItemModelShaper { + @Shadow @Final private Map, ModelResourceLocation> locations; + + public ItemModelShaperMixin(ModelManager arg) { + super(arg); + } + + /** + * @reason Get the stored location for that item and meta, and get the model + * from that location from the model manager. + **/ + @Overwrite + @Override + public BakedModel getItemModel(Item item) { + ModelResourceLocation map = locations.get(item.delegate); + return map == null ? null : getModelManager().getModel(map); + } + + /** + * @reason Don't get all models during init (with dynamic loading, that would + * generate them all). Just store location instead. + **/ + @Overwrite + @Override + public void register(Item item, ModelResourceLocation location) { + locations.put(item.delegate, location); + } + + /** + * @reason Disable cache rebuilding (with dynamic loading, that would generate + * all models). + **/ + @Overwrite + @Override + public void rebuildCache() {} +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakeryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakeryMixin.java new file mode 100644 index 00000000..1f5272ae --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakeryMixin.java @@ -0,0 +1,311 @@ +package org.embeddedt.modernfix.mixin.perf.dynamic_resources; + +import com.google.common.base.Stopwatch; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalNotification; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import com.mojang.datafixers.util.Pair; +import com.mojang.math.Transformation; +import net.minecraft.Util; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.ItemModelGenerator; +import net.minecraft.client.renderer.texture.AtlasSet; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.client.resources.model.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.Resource; +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 net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.model.ForgeModelBakery; +import net.minecraftforge.client.model.ModelLoaderRegistry; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.ModLoader; +import org.apache.commons.lang3.tuple.Triple; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider; +import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent; +import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; +import org.embeddedt.modernfix.dynamicresources.ResourcePackHandler; +import org.slf4j.Logger; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Mixin(ModelBakery.class) +public abstract class ModelBakeryMixin { + + private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading"); + + @Shadow @Final @Mutable public Map unbakedCache; + + @Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION; + + @Shadow protected abstract BlockModel loadBlockModel(ResourceLocation location) throws IOException; + + @Shadow @Final protected static Set UNREFERENCED_TEXTURES; + @Shadow private Map> atlasPreparations; + @Shadow @Final protected ResourceManager resourceManager; + @Shadow @Nullable private AtlasSet atlasSet; + @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, BakedModel> bakedCache; + + @Shadow @Final public static BlockModel GENERATION_MARKER; + + @Shadow @Final private static ItemModelGenerator ITEM_MODEL_GENERATOR; + + @Shadow @Final public static BlockModel BLOCK_ENTITY_MARKER; + + @Shadow @Final private static Logger LOGGER; + private Cache, BakedModel> loadedBakedModels; + private Cache loadedModels; + + + @Inject(method = "(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/client/color/block/BlockColors;Z)V", at = @At("RETURN")) + private void replaceTopLevelBakedModels(ResourceManager manager, BlockColors colors, boolean vanillaBakery, CallbackInfo ci) { + this.loadedBakedModels = CacheBuilder.newBuilder() + .expireAfterAccess(3, TimeUnit.MINUTES) + .maximumSize(1000) + .concurrencyLevel(8) + .removalListener(this::onModelRemoved) + .softValues() + .build(); + this.loadedModels = CacheBuilder.newBuilder() + .expireAfterAccess(3, TimeUnit.MINUTES) + .maximumSize(1000) + .concurrencyLevel(8) + .removalListener(this::onModelRemoved) + .softValues() + .build(); + this.bakedCache = loadedBakedModels.asMap(); + this.unbakedCache = loadedModels.asMap(); + this.bakedTopLevelModels = new DynamicBakedModelProvider((ModelBakery)(Object)this, bakedCache); + } + + 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 = ((Triple)k).getLeft(); + baked = true; + } + ModernFix.LOGGER.warn("Evicted {} model {}", baked ? "baked" : "unbaked", rl); + } + + private UnbakedModel missingModel; + + /** + * @author embeddedt + * @reason don't load any models initially, just set up initial data structures + */ + @Overwrite(remap = false) + protected void processLoading(ProfilerFiller arg, int maxMipLevels) { + ModelLoaderRegistry.onModelLoadingStart(); + try { + this.missingModel = this.loadBlockModel(MISSING_MODEL_LOCATION); + } catch (IOException var10) { + ModernFix.LOGGER.error("Error loading missing model, should never happen :(", var10); + throw new RuntimeException(var10); + } + // Gather model materials + Set initialMaterials = new HashSet<>(UNREFERENCED_TEXTURES); + gatherModelMaterials(initialMaterials); + ForgeHooksClient.gatherFluidTextures(initialMaterials); + Map> map = initialMaterials.stream().collect(Collectors.groupingBy(Material::atlasLocation)); + this.atlasPreparations = Maps.newHashMap(); + for(Map.Entry> entry : map.entrySet()) { + TextureAtlas atlas = new TextureAtlas(entry.getKey()); + TextureAtlas.Preparations atlastexture$sheetdata = atlas.prepareToStitch(this.resourceManager, entry.getValue().stream().map(Material::texture), arg, maxMipLevels); + this.atlasPreparations.put(entry.getKey(), Pair.of(atlas, atlastexture$sheetdata)); + } + } + + /** + * Scan the models folder and try to load, parse, and get materials from as many models as possible. + */ + private void gatherModelMaterials(Set materialSet) { + Stopwatch stopwatch = Stopwatch.createStarted(); + List allModels = new ArrayList<>(this.resourceManager.listResources("models", path -> path.endsWith(".json"))); + // for KubeJS, etc. + allModels.addAll(ResourcePackHandler.getExtraResources(this.resourceManager, "models", path -> path.endsWith(".json"))); + List>> modelBytes = new ArrayList<>(); + for(ResourceLocation fileLocation : allModels) { + modelBytes.add(CompletableFuture.supplyAsync(() -> { + try(Resource resource = this.resourceManager.getResource(fileLocation)) { + JsonParser parser = new JsonParser(); + // strip models/ and .json from the name + ResourceLocation model = new ResourceLocation(fileLocation.getNamespace(), fileLocation.getPath().substring(7, fileLocation.getPath().length()-5)); + return Pair.of(model, parser.parse(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))); + } catch(IOException | JsonParseException e) { + ModernFix.LOGGER.error("Error reading model {}: {}", fileLocation, e); + return Pair.of(fileLocation, null); + } + }, Util.backgroundExecutor())); + } + allModels.clear(); + CompletableFuture.allOf(modelBytes.toArray(new CompletableFuture[0])).join(); + Set> errorSet = Sets.newLinkedHashSet(); + Map basicModels = new HashMap<>(); + try { + basicModels.put(MISSING_MODEL_LOCATION, this.loadBlockModel(MISSING_MODEL_LOCATION)); + basicModels.put(new ResourceLocation("builtin/generated"), GENERATION_MARKER); + basicModels.put(new ResourceLocation("builtin/entity"), BLOCK_ENTITY_MARKER); + } catch(IOException e) { + throw new RuntimeException("Exception when populating built-in models", e); + } + for(CompletableFuture> future : modelBytes) { + Pair pair = future.join(); + try { + if(pair.getSecond() != null) { + BlockModel model = ModelLoaderRegistry.ExpandedBlockModelDeserializer.INSTANCE.fromJson(pair.getSecond(), BlockModel.class); + model.name = pair.getFirst().toString(); + basicModels.put(pair.getFirst(), model); + } + } catch(Throwable e) { + ModernFix.LOGGER.warn("Unable to parse {}: {}", pair.getFirst(), e); + } + } + modelBytes.clear(); + Function modelGetter = loc -> basicModels.getOrDefault(loc, (BlockModel)this.missingModel); + for(BlockModel model : basicModels.values()) { + materialSet.addAll(model.getMaterials(modelGetter, errorSet)); + } + //errorSet.stream().filter(pair -> !pair.getSecond().equals(MISSING_MODEL_LOCATION_STRING)).forEach(pair -> LOGGER.warn("Unable to resolve texture reference: {} in {}", pair.getFirst(), pair.getSecond())); + stopwatch.stop(); + ModernFix.LOGGER.info("Resolving model textures took " + stopwatch); + } + + @Inject(method = "uploadTextures", at = @At(value = "FIELD", target = "Lnet/minecraft/client/resources/model/ModelBakery;topLevelModels:Ljava/util/Map;", ordinal = 0), cancellable = true) + private void skipBake(TextureManager resourceManager, ProfilerFiller profiler, CallbackInfoReturnable cir) { + profiler.pop(); + cir.setReturnValue(atlasSet); + } + + /** + * 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); + } + + + /** + * @author embeddedt + * @reason synchronize + */ + @Overwrite + public UnbakedModel getModel(ResourceLocation modelLocation) { + if(modelLocation.equals(MISSING_MODEL_LOCATION)) + return missingModel; + UnbakedModel existing = this.unbakedCache.get(modelLocation); + if (existing != null) { + return 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(); + + try { + if (!this.unbakedCache.containsKey(resourcelocation)) { + if(debugDynamicModelLoading) + LOGGER.info("Loading {}", resourcelocation); + this.loadModel(resourcelocation); + // TODO: in theory the cache can get evicted right here and we lose the model + // very unlikely to occur though + } + } catch (ModelBakery.BlockStateDefinitionException var9) { + LOGGER.warn(var9.getMessage()); + this.unbakedCache.put(resourcelocation, iunbakedmodel); + } catch (Exception var10) { + LOGGER.warn("Unable to load model: '{}' referenced from: {}: {}", resourcelocation, modelLocation, var10); + this.unbakedCache.put(resourcelocation, iunbakedmodel); + } finally { + this.loadingStack.remove(resourcelocation); + } + } + + return this.unbakedCache.getOrDefault(modelLocation, iunbakedmodel); + } + } + } + } + + @Overwrite(remap = false) + public BakedModel bake(ResourceLocation arg, ModelState arg2, Function textureGetter) { + Triple triple = Triple.of(arg, arg2.getRotation(), arg2.isUvLocked()); + BakedModel existing = this.bakedCache.get(triple); + if (existing != null) { + return existing; + } else if (this.atlasSet == null) { + throw new IllegalStateException("bake called too early"); + } else { + synchronized (this) { + if(debugDynamicModelLoading) + LOGGER.info("Baking {}", arg); + UnbakedModel iunbakedmodel = this.getModel(arg); + iunbakedmodel.getMaterials(this::getModel, new HashSet<>()); + BakedModel ibakedmodel = null; + if (iunbakedmodel instanceof BlockModel) { + BlockModel blockmodel = (BlockModel)iunbakedmodel; + if (blockmodel.getRootModel() == GENERATION_MARKER) { + ibakedmodel = ITEM_MODEL_GENERATOR.generateBlockModel(textureGetter, blockmodel).bake((ModelBakery)(Object)this, blockmodel, this.atlasSet::getSprite, arg2, arg, false); + } + } + if(ibakedmodel == null) { + ibakedmodel = iunbakedmodel.bake((ModelBakery) (Object) this, textureGetter, arg2, arg); + } + DynamicModelBakeEvent event = new DynamicModelBakeEvent(arg, iunbakedmodel, ibakedmodel, (ForgeModelBakery)(Object)this); + ModLoader.get().postEvent(event); + this.bakedCache.put(triple, event.getModel()); + return event.getModel(); + } + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ctm/CTMPackReloadListenerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ctm/CTMPackReloadListenerMixin.java new file mode 100644 index 00000000..2fcd6028 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ctm/CTMPackReloadListenerMixin.java @@ -0,0 +1,75 @@ +package org.embeddedt.modernfix.mixin.perf.dynamic_resources.ctm; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.client.renderer.ItemBlockRenderTypes; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.BlockModelShaper; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.client.resources.model.MultiPartBakedModel; +import net.minecraft.client.resources.model.WeightedBakedModel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.IRegistryDelegate; +import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +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; +import team.chisel.ctm.client.model.AbstractCTMBakedModel; +import team.chisel.ctm.client.util.CTMPackReloadListener; + +import java.util.Map; +import java.util.function.Predicate; + +@Mixin(CTMPackReloadListener.class) +public abstract class CTMPackReloadListenerMixin { + @Shadow @Final private static Map, Predicate> blockRenderChecks; + + @Shadow protected abstract Predicate getLayerCheck(BlockState state, BakedModel model); + + @Shadow protected abstract Predicate getExistingRenderCheck(Block block); + + private Map locationToState = new Object2ObjectOpenHashMap<>(); + + @Inject(method = "", at = @At("RETURN")) + private void onInit(CallbackInfo ci) { + FMLJavaModLoadingContext.get().getModEventBus().addListener(EventPriority.LOW, this::onModelBake); + } + + @Overwrite(remap = false) + private void refreshLayerHacks() { + blockRenderChecks.forEach((b, p) -> ItemBlockRenderTypes.setRenderLayer((Block) b.get(), p)); + blockRenderChecks.clear(); + if(locationToState.isEmpty()) { + for(Block block : ForgeRegistries.BLOCKS.getValues()) { + for(BlockState state : block.getStateDefinition().getPossibleStates()) { + locationToState.put(BlockModelShaper.stateToModelLocation(state), state); + } + } + } + } + + private void onModelBake(DynamicModelBakeEvent event) { + if(!(event.getModel() instanceof AbstractCTMBakedModel || event.getModel() instanceof WeightedBakedModel || event.getModel() instanceof MultiPartBakedModel)) + return; + BlockState state = locationToState.get(event.getLocation()); + if(state == null) + return; + Block block = state.getBlock(); + if(blockRenderChecks.containsKey(block.delegate)) + return; + Predicate newPredicate = this.getLayerCheck(state, event.getModel()); + if(newPredicate != null) { + blockRenderChecks.put(block.delegate, this.getExistingRenderCheck(block)); + ItemBlockRenderTypes.setRenderLayer(block, newPredicate); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ctm/TextureMetadataHandlerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ctm/TextureMetadataHandlerMixin.java new file mode 100644 index 00000000..453b8962 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ctm/TextureMetadataHandlerMixin.java @@ -0,0 +1,82 @@ +package org.embeddedt.modernfix.mixin.perf.dynamic_resources.ctm; + +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.Object2BooleanMap; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.Material; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.client.model.ForgeModelBakery; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import team.chisel.ctm.CTM; +import team.chisel.ctm.client.model.AbstractCTMBakedModel; +import team.chisel.ctm.client.texture.IMetadataSectionCTM; +import team.chisel.ctm.client.util.ResourceUtil; +import team.chisel.ctm.client.util.TextureMetadataHandler; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.util.*; + +@Mixin(TextureMetadataHandler.class) +public abstract class TextureMetadataHandlerMixin { + + @Shadow @Nonnull protected abstract BakedModel wrap(ResourceLocation loc, UnbakedModel model, BakedModel object, ForgeModelBakery loader) throws IOException; + + @SubscribeEvent + public void onDynamicModelBake(DynamicModelBakeEvent event) { + UnbakedModel rootModel = event.getUnbakedModel(); + BakedModel baked = event.getModel(); + ResourceLocation rl = event.getLocation(); + if (!(baked instanceof AbstractCTMBakedModel) && !baked.isCustomRenderer()) { + Deque dependencies = new ArrayDeque<>(); + Set seenModels = new HashSet<>(); + dependencies.push(rl); + seenModels.add(rl); + boolean shouldWrap = false; + Set> errors = new HashSet<>(); + // Breadth-first loop through dependencies, exiting as soon as a CTM texture is found, and skipping duplicates/cycles + while (!shouldWrap && !dependencies.isEmpty()) { + ResourceLocation dep = dependencies.pop(); + UnbakedModel model; + try { + model = dep == rl ? rootModel : event.getModelLoader().getModel(dep); + } catch (Exception e) { + continue; + } + + Collection textures = model.getMaterials(event.getModelLoader()::getModel, errors); + Collection newDependencies = model.getDependencies(); + for (Material tex : textures) { + IMetadataSectionCTM meta = null; + // Cache all dependent texture metadata + try { + meta = ResourceUtil.getMetadata(ResourceUtil.spriteToAbsolute(tex.texture())); + } catch (IOException e) {} // Fallthrough + if (meta != null) { + // At least one texture has CTM metadata, so we should wrap this model + shouldWrap = true; + } + } + + for (ResourceLocation newDep : newDependencies) { + if (seenModels.add(newDep)) { + dependencies.push(newDep); + } + } + } + if (shouldWrap) { + try { + event.setModel(wrap(rl, rootModel, baked, event.getModelLoader())); + dependencies.clear(); + } catch (IOException e) { + CTM.logger.error("Could not wrap model " + rl + ". Aborting...", e); + } + } + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/BlockModelShapesMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/BlockModelShapesMixin.java deleted file mode 100644 index c397f9a9..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/BlockModelShapesMixin.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.faster_baking; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.client.renderer.block.BlockModelShaper; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.ModelManager; -import net.minecraft.Util; -import net.minecraft.core.Registry; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; -import org.spongepowered.asm.mixin.Shadow; - -import java.util.ArrayList; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -@Mixin(BlockModelShaper.class) -public abstract class BlockModelShapesMixin { - @Shadow @Final private ModelManager modelManager; - - @Shadow @Final private Map modelByStateCache; - - /** - * @author embeddedt - * @reason parallelize cache rebuild - */ - @Overwrite - public void rebuildCache() { - this.modelByStateCache.clear(); - ArrayList>> futures = new ArrayList<>(); - for(Block block : Registry.BLOCK) { - block.getStateDefinition().getPossibleStates().forEach((state) -> { - futures.add(CompletableFuture.supplyAsync(() -> { - return Pair.of(state, this.modelManager.getModel(BlockModelShaper.stateToModelLocation(state))); - }, Util.backgroundExecutor())); - }); - } - for(CompletableFuture> future : futures) { - Pair pair = future.join(); - this.modelByStateCache.put(pair.getFirst(), pair.getSecond()); - } - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelBakeryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelBakeryMixin.java deleted file mode 100644 index 2b18b277..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelBakeryMixin.java +++ /dev/null @@ -1,162 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.faster_baking; - -import com.google.common.collect.ImmutableSet; -import com.mojang.datafixers.util.Pair; -import net.minecraft.client.Minecraft; -import net.minecraft.client.color.block.BlockColors; -import net.minecraft.client.renderer.block.model.MultiVariant; -import net.minecraft.client.renderer.block.model.multipart.MultiPart; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.renderer.texture.AtlasSet; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.resources.ResourceLocation; -import com.mojang.math.Transformation; -import net.minecraftforge.client.event.ModelEvent; -import net.minecraftforge.fml.loading.progress.StartupMessageManager; -import org.apache.commons.lang3.tuple.Triple; -import org.embeddedt.modernfix.core.config.ModernFixConfig; -import org.embeddedt.modernfix.duck.IExtendedModelBakery; -import org.embeddedt.modernfix.models.LazyBakedModel; -import org.embeddedt.modernfix.util.ModUtil; -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 javax.annotation.Nullable; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static org.embeddedt.modernfix.ModernFix.LOGGER; - -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.client.resources.model.ModelResourceLocation; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.client.resources.model.UnbakedModel; - -@Mixin(ModelBakery.class) -public abstract class ModelBakeryMixin implements IExtendedModelBakery { - @Shadow @Final private Map topLevelModels; - - @Shadow @Final private Map bakedTopLevelModels; - - @Shadow @Deprecated @Nullable public abstract BakedModel bake(ResourceLocation pLocation, ModelState pTransform); - - @Shadow private Map> atlasPreparations; - - @Shadow @Nullable private AtlasSet atlasSet; - - @Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION; - - @Shadow @Final private Map, BakedModel> bakedCache; - - @Shadow @Final private Map unbakedCache; - - private BakedModel bakeIfPossible(ResourceLocation p_229350_1_) { - BakedModel ibakedmodel = null; - - try { - ibakedmodel = this.bake(p_229350_1_, BlockModelRotation.X0_Y0); - } catch (Exception exception) { - exception.printStackTrace(); - LOGGER.warn("Unable to bake model: '{}': {}", p_229350_1_, exception); - } - - return ibakedmodel; - } - - private boolean requiresBake(UnbakedModel model) { - if(model instanceof BlockModel && ((BlockModel)model).customData.hasCustomGeometry()) - return true; - else - return false; - } - - @Inject(method = "", at = @At("TAIL")) - private void bakeModels(ResourceManager arg, BlockColors arg2, ProfilerFiller pProfiler, int i, CallbackInfo ci) { - pProfiler.push("atlas"); - Minecraft.getInstance().executeBlocking(() -> { - for(Pair pair : this.atlasPreparations.values()) { - TextureAtlas atlastexture = pair.getFirst(); - TextureAtlas.Preparations atlastexture$sheetdata = pair.getSecond(); - atlastexture.reload(atlastexture$sheetdata); - } - }); - pProfiler.popPush("baking"); - StartupMessageManager.mcLoaderConsumer().ifPresent(c -> c.accept("Baking models")); - this.atlasSet = new AtlasSet(this.atlasPreparations.values().stream().map(Pair::getFirst).collect(Collectors.toList())); - BakedModel missingModel = this.bake(MISSING_MODEL_LOCATION, BlockModelRotation.X0_Y0); - this.bakedTopLevelModels.put(MISSING_MODEL_LOCATION, missingModel); - Collection modsListening = ModUtil.findAllModsListeningToEvent(ModelEvent.BakingCompleted.class); - LOGGER.debug("Found ModelBakeEvent listeners: [" + String.join(", ", modsListening) + "]"); - Set incompatibleLazyBakedModels = ImmutableSet.builder() - .addAll(modsListening) - .build(); - /* First, bake any incompatible models ahead of time (for mods that have custom models) */ - new ArrayList<>(this.unbakedCache.keySet()).forEach(location -> { - if(incompatibleLazyBakedModels.contains(location.getNamespace())) { - this.bakeIfPossible(location); - } - }); - List multiparts = new ArrayList<>(); - /* Then store them as top-level models if needed, and set up the lazy models */ - this.topLevelModels.forEach((location, value) -> { - if (requiresBake(value) || incompatibleLazyBakedModels.contains(location.getNamespace())) { - BakedModel model = this.bakeIfPossible(location); - if (model != null) - this.bakedTopLevelModels.put(location, model); - } else { - if(value instanceof MultiPart || value instanceof MultiVariant) { - multiparts.add(location); - } else { - this.bakedTopLevelModels.put(location, new LazyBakedModel(() -> { - synchronized (this.bakedCache) { - BakedModel ibakedmodel = this.bakeIfPossible(location); - - return ibakedmodel != null ? ibakedmodel : missingModel; - } - })); - } - } - }); - multiparts.forEach(location -> { - BakedModel model = this.bakeIfPossible(location); - if (model != null) - this.bakedTopLevelModels.put(location, model); - }); - pProfiler.pop(); - } - - /** - * @author embeddedt - * @reason texture loading and baking are moved earlier in the launch process, only render thread stuff is done here - */ - @Overwrite - public AtlasSet uploadTextures(TextureManager pResourceManager, ProfilerFiller pProfiler) { - pProfiler.push("atlas_upload"); - for(Pair pair : this.atlasPreparations.values()) { - TextureAtlas atlastexture = pair.getFirst(); - TextureAtlas.Preparations atlastexture$sheetdata = pair.getSecond(); - pResourceManager.register(atlastexture.location(), atlastexture); - pResourceManager.bindForSetup(atlastexture.location()); - atlastexture.updateFilter(atlastexture$sheetdata); - } - pProfiler.pop(); - return this.atlasSet; - } - - @Override - public AtlasSet getUnfinishedAtlasSet() { - return this.atlasSet; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelManagerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelManagerMixin.java deleted file mode 100644 index 574551a8..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelManagerMixin.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.faster_baking; - -import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.client.resources.model.ModelManager; -import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.server.packs.resources.ResourceManager; -import org.embeddedt.modernfix.models.LazyBakedModel; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(ModelManager.class) -public class ModelManagerMixin { - @Inject(method = "apply(Lnet/minecraft/client/resources/model/ModelBakery;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V", - at = @At(value = "RETURN")) - private void allowBake(ModelBakery pObject, ResourceManager pResourceManager, ProfilerFiller pProfiler, CallbackInfo ci) { - LazyBakedModel.allowBakeForFlags = true; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BooleanPropertyMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/BooleanPropertyMixin.java similarity index 91% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BooleanPropertyMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/BooleanPropertyMixin.java index 16a6e253..a29f6ba7 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BooleanPropertyMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/BooleanPropertyMixin.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; +package org.embeddedt.modernfix.mixin.perf.model_optimizations; import com.google.common.collect.ImmutableSet; import net.minecraft.world.level.block.state.properties.BooleanProperty; diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/OBJLoaderMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/OBJLoaderMixin.java similarity index 95% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/OBJLoaderMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/OBJLoaderMixin.java index 954083df..6f7dd2fc 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/OBJLoaderMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/OBJLoaderMixin.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; +package org.embeddedt.modernfix.mixin.perf.model_optimizations; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.client.model.obj.ObjLoader; diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/PropertyMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/PropertyMixin.java similarity index 95% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/PropertyMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/PropertyMixin.java index 417ed7e1..90c04d57 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/PropertyMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/PropertyMixin.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; +package org.embeddedt.modernfix.mixin.perf.model_optimizations; import net.minecraft.world.level.block.state.properties.Property; import org.embeddedt.modernfix.dedup.IdentifierCaches; diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/SelectorMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/SelectorMixin.java similarity index 90% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/SelectorMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/SelectorMixin.java index 19c88421..e52d84c4 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/SelectorMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/SelectorMixin.java @@ -1,6 +1,5 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; +package org.embeddedt.modernfix.mixin.perf.model_optimizations; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.client.renderer.block.model.multipart.Selector; diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/TransformationMatrixMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/TransformationMatrixMixin.java similarity index 92% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/TransformationMatrixMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/TransformationMatrixMixin.java index 5d02a603..3ef98818 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/TransformationMatrixMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/TransformationMatrixMixin.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; +package org.embeddedt.modernfix.mixin.perf.model_optimizations; import com.mojang.math.Matrix4f; import com.mojang.math.Transformation; diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BlockModelShaperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BlockModelShaperMixin.java deleted file mode 100644 index 9da0fed8..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BlockModelShaperMixin.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; - -import com.google.common.collect.Interner; -import com.google.common.collect.Interners; -import net.minecraft.client.renderer.block.BlockModelShaper; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Mixin(BlockModelShaper.class) -public class BlockModelShaperMixin { - private static Map stateToPropertiesCache = new ConcurrentHashMap<>(); - - @Redirect(method = "stateToModelLocation(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/client/resources/model/ModelResourceLocation;", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/block/BlockModelShaper;statePropertiesToString(Ljava/util/Map;)Ljava/lang/String;")) - private static String getCachedProperty(Map, Comparable> values, ResourceLocation location, BlockState state) { - /* We intentionally don't use the values parameter inside the lambda to avoid an allocation */ - return stateToPropertiesCache.computeIfAbsent(state, s -> BlockModelShaper.statePropertiesToString(s.getValues())); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java deleted file mode 100644 index 4bde292a..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java +++ /dev/null @@ -1,111 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.client.color.block.BlockColors; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.Util; -import net.minecraft.core.Registry; -import net.minecraftforge.fml.loading.progress.StartupMessageManager; -import org.embeddedt.modernfix.ModernFix; -import org.embeddedt.modernfix.util.AsyncStopwatch; -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.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import java.util.stream.Stream; - -import net.minecraft.client.renderer.block.model.BlockModelDefinition; -import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.client.resources.model.ModelResourceLocation; - -@Mixin(ModelBakery.class) -public abstract class ModelBakeryMixin { - @Shadow @Final protected ResourceManager resourceManager; - - private Map>> deserializedBlockstateCache = null; - - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", ordinal = 0)) - private void preloadJsonModels(ProfilerFiller profilerIn, String str) { - StartupMessageManager.mcLoaderConsumer().ifPresent(c -> c.accept("Loading models")); - profilerIn.popPush("loadblockstates"); - AsyncStopwatch.measureAndLogSerialRunningTime("Parallel blockstate loading", () -> { - ThreadLocal containerHolder = ThreadLocal.withInitial(BlockModelDefinition.Context::new); - this.deserializedBlockstateCache = new ConcurrentHashMap<>(); - ArrayList> futures = new ArrayList<>(); - for(Block block : Registry.BLOCK) { - ResourceLocation blockLocation = Registry.BLOCK.getKey(block); - futures.add(CompletableFuture.runAsync(() -> { - ResourceLocation blockStateJSON = new ResourceLocation(blockLocation.getNamespace(), "blockstates/" + blockLocation.getPath() + ".json"); - List blockStates; - /* Some mods' custom resource pack implementations don't seem to like concurrency here */ - synchronized(this.resourceManager) { - blockStates = this.resourceManager.getResourceStack(blockStateJSON); - } - List> definitions = new ArrayList<>(); - StateDefinition stateContainer = block.getStateDefinition(); - BlockModelDefinition.Context context = containerHolder.get(); - context.setDefinition(stateContainer); - for(Resource resource : blockStates) { - try (InputStream inputstream = resource.open()) { - BlockModelDefinition definition = BlockModelDefinition.fromStream(context, new InputStreamReader(inputstream, StandardCharsets.UTF_8)); - definitions.add(Pair.of(resource.sourcePackId(), definition)); - } catch (Exception exception1) { - ModernFix.LOGGER.warn(String.format("Exception loading blockstate definition: '%s' in resourcepack: '%s': %s", blockStateJSON, resource.sourcePackId(), exception1.getMessage())); - return; - } - } - this.deserializedBlockstateCache.put(blockLocation, definitions); - }, Util.backgroundExecutor())); - } - CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); - }); - profilerIn.popPush(str); - } - - @Inject(method = "", at = @At("RETURN")) - private void clearModelCache(ResourceManager arg, BlockColors arg2, ProfilerFiller arg3, int i, CallbackInfo ci) { - deserializedBlockstateCache.clear(); - } - - private List replacementList = null; - - @Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/ResourceManager;getResourceStack(Lnet/minecraft/resources/ResourceLocation;)Ljava/util/List;", ordinal = 0)) - private List getResourceList(ResourceManager instance, ResourceLocation jsonLocation, ResourceLocation originalBlockLocation) throws IOException { - replacementList = null; - if(this.deserializedBlockstateCache != null) { - if(!(originalBlockLocation instanceof ModelResourceLocation)) { - throw new AssertionError("Injector in unexpected spot?"); - } - ModelResourceLocation mrl = (ModelResourceLocation)originalBlockLocation; - ResourceLocation location = new ResourceLocation(mrl.getNamespace(), mrl.getPath()); - List theList = this.deserializedBlockstateCache.get(location); - if(theList != null && theList.size() > 0) { - replacementList = theList; - return theList; - } - } - return instance.getResourceStack(jsonLocation); - } - - @Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;map(Ljava/util/function/Function;)Ljava/util/stream/Stream;", ordinal = 0)) - private Stream fakeResourceList(Stream instance, Function function) { - return replacementList != null ? instance : instance.map(function); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/MultipartMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/MultipartMixin.java deleted file mode 100644 index 5d375be1..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/MultipartMixin.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading.multipart; - -import net.minecraft.client.renderer.block.model.multipart.MultiPart; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.util.List; -import java.util.stream.Stream; - -@Mixin(MultiPart.class) -public class MultipartMixin { - @Redirect(method = "getMaterials", at = @At(value = "INVOKE", target = "Ljava/util/List;stream()Ljava/util/stream/Stream;", ordinal = 0)) - private Stream makeStreamParallel(List instance) { - return instance.parallelStream(); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/VariantListMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/VariantListMixin.java deleted file mode 100644 index ead9af25..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/VariantListMixin.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading.multipart; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.renderer.block.model.Variant; -import net.minecraft.client.renderer.block.model.MultiVariant; -import net.minecraft.resources.ResourceLocation; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; -import org.spongepowered.asm.mixin.Shadow; - -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - -@Mixin(MultiVariant.class) -public abstract class VariantListMixin { - @Shadow public abstract List getVariants(); - - /** - * @author embeddedt - * @reason Parallelize calls to getMaterials - */ - @Overwrite - public Collection getMaterials(Function pModelGetter, Set> pMissingTextureErrors) { - List models = this.getVariants().stream().map(Variant::getModelLocation).distinct().map(pModelGetter).collect(Collectors.toList()); - return models.parallelStream().flatMap(model -> model.getMaterials(pModelGetter, pMissingTextureErrors).stream()).collect(Collectors.toSet()); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/state_definition_construct/StateDefinitionMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/state_definition_construct/StateDefinitionMixin.java new file mode 100644 index 00000000..dd6157c3 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/state_definition_construct/StateDefinitionMixin.java @@ -0,0 +1,29 @@ +package org.embeddedt.modernfix.mixin.perf.state_definition_construct; + +import com.google.common.collect.ImmutableSortedMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.StateHolder; +import net.minecraft.world.level.block.state.properties.Property; +import org.embeddedt.modernfix.blockstate.FakeStateMap; +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.ModifyVariable; + +import java.util.Map; + +@Mixin(StateDefinition.class) +public class StateDefinitionMixin> { + @Shadow @Final private ImmutableSortedMap> propertiesByName; + + @ModifyVariable(method = "", at = @At(value = "STORE", ordinal = 0), ordinal = 1, index = 8) + private Map, Comparable>, S> useArrayMap(Map, Comparable>, S> in) { + int numStates = 1; + for(Property prop : this.propertiesByName.values()) { + numStates *= prop.getPossibleValues().size(); + } + return new FakeStateMap<>(numStates); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/use_integrated_resources/LootTableHelperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/use_integrated_resources/LootTableHelperMixin.java deleted file mode 100644 index 8ec63f73..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/use_integrated_resources/LootTableHelperMixin.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.use_integrated_resources; - -import jeresources.util.LootTableHelper; -import net.minecraft.server.MinecraftServer; -import net.minecraft.world.level.Level; -import net.minecraftforge.server.ServerLifecycleHooks; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(LootTableHelper.class) -public class LootTableHelperMixin { - @Redirect(method = "getLootTables", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getServer()Lnet/minecraft/server/MinecraftServer;")) - private static MinecraftServer useIntegrated(Level level) { - MinecraftServer server = level.getServer(); - if(server != null) - return server; - return ServerLifecycleHooks.getCurrentServer(); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/models/LazyBakedModel.java b/src/main/java/org/embeddedt/modernfix/models/LazyBakedModel.java deleted file mode 100644 index 92d1ac3e..00000000 --- a/src/main/java/org/embeddedt/modernfix/models/LazyBakedModel.java +++ /dev/null @@ -1,140 +0,0 @@ -package org.embeddedt.modernfix.models; - -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.datafixers.util.Pair; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.block.model.ItemOverrides; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.world.item.ItemStack; -import net.minecraft.core.Direction; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraftforge.client.ChunkRenderTypeSet; -import net.minecraftforge.client.model.data.ModelData; -import org.embeddedt.modernfix.ModernFix; -import org.jetbrains.annotations.NotNull; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.List; -import java.util.Random; -import java.util.function.Supplier; - -public class LazyBakedModel implements BakedModel { - private BakedModel delegate = null; - private Supplier delegateSupplier; - - /** - * This flag is changed to true when we should bake instead of returning reasonable defaults for certain - * method calls. - */ - public static boolean allowBakeForFlags = false; - - public LazyBakedModel(Supplier delegateSupplier) { - this.delegateSupplier = delegateSupplier; - } - - public BakedModel computeDelegate() { - if(this.delegate == null) { - this.delegate = this.delegateSupplier.get(); - this.delegateSupplier = null; - } - return this.delegate; - } - - @Override - public List getQuads(@Nullable BlockState pState, @Nullable Direction pSide, RandomSource pRand) { - return computeDelegate().getQuads(pState, pSide, pRand); - } - - @Override - public boolean useAmbientOcclusion() { - return computeDelegate().useAmbientOcclusion(); - } - - @Override - public boolean isGui3d() { - return computeDelegate().isGui3d(); - } - - @Override - public boolean usesBlockLight() { - return computeDelegate().usesBlockLight(); - } - - @Override - public boolean isCustomRenderer() { - if(this.delegate != null) - return this.delegate.isCustomRenderer(); - if(!LazyBakedModel.allowBakeForFlags) - return false; - return computeDelegate().isCustomRenderer(); - } - - @Override - public TextureAtlasSprite getParticleIcon() { - return computeDelegate().getParticleIcon(); - } - - @Override - public ItemTransforms getTransforms() { - return computeDelegate().getTransforms(); - } - - @Override - public ItemOverrides getOverrides() { - return computeDelegate().getOverrides(); - } - - @Nonnull - @Override - public List getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull RandomSource rand, @Nonnull ModelData extraData, @org.jetbrains.annotations.Nullable RenderType renderType) { - return computeDelegate().getQuads(state, side, rand, extraData, renderType); - } - - @Override - public boolean useAmbientOcclusion(BlockState state) { - return computeDelegate().useAmbientOcclusion(state); - } - - @Override - public boolean useAmbientOcclusion(BlockState state, RenderType renderType) { - return computeDelegate().useAmbientOcclusion(state, renderType); - } - - @Override - public BakedModel applyTransform(ItemTransforms.TransformType transformType, PoseStack poseStack, boolean applyLeftHandTransform) { - return computeDelegate().applyTransform(transformType, poseStack, applyLeftHandTransform); - } - - @Nonnull - @Override - public ModelData getModelData(@Nonnull BlockAndTintGetter world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull ModelData tileData) { - return computeDelegate().getModelData(world, pos, state, tileData); - } - - @Override - public TextureAtlasSprite getParticleIcon(@Nonnull ModelData data) { - return computeDelegate().getParticleIcon(data); - } - - @Override - public ChunkRenderTypeSet getRenderTypes(@NotNull BlockState state, @NotNull RandomSource rand, @NotNull ModelData data) { - return computeDelegate().getRenderTypes(state, rand, data); - } - - @Override - public List getRenderPasses(ItemStack itemStack, boolean fabulous) { - return computeDelegate().getRenderPasses(itemStack, fabulous); - } - - @Override - public List getRenderTypes(ItemStack itemStack, boolean fabulous) { - return computeDelegate().getRenderTypes(itemStack, fabulous); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/textures/StbStitcher.java b/src/main/java/org/embeddedt/modernfix/textures/StbStitcher.java index d5958c08..42c2fdf8 100644 --- a/src/main/java/org/embeddedt/modernfix/textures/StbStitcher.java +++ b/src/main/java/org/embeddedt/modernfix/textures/StbStitcher.java @@ -92,18 +92,18 @@ public class StbStitcher { } } - private static STBRPRect setWrapper(STBRPRect rect, int id, int width, int height, int x, int y, boolean was_packed) { + public static STBRPRect setWrapper(STBRPRect rect, int id, int width, int height, int x, int y, boolean was_packed) { try { if(MH_rect_shortSet != null) - return (STBRPRect)MH_rect_shortSet.invokeExact(rect, id, (short)width, (short)height, (short)0, (short)0, false); + return (STBRPRect)MH_rect_shortSet.invokeExact(rect, id, (short)width, (short)height, (short)x, (short)y, was_packed); else - return (STBRPRect)MH_rect_intSet.invokeExact(rect, id, width, height, 0, 0, false); + return (STBRPRect)MH_rect_intSet.invokeExact(rect, id, width, height, x, y, was_packed); } catch(Throwable e) { throw new AssertionError(e); } } - private static int getX(STBRPRect rect) { + public static int getX(STBRPRect rect) { try { if(MH_rect_shortX != null) return (short)MH_rect_shortX.invokeExact(rect); @@ -114,7 +114,7 @@ public class StbStitcher { } } - private static int getY(STBRPRect rect) { + public static int getY(STBRPRect rect) { try { if(MH_rect_shortX != null) return (short)MH_rect_shortY.invokeExact(rect); diff --git a/src/main/java/org/embeddedt/modernfix/util/DummyList.java b/src/main/java/org/embeddedt/modernfix/util/DummyList.java new file mode 100644 index 00000000..221c6506 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/util/DummyList.java @@ -0,0 +1,131 @@ +package org.embeddedt.modernfix.util; + +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/** + * List with no-op methods. + */ +public class DummyList implements List { + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public boolean contains(Object o) { + return false; + } + + @NotNull + @Override + public Iterator iterator() { + return Collections.emptyIterator(); + } + + @NotNull + @Override + public Object[] toArray() { + return new Object[0]; + } + + @NotNull + @Override + public T1[] toArray(@NotNull T1[] t1s) { + return Arrays.copyOf(t1s, 0); + } + + @Override + public boolean add(T t) { + return false; + } + + @Override + public boolean remove(Object o) { + return false; + } + + @Override + public boolean containsAll(@NotNull Collection collection) { + return false; + } + + @Override + public boolean addAll(@NotNull Collection collection) { + return false; + } + + @Override + public boolean addAll(int i, @NotNull Collection collection) { + return false; + } + + @Override + public boolean removeAll(@NotNull Collection collection) { + return false; + } + + @Override + public boolean retainAll(@NotNull Collection collection) { + return false; + } + + @Override + public void clear() { + + } + + @Override + public T get(int i) { + return null; + } + + @Override + public T set(int i, T t) { + return null; + } + + @Override + public void add(int i, T t) { + + } + + @Override + public T remove(int i) { + return null; + } + + @Override + public int indexOf(Object o) { + return -1; + } + + @Override + public int lastIndexOf(Object o) { + return -1; + } + + @NotNull + @Override + public ListIterator listIterator() { + return Collections.emptyListIterator(); + } + + @NotNull + @Override + public ListIterator listIterator(int i) { + return Collections.emptyListIterator(); + } + + @NotNull + @Override + public List subList(int i, int i1) { + return new DummyList<>(); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/util/FileUtil.java b/src/main/java/org/embeddedt/modernfix/util/FileUtil.java index 51045bda..41f2c4f5 100644 --- a/src/main/java/org/embeddedt/modernfix/util/FileUtil.java +++ b/src/main/java/org/embeddedt/modernfix/util/FileUtil.java @@ -9,14 +9,34 @@ public class FileUtil { return file; } - private static final Pattern SLASH_PATTERN = Pattern.compile("(?:\\\\+|\\/+)"); - /** * Normalize a path by removing double slashes, etc. + *

+ * This implementation avoids creating a new string unless there are actually double slashes present + * in the input path. * @param path input path * @return a normalized version of the path */ public static String normalize(String path) { - return SLASH_PATTERN.matcher(path).replaceAll("/"); + char prevChar = 0; + StringBuilder sb = null; + for(int i = 0; i < path.length(); i++) { + char thisChar = path.charAt(i); + if(prevChar != '/' || thisChar != prevChar) { + /* This character should end up in the final string. If we are using the builder, add it there. */ + if(sb != null) + sb.append(thisChar); + } else { + /* This character should not end up in the final string. We need to make a buidler if we haven't + * done so yet. + */ + if(sb == null) { + sb = new StringBuilder(path.length()); + sb.append(path, 0, i); + } + } + prevChar = thisChar; + } + return sb == null ? path : sb.toString(); } } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index f870e652..ceba8d1e 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -12,4 +12,15 @@ public net.minecraft.client.Minecraft f_90999_ # progressListener public-f net.minecraft.network.syncher.EntityDataAccessor f_135010_ # id public-f net.minecraft.network.syncher.SynchedEntityData f_135345_ # itemsById public-f net.minecraft.network.syncher.SynchedEntityData f_135346_ # lock -public net.minecraft.client.gui.screens.worldselection.WorldGenSettingsComponent m_233042_(Lnet/minecraft/client/gui/screens/worldselection/WorldCreationContext;)V # updateSettings \ No newline at end of file +public net.minecraft.client.gui.screens.worldselection.WorldGenSettingsComponent m_233042_(Lnet/minecraft/client/gui/screens/worldselection/WorldCreationContext;)V # updateSettings +public net.minecraft.world.level.block.state.BlockBehaviour f_60439_ # properties +public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60882_ # material +public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60895_ # canOcclude +public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60896_ # isAir +public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60898_ # isRedstoneConductor +public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60899_ # isSuffocating +public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60900_ # isViewBlocking +public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60901_ # hasPostProcess +public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60902_ # emissiveRendering +public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60889_ # requiresCorrectToolForDrops +public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60888_ # destroyTime diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index c4088bc0..5a3d2210 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -28,7 +28,11 @@ "perf.fast_registry_validation.ForgeRegistryMixin", "perf.cache_strongholds.ChunkGeneratorMixin", "perf.cache_upgraded_structures.StructureManagerMixin", - "perf.cache_strongholds.ServerLevelMixin" + "perf.cache_strongholds.ServerLevelMixin", + "perf.state_definition_construct.StateDefinitionMixin", + "perf.compress_blockstate.BlockStateBaseMixin", + "perf.compress_blockstate.BlockBehaviourMixin", + "devenv.MinecraftServerMixin" ], "client": [ "core.MinecraftMixin", @@ -36,15 +40,16 @@ "feature.measure_time.MinecraftMixin", "feature.reduce_loading_screen_freezes.ModelBakeryMixin", "bugfix.concurrency.MinecraftMixin", - "perf.parallelize_model_loading.BlockModelShaperMixin", - "perf.parallelize_model_loading.ModelBakeryMixin", - "perf.parallelize_model_loading.OBJLoaderMixin", - "perf.parallelize_model_loading.multipart.MultipartMixin", - "perf.parallelize_model_loading.multipart.VariantListMixin", - "perf.parallelize_model_loading.SelectorMixin", - "perf.parallelize_model_loading.TransformationMatrixMixin", - "perf.parallelize_model_loading.BooleanPropertyMixin", - "perf.parallelize_model_loading.PropertyMixin", + "perf.dynamic_resources.BlockModelShaperMixin", + "perf.dynamic_resources.ItemModelShaperMixin", + "perf.dynamic_resources.ModelBakeryMixin", + "perf.dynamic_resources.ctm.TextureMetadataHandlerMixin", + "perf.dynamic_resources.ctm.CTMPackReloadListenerMixin", + "perf.model_optimizations.OBJLoaderMixin", + "perf.model_optimizations.SelectorMixin", + "perf.model_optimizations.TransformationMatrixMixin", + "perf.model_optimizations.BooleanPropertyMixin", + "perf.model_optimizations.PropertyMixin", "perf.thread_priorities.IntegratedServerMixin", "safety.BlockColorsMixin", "perf.flatten_model_predicates.AndConditionMixin", @@ -52,15 +57,14 @@ "perf.flatten_model_predicates.PropertyValueConditionMixin", "perf.blast_search_trees.MinecraftMixin", "perf.blast_search_trees.IngredientFilterInvoker", - "perf.faster_baking.ModelBakeryMixin", - "perf.faster_baking.BlockModelShapesMixin", - "perf.faster_baking.ModelManagerMixin", "perf.cache_model_materials.VanillaModelMixin", "perf.cache_model_materials.MultipartMixin", "perf.faster_texture_stitching.StitcherMixin", "perf.use_integrated_resources.LootTableHelperMixin", "perf.skip_first_datapack_reload.CreateWorldScreenMixin", - "perf.faster_singleplayer_load.MinecraftServerMixin" + "perf.faster_singleplayer_load.MinecraftServerMixin", + "devenv.MinecraftMixin", + "devenv.NarratorMixin" ], "injectors": { "defaultRequire": 1