diff --git a/README.md b/README.md index d9266842..e1543d82 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # ModernFix -A Forge 1.16 mod that uses mixins to make the game slightly more performant (and hopefully less buggy too). - -In the words of asiekierka, questionable "performance improvements" that are not in Forge for probably very good reasons. +A performance mod for modern Minecraft that significantly improves launch times, world load times, memory usage, etc. Some fixes are based on prior work in various Forge PRs (check commit history and/or code comments). The config system is directly derived from Sodium and used under the terms of the LGPL-3.0 license. diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockModelShaperMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockModelShaperMixin.java index b3a1a6e4..9648c6a1 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockModelShaperMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockModelShaperMixin.java @@ -6,10 +6,13 @@ import net.minecraft.client.resources.model.ModelManager; import net.minecraft.world.level.block.state.BlockState; import org.embeddedt.modernfix.annotation.ClientOnlyMixin; 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.embeddedt.modernfix.util.DynamicMap; +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 java.util.Map; @Mixin(BlockModelShaper.class) @@ -17,6 +20,15 @@ import org.spongepowered.asm.mixin.Shadow; public class BlockModelShaperMixin { @Shadow @Final private ModelManager modelManager; + @Shadow + private Map modelByStateCache; + + @Inject(method = { "", "replaceCache" }, at = @At("RETURN")) + private void replaceModelMap(CallbackInfo ci) { + // replace the backing map for mods which will access it + this.modelByStateCache = new DynamicMap<>(state -> modelManager.getModel(ModelLocationCache.get(state))); + } + @Overwrite public BakedModel getBlockModel(BlockState state) { BakedModel model = modelManager.getModel(ModelLocationCache.get(state)); diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemModelShaperMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemModelShaperMixin.java index 918e5266..8739d0e3 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemModelShaperMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemModelShaperMixin.java @@ -1,5 +1,6 @@ package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.client.renderer.ItemModelShaper; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelManager; @@ -7,6 +8,7 @@ import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; +import org.embeddedt.modernfix.util.DynamicInt2ObjectMap; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -20,6 +22,8 @@ public abstract class ItemModelShaperMixin { @Shadow public abstract ModelManager getModelManager(); + @Shadow @Final @Mutable private Int2ObjectMap shapesCache; + private Map overrideLocationsVanilla; public ItemModelShaperMixin() { @@ -31,6 +35,7 @@ public abstract class ItemModelShaperMixin { @Inject(method = "", at = @At("RETURN")) private void replaceLocationMap(CallbackInfo ci) { overrideLocationsVanilla = new HashMap<>(); + this.shapesCache = new DynamicInt2ObjectMap<>(index -> getModelManager().getModel(ModelLocationCache.get(Item.byId(index)))); } @Unique diff --git a/common/src/main/java/org/embeddedt/modernfix/structure/CachingStructureManager.java b/common/src/main/java/org/embeddedt/modernfix/structure/CachingStructureManager.java index 64f01bcc..d14f2777 100644 --- a/common/src/main/java/org/embeddedt/modernfix/structure/CachingStructureManager.java +++ b/common/src/main/java/org/embeddedt/modernfix/structure/CachingStructureManager.java @@ -1,6 +1,7 @@ package org.embeddedt.modernfix.structure; import com.mojang.datafixers.DataFixer; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.minecraft.SharedConstants; import net.minecraft.core.HolderGetter; import net.minecraft.nbt.CompoundTag; @@ -16,6 +17,7 @@ import org.embeddedt.modernfix.util.FileUtil; import java.io.*; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Set; public class CachingStructureManager { private static ThreadLocal digestThreadLocal = ThreadLocal.withInitial(() -> { @@ -46,6 +48,8 @@ public class CachingStructureManager { return sb.toString(); } + private static final Set laggyStructureMods = new ObjectOpenHashSet<>(); + public static CompoundTag readStructureTag(ResourceLocation location, DataFixer datafixer, InputStream stream) throws IOException { byte[] structureBytes = toBytes(stream); CompoundTag currentTag = NbtIo.readCompressed(new ByteArrayInputStream(structureBytes)); @@ -54,6 +58,11 @@ public class CachingStructureManager { } int currentDataVersion = currentTag.getInt("DataVersion"); if(currentDataVersion < SharedConstants.getCurrentVersion().getDataVersion().getVersion()) { + synchronized (laggyStructureMods) { + if(laggyStructureMods.add(location.getNamespace())) { + ModernFix.LOGGER.warn("Mod {} is shipping outdated structure files, which can cause worldgen lag; please report this to them.", location.getNamespace()); + } + } /* Needs upgrade, try looking up from cache */ MessageDigest hasher = digestThreadLocal.get(); hasher.reset(); diff --git a/common/src/main/java/org/embeddedt/modernfix/util/DynamicInt2ObjectMap.java b/common/src/main/java/org/embeddedt/modernfix/util/DynamicInt2ObjectMap.java new file mode 100644 index 00000000..5c44208e --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/util/DynamicInt2ObjectMap.java @@ -0,0 +1,61 @@ +package org.embeddedt.modernfix.util; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.objects.ObjectCollection; +import it.unimi.dsi.fastutil.objects.ObjectSet; + +import java.util.Map; +import java.util.function.Function; + +public class DynamicInt2ObjectMap extends DynamicMap implements Int2ObjectMap { + public DynamicInt2ObjectMap(Function function) { + super(function); + } + + @Override + public IntSet keySet() { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectCollection values() { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectSet> entrySet() { + throw new UnsupportedOperationException(); + } + + @Override + public void defaultReturnValue(V rv) { + throw new UnsupportedOperationException(); + } + + @Override + public V defaultReturnValue() { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectSet> int2ObjectEntrySet() { + throw new UnsupportedOperationException(); + } + + @Override + public V getOrDefault(int key, V defaultValue) { + V value = get(key); + return value == null ? defaultValue : value; + } + + @Override + public V get(int key) { + return function.apply(key); + } + + @Override + public boolean containsKey(int key) { + return true; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/util/DynamicMap.java b/common/src/main/java/org/embeddedt/modernfix/util/DynamicMap.java new file mode 100644 index 00000000..e8803770 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/util/DynamicMap.java @@ -0,0 +1,81 @@ +package org.embeddedt.modernfix.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +public class DynamicMap implements Map { + protected final Function function; + + public DynamicMap(Function function) { + this.function = function; + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean containsKey(Object o) { + return true; + } + + @Override + public boolean containsValue(Object o) { + return true; + } + + @Override + public V get(Object o) { + return function.apply((K)o); + } + + @Nullable + @Override + public V put(K k, V v) { + throw new UnsupportedOperationException(); + } + + @Override + public V remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(@NotNull Map map) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Set keySet() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Collection values() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Set> entrySet() { + throw new UnsupportedOperationException(); + } +} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index a739d549..2b241674 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -33,5 +33,8 @@ "fabric-screen-api-v1": "*", "fabric-command-api-v1": "*", "minecraft": ">=1.16.5" + }, + "breaks": { + "dashloader": "*" } } \ No newline at end of file