From 863fbaf3a4ddab070333aa81ce2bc706834184f5 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 24 Apr 2023 16:56:27 -0400 Subject: [PATCH 01/36] Remove a lot of now dead code --- .../ModernFixCachingClassTransformer.java | 213 ------------------ .../embeddedt/modernfix/ModernFixClient.java | 8 - .../org/embeddedt/modernfix/agent/Agent.java | 59 ----- .../api/IHashableTransformer.java | 11 - .../hashers/CoreModTransformerHasher.java | 49 ---- .../hashers/MixinTransformerHasher.java | 154 ------------- .../modernfix/core/ModernFixMixinPlugin.java | 22 -- .../core/config/ModernFixEarlyConfig.java | 1 - .../embeddedt/modernfix/load/LoadEvents.java | 113 ---------- .../MinecraftServerMixin.java | 41 ---- .../AndConditionMixin.java | 32 --- .../OrConditionMixin.java | 32 --- .../PropertyValueConditionMixin.java | 77 ------- .../preload_block_classes/GameDataMixin.java | 20 -- .../modernfix/predicate/AllPredicate.java | 22 -- .../modernfix/predicate/AnyPredicate.java | 22 -- .../predicate/CachedModelPredicate.java | 7 - .../StatePropertyPredicateHelper.java | 33 --- .../predicate/all/AllMatchOneBoolean.java | 47 ---- .../predicate/all/AllMatchOneObject.java | 38 ---- .../predicate/any/AllMatchAnyObject.java | 39 ---- .../predicate/single/SingleMatchAny.java | 63 ------ .../predicate/single/SingleMatchOne.java | 36 --- .../registry/DeferredRegisterBaker.java | 59 ----- .../screen/DeferredLevelLoadingScreen.java | 27 --- .../modernfix/util/AsyncStopwatch.java | 45 ---- .../modernfix/util/BlockClassPreloader.java | 90 -------- .../modernfix/util/CachedSupplier.java | 33 --- .../org/embeddedt/modernfix/util/JEIUtil.java | 3 +- .../util/OrderedParallelModDispatcher.java | 97 -------- src/main/resources/modernfix.mixins.json | 5 - 31 files changed, 1 insertion(+), 1497 deletions(-) delete mode 100644 src/main/java/cpw/mods/modlauncher/ModernFixCachingClassTransformer.java delete mode 100644 src/main/java/org/embeddedt/modernfix/agent/Agent.java delete mode 100644 src/main/java/org/embeddedt/modernfix/classloading/api/IHashableTransformer.java delete mode 100644 src/main/java/org/embeddedt/modernfix/classloading/hashers/CoreModTransformerHasher.java delete mode 100644 src/main/java/org/embeddedt/modernfix/classloading/hashers/MixinTransformerHasher.java delete mode 100644 src/main/java/org/embeddedt/modernfix/load/LoadEvents.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/faster_singleplayer_load/MinecraftServerMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/AndConditionMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/OrConditionMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/PropertyValueConditionMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/preload_block_classes/GameDataMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/predicate/AllPredicate.java delete mode 100644 src/main/java/org/embeddedt/modernfix/predicate/AnyPredicate.java delete mode 100644 src/main/java/org/embeddedt/modernfix/predicate/CachedModelPredicate.java delete mode 100644 src/main/java/org/embeddedt/modernfix/predicate/StatePropertyPredicateHelper.java delete mode 100644 src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneBoolean.java delete mode 100644 src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneObject.java delete mode 100644 src/main/java/org/embeddedt/modernfix/predicate/any/AllMatchAnyObject.java delete mode 100644 src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchAny.java delete mode 100644 src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchOne.java delete mode 100644 src/main/java/org/embeddedt/modernfix/registry/DeferredRegisterBaker.java delete mode 100644 src/main/java/org/embeddedt/modernfix/screen/DeferredLevelLoadingScreen.java delete mode 100644 src/main/java/org/embeddedt/modernfix/util/AsyncStopwatch.java delete mode 100644 src/main/java/org/embeddedt/modernfix/util/BlockClassPreloader.java delete mode 100644 src/main/java/org/embeddedt/modernfix/util/CachedSupplier.java delete mode 100644 src/main/java/org/embeddedt/modernfix/util/OrderedParallelModDispatcher.java diff --git a/src/main/java/cpw/mods/modlauncher/ModernFixCachingClassTransformer.java b/src/main/java/cpw/mods/modlauncher/ModernFixCachingClassTransformer.java deleted file mode 100644 index 1e09c60b..00000000 --- a/src/main/java/cpw/mods/modlauncher/ModernFixCachingClassTransformer.java +++ /dev/null @@ -1,213 +0,0 @@ -package cpw.mods.modlauncher; - -import java.io.*; -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ForkJoinPool; - -import cpw.mods.modlauncher.api.ITransformer; -import cpw.mods.modlauncher.api.ITransformerActivity; -import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; -import net.minecraftforge.coremod.transformer.CoreModBaseTransformer; -import net.minecraftforge.fml.loading.FMLPaths; -import net.minecraftforge.fml.loading.LoadingModList; -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.embeddedt.modernfix.ModernFix; -import org.embeddedt.modernfix.classloading.api.IHashableTransformer; -import org.embeddedt.modernfix.classloading.hashers.CoreModTransformerHasher; -import org.embeddedt.modernfix.classloading.hashers.MixinTransformerHasher; -import org.embeddedt.modernfix.util.FileUtil; -import org.objectweb.asm.Type; -import org.spongepowered.asm.launch.MixinLaunchPluginLegacy; - -import javax.lang.model.SourceVersion; - -public class ModernFixCachingClassTransformer extends ClassTransformer { - public static final Logger LOGGER = LogManager.getLogger("ModernFixCachingTransformer"); - - public static File CLASS_CACHE_FOLDER = null; - private final LaunchPluginHandler pluginHandler; - private final Map plugins; - private final TransformStore transformStore; - private final TransformerAuditTrail auditTrail; - private final TransformingClassLoader transformingClassLoader; - private final HashMap>> transformersByClass; - - private ConcurrentHashMap, byte[]>> transformationCache; - private ForkJoinPool classSaverPool = ForkJoinPool.commonPool(); - - public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - - public static ThreadLocal systemHasher = ThreadLocal.withInitial(() -> { - try { - return MessageDigest.getInstance("SHA-256"); - } catch(NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - }); - - public ModernFixCachingClassTransformer(TransformStore transformStore, LaunchPluginHandler pluginHandler, TransformingClassLoader transformingClassLoader, TransformerAuditTrail trail) { - super(transformStore, pluginHandler, transformingClassLoader, trail); - CLASS_CACHE_FOLDER = FileUtil.childFile(FMLPaths.GAMEDIR.get().resolve("modernfix").resolve("classCacheV1").toFile()); - this.transformStore = transformStore; - this.pluginHandler = pluginHandler; - this.transformingClassLoader = transformingClassLoader; - this.auditTrail = trail; - /* Build a lookup table of all transformers for a given class */ - this.transformersByClass = new HashMap<>(); - try { - Field pluginsField = LaunchPluginHandler.class.getDeclaredField("plugins"); - pluginsField.setAccessible(true); - this.plugins = (Map)pluginsField.get(this.pluginHandler); - Field transformersByTypeField = TransformStore.class.getDeclaredField("transformers"); - transformersByTypeField.setAccessible(true); - Field transformersMapField = TransformList.class.getDeclaredField("transformers"); - transformersMapField.setAccessible(true); - EnumMap> transformersByType = (EnumMap>)transformersByTypeField.get(this.transformStore); - for(TransformList transformList : transformersByType.values()) { - Map>> transformers = (Map>>)transformersMapField.get(transformList); - for(Map.Entry>> entry : transformers.entrySet()) { - String className = entry.getKey().getClassName().getClassName(); - List> transformerList = this.transformersByClass.computeIfAbsent(className, k -> new ArrayList<>()); - transformerList.addAll(entry.getValue()); - } - } - for(List> transformerList : this.transformersByClass.values()) { - transformerList.sort((t1, t2) -> Comparator.naturalOrder().compare(StringUtils.join(t1.labels(), " "), StringUtils.join(t2.labels(), " "))); - } - } catch(ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - private ArrayList computeHash(String className, byte[] inputClass, String reason) { - final String internalName = className.replace('.', '/'); - final Type classDesc = Type.getObjectType(internalName); - ArrayList pluginList = new ArrayList<>(); - for(ILaunchPluginService plugin : plugins.values()) { - if(!plugin.handlesClass(classDesc, inputClass.length == 0, reason).isEmpty()) { - pluginList.add(plugin); - } - } - final boolean needsTransforming = transformStore.needsTransforming(internalName); - if (!needsTransforming && pluginList.isEmpty()) { - return null; - } - /* Now compute the hash list for the required transformers */ - ArrayList hashList = new ArrayList<>(); - pluginList.sort((service1, service2) -> Comparator.naturalOrder().compare(service1.name(), service2.name())); - for(ILaunchPluginService service : pluginList) { - byte[] hash = obtainHash(service, className); - if(hash == null) { - return null; - } - hashList.add(hash); - } - if(needsTransforming) { - List> transformers = this.transformersByClass.get(internalName); - if(transformers != null) { - for(ITransformer transformer : transformers) { - byte[] hash = obtainHash(transformer, className); - if(hash == null) { - return null; - } - hashList.add(hash); - } - } - } - /* Hash the class itself last, so that we bail out early if plugins can't hash */ - MessageDigest hasher = systemHasher.get(); - hasher.reset(); - hashList.add(hasher.digest(inputClass)); - return hashList; - } - - /** - * Check the hashed list of transformers and use a cached version of the class if possible. This code needs - * to be very fast as the entire point is to spend very little time doing transformation work that was done before. - * @param inputClass The bytecode to be transformed - * @param className Name of the class - * @param reason Reason for the class being loaded - * @return The transformed version of the class - */ - @Override - public byte[] transform(byte[] inputClass, String className, String reason) { - /* We only want to cache actual transformations */ - if(ITransformerActivity.CLASSLOADING_REASON.equals(reason) || "mixin".equals(reason)) { - final byte[] classToHash = inputClass; - ArrayList hashList = computeHash(className, classToHash, reason); - if(hashList == null) - return super.transform(inputClass, className, reason); - /* Check if the cache contains a transformed class matching these hashes */ - /* TODO maybe sanitize the class name? */ - File cacheLocation = new File(CLASS_CACHE_FOLDER, className.replace('.', '/') + "." + reason); - boolean hashesMatch = true; - try(ObjectInputStream stream = new ObjectInputStream(new FileInputStream(cacheLocation))) { - ArrayList savedHash = (ArrayList)stream.readObject(); - byte[] savedInputClass = (byte[])stream.readObject(); - if(hashList != null) { - for(int i = 0; i < savedHash.size(); i++) { - if(!Arrays.equals(savedHash.get(i), hashList.get(i))) { - hashesMatch = false; - break; - } - } - } else - hashesMatch = false; - if(hashesMatch) - inputClass = savedInputClass; - } catch(IOException | ClassNotFoundException | ClassCastException e) { - if(!(e instanceof FileNotFoundException)) - e.printStackTrace(); - hashesMatch = false; - } - if(!hashesMatch) { - inputClass = super.transform(inputClass, className, reason); - if(hashList != null) { - final byte[] classToSave = inputClass; - final ArrayList hashListToSave = hashList; - classSaverPool.submit(() -> { - cacheLocation.getParentFile().mkdirs(); - try(ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(cacheLocation))) { - stream.writeObject(hashListToSave); - stream.writeObject(classToSave); - } catch(IOException e) { - e.printStackTrace(); - } - }); - } - - } - return inputClass; - } - return super.transform(inputClass, className, reason); - } - - private final byte[] FORGE_HASH = LoadingModList.get().getModFileById("forge").getMods().get(0).getVersion().toString().getBytes(StandardCharsets.UTF_8); - - private byte[] obtainHash(Object o, String className) { - if(o instanceof CoreModBaseTransformer) { - return CoreModTransformerHasher.obtainHash((CoreModBaseTransformer)o); - } else if(o instanceof MixinLaunchPluginLegacy) { - return MixinTransformerHasher.obtainHash((MixinLaunchPluginLegacy)o, className); - } else if(o instanceof IHashableTransformer) { - return ((IHashableTransformer)o).getHashForClass(className); - } else if(o.getClass().getName().startsWith("net.minecraftforge.")) { - return FORGE_HASH; - } else { - LOGGER.warn("No hash implementation found for: " + o.getClass().getName()); - return null; - } - } -} diff --git a/src/main/java/org/embeddedt/modernfix/ModernFixClient.java b/src/main/java/org/embeddedt/modernfix/ModernFixClient.java index a81291e2..f1ab4ff8 100644 --- a/src/main/java/org/embeddedt/modernfix/ModernFixClient.java +++ b/src/main/java/org/embeddedt/modernfix/ModernFixClient.java @@ -20,19 +20,15 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.common.ObfuscationReflectionHelper; -import net.minecraftforge.fml.event.server.FMLServerStartedEvent; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.network.NetworkEvent; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.core.config.ModernFixConfig; -import org.embeddedt.modernfix.load.LoadEvents; import org.embeddedt.modernfix.packet.EntityIDSyncPacket; -import org.embeddedt.modernfix.screen.DeferredLevelLoadingScreen; import org.embeddedt.modernfix.world.IntegratedWatchdog; import java.lang.management.ManagementFactory; import java.lang.reflect.Field; -import java.sql.Ref; import java.util.*; import java.util.function.Supplier; @@ -49,9 +45,6 @@ public class ModernFixClient { public ModernFixClient() { // clear reserve as it's not needed 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()); - } if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) { Optional mfContainer = ModList.get().getModContainerById("modernfix"); if(mfContainer.isPresent()) @@ -91,7 +84,6 @@ public class ModernFixClient { if(event.phase == TickEvent.Phase.END && recipesUpdated && tagsUpdated - && !(Minecraft.getInstance().screen instanceof DeferredLevelLoadingScreen) && worldLoadStartTime != -1 && Minecraft.getInstance().player != null && numRenderTicks++ >= 10) { diff --git a/src/main/java/org/embeddedt/modernfix/agent/Agent.java b/src/main/java/org/embeddedt/modernfix/agent/Agent.java deleted file mode 100644 index 5cf5298d..00000000 --- a/src/main/java/org/embeddedt/modernfix/agent/Agent.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.embeddedt.modernfix.agent; - -import com.google.common.collect.ImmutableMap; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.*; - -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.IllegalClassFormatException; -import java.lang.instrument.Instrumentation; -import java.security.ProtectionDomain; -import java.util.function.Function; - -public class Agent { - public static void agentmain(String args, Instrumentation instrumentation) { - instrumentation.addTransformer(new EarlyTransformer()); - } - - private static class EarlyTransformer implements ClassFileTransformer { - - private static final ImmutableMap> TRANSFORMERS = ImmutableMap.>builder() - .put("net/minecraftforge/fml/loading/moddiscovery/Scanner", EarlyTransformer::transformScanner) - .build(); - - private static ClassNode transformScanner(ClassNode input) { - for(MethodNode method : input.methods) { - if(method.name.equals("fileVisitor")) { - for(int i = 0; i < method.instructions.size(); i++) { - AbstractInsnNode ainsn = method.instructions.get(i); - if(ainsn.getOpcode() == Opcodes.INVOKEVIRTUAL) { - MethodInsnNode minsn = (MethodInsnNode)ainsn; - if(minsn.name.equals("accept") && minsn.owner.equals("org/objectweb/asm/ClassReader")) { - method.instructions.set(minsn.getPrevious(), new LdcInsnNode(ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES)); - return input; - } - } - } - } - } - return input; - } - - @Override - public byte[] transform(ClassLoader classLoader, String s, Class aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException { - Function func = TRANSFORMERS.get(s); - if(func != null) { - ClassReader reader = new ClassReader(bytes); - ClassNode node = new ClassNode(Opcodes.ASM9); - reader.accept(node, 0); - node = func.apply(node); - ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); - node.accept(writer); - return writer.toByteArray(); - } else - return bytes; - } - } -} diff --git a/src/main/java/org/embeddedt/modernfix/classloading/api/IHashableTransformer.java b/src/main/java/org/embeddedt/modernfix/classloading/api/IHashableTransformer.java deleted file mode 100644 index f867a16e..00000000 --- a/src/main/java/org/embeddedt/modernfix/classloading/api/IHashableTransformer.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.embeddedt.modernfix.classloading.api; - -public interface IHashableTransformer { - /** - * Called on an ILaunchPluginService or ITransformer to obtain a unique hash of the transformations that will be applied. - * Used to invalidate the transformation cache when needed. - * @param className Name of class being transformed - * @return A unique hash of the transformations that will be applied - */ - byte[] getHashForClass(String className); -} diff --git a/src/main/java/org/embeddedt/modernfix/classloading/hashers/CoreModTransformerHasher.java b/src/main/java/org/embeddedt/modernfix/classloading/hashers/CoreModTransformerHasher.java deleted file mode 100644 index fa4f602e..00000000 --- a/src/main/java/org/embeddedt/modernfix/classloading/hashers/CoreModTransformerHasher.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.embeddedt.modernfix.classloading.hashers; - -import cpw.mods.modlauncher.ModernFixCachingClassTransformer; -import net.minecraftforge.coremod.CoreMod; -import net.minecraftforge.coremod.transformer.CoreModBaseTransformer; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.nio.file.Files; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.concurrent.ConcurrentHashMap; - -public class CoreModTransformerHasher { - private static final ConcurrentHashMap hashForCoremod; - private static Field coremodField; - - static { - hashForCoremod = new ConcurrentHashMap<>(); - try { - coremodField = CoreModBaseTransformer.class.getDeclaredField("coreMod"); - coremodField.setAccessible(true); - } catch(ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - private static byte[] hashCoreMod(CoreMod coreMod) { - byte[] coreModContents; - try { - coreModContents = Files.readAllBytes(coreMod.getPath()); - } catch(IOException e) { - throw new RuntimeException(e); - } - MessageDigest hasher = ModernFixCachingClassTransformer.systemHasher.get(); - hasher.reset(); - return hasher.digest(coreModContents); - } - - public static byte[] obtainHash(CoreModBaseTransformer transformer) { - CoreMod coremod; - try { - coremod = (CoreMod)coremodField.get(transformer); - } catch(ReflectiveOperationException e) { - throw new RuntimeException(e); - } - return hashForCoremod.computeIfAbsent(coremod, CoreModTransformerHasher::hashCoreMod); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/classloading/hashers/MixinTransformerHasher.java b/src/main/java/org/embeddedt/modernfix/classloading/hashers/MixinTransformerHasher.java deleted file mode 100644 index 1716dcd3..00000000 --- a/src/main/java/org/embeddedt/modernfix/classloading/hashers/MixinTransformerHasher.java +++ /dev/null @@ -1,154 +0,0 @@ -package org.embeddedt.modernfix.classloading.hashers; - -import com.google.common.io.Resources; -import cpw.mods.modlauncher.ModernFixCachingClassTransformer; -import org.spongepowered.asm.launch.IClassProcessor; -import org.spongepowered.asm.launch.MixinLaunchPluginLegacy; -import org.spongepowered.asm.mixin.MixinEnvironment; -import org.spongepowered.asm.mixin.extensibility.IMixinInfo; -import org.spongepowered.asm.mixin.injection.invoke.arg.ArgsClassGenerator; -import org.spongepowered.asm.mixin.transformer.ext.Extensions; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.*; -import java.util.stream.Stream; - -public class MixinTransformerHasher { - private static HashMap hashesByClass = null; - private final static MessageDigest hasher; - - private static Field processorsListField, transformerField, processorField, environmentField; - - private static boolean fixedArgsClassCount = false; - - private static final byte[] NO_MIXINS = new byte[] {(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef}; - - static { - try { - hasher = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - public static byte[] obtainHash(MixinLaunchPluginLegacy plugin, String className) { - /* FIXME runs too early right now, and therefore doesn't pick up the list of mixins correctly */ - synchronized (MixinTransformerHasher.class) { - if(hashesByClass == null) { - try { - if(processorsListField == null) { - processorsListField = MixinLaunchPluginLegacy.class.getDeclaredField("processors"); - processorsListField.setAccessible(true); - } - List processors = (List) processorsListField.get(plugin); - Object transformHandler = null; - for(IClassProcessor processor : processors) { - if(processor.getClass().getName().equals("org.spongepowered.asm.service.modlauncher.MixinTransformationHandler")) { - transformHandler = processor; - break; - } - } - if(transformHandler == null) - throw new IllegalStateException("Mixin transform handler not found"); - if(transformerField == null) { - transformerField = transformHandler.getClass().getDeclaredField("transformer"); - transformerField.setAccessible(true); - } - Object transformer = transformerField.get(transformHandler); - if(!fixedArgsClassCount) { - Path syntheticFolderPath = ModernFixCachingClassTransformer.CLASS_CACHE_FOLDER.toPath().resolve("org").resolve("spongepowered").resolve("asm").resolve("synthetic"); - if(Files.exists(syntheticFolderPath)) { - Field extensionsField = transformer.getClass().getDeclaredField("extensions"); - extensionsField.setAccessible(true); - Extensions extensions = (Extensions)extensionsField.get(transformer); - ArgsClassGenerator argsGen = extensions.getGenerator(ArgsClassGenerator.class); - Field nextIndexField = ArgsClassGenerator.class.getDeclaredField("nextIndex"); - try(Stream argsStream = Files.find(syntheticFolderPath, 1, (path, attr) -> path.getFileName().toString().startsWith("Args$"))) { - int[] startIndex = new int[1]; - startIndex[0] = 1; - argsStream.forEach(path -> { - String fileName = path.getFileName().toString(); - try { - int idx = Integer.parseInt(fileName.replace("Args$", "")); - startIndex[0] = Math.max(startIndex[0], idx + 1); - } catch(NumberFormatException e) { - ModernFixCachingClassTransformer.LOGGER.warn("Unexpected classname: " + fileName); - } - }); - nextIndexField.setAccessible(true); - nextIndexField.set(argsGen, startIndex[0]); - ModernFixCachingClassTransformer.LOGGER.debug("Patched ArgsClassGenerator to start at index " + startIndex[0]); - } catch(IOException e) { - ModernFixCachingClassTransformer.LOGGER.error("Failed to adjust Mixin synthetic args"); - } - } - - fixedArgsClassCount = true; - } - if(processorField == null) { - processorField = transformer.getClass().getDeclaredField("processor"); - processorField.setAccessible(true); - } - Object processor = processorField.get(transformer); - if(environmentField == null) { - environmentField = processor.getClass().getDeclaredField("currentEnvironment"); - environmentField.setAccessible(true); - } - MixinEnvironment currentEnv = (MixinEnvironment)environmentField.get(processor); - if(currentEnv == null || currentEnv.getPhase() != MixinEnvironment.Phase.DEFAULT) { - return null; /* no hash obtained until mixin is ready */ - } - Field configsField = processor.getClass().getDeclaredField("configs"); - configsField.setAccessible(true); - List configs = (List)configsField.get(processor); - Field mixinsField = Class.forName("org.spongepowered.asm.mixin.transformer.MixinConfig").getDeclaredField("mixins"); - mixinsField.setAccessible(true); - /* getTargetClasses can't be used because it's package-private */ - Field classNamesField = Class.forName("org.spongepowered.asm.mixin.transformer.MixinInfo").getDeclaredField("targetClassNames"); - classNamesField.setAccessible(true); - HashMap> mixinsByClass = new HashMap<>(); - for(Object config : configs) { - List mixins = (List)mixinsField.get(config); - for(IMixinInfo mixin : mixins) { - List targetClassNames = (List)classNamesField.get(mixin); - for(String s : targetClassNames) { - mixinsByClass.computeIfAbsent(s, k -> new ArrayList<>()).add(mixin); - } - } - } - for(ArrayList infos : mixinsByClass.values()) { - infos.sort((info1, info2) -> Comparator.naturalOrder().compare(info1.getClassName(), info2.getClassName())); - } - /* Now go through each class name and hash it */ - HashMap hashesByClassInit = new HashMap<>(); - for(Map.Entry> mixinsForClass : mixinsByClass.entrySet()) { - hasher.reset(); - for(IMixinInfo mixin : mixinsForClass.getValue()) { - URL url = Thread.currentThread().getContextClassLoader().getResource(mixin.getClassName().replace('.', '/') + ".class"); - if(url == null) - throw new IllegalStateException("Can't find " + mixin.getClassName()); - byte[] bytecode; - try { - bytecode = Resources.asByteSource(url).read(); - } catch(IOException e) { - throw new RuntimeException(e); - } - hasher.update(bytecode); - } - hashesByClassInit.put(mixinsForClass.getKey().replace('/', '.'), hasher.digest()); - } - hashesByClass = hashesByClassInit; - } catch(ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - } - return hashesByClass.getOrDefault(className, NO_MIXINS); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index 1c696258..df6e7933 100644 --- a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java +++ b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java @@ -134,29 +134,7 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { @Override public void onLoad(String mixinPackage) { - try { - if(isOptionEnabled("launch.transformer_cache.ModernFixClassTransformer")) { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - Field classTransformerField = TransformingClassLoader.class.getDeclaredField("classTransformer"); - classTransformerField.setAccessible(true); - ClassTransformer t = (ClassTransformer)classTransformerField.get(loader); - TransformStore store = ObfuscationReflectionHelper.getPrivateValue(ClassTransformer.class, t, "transformers"); - LaunchPluginHandler pluginHandler = ObfuscationReflectionHelper.getPrivateValue(ClassTransformer.class, t, "pluginHandler"); - TransformerAuditTrail trail = ObfuscationReflectionHelper.getPrivateValue(ClassTransformer.class, t, "auditTrail"); - injectClassIntoSystemLoader("org.embeddedt.modernfix.util.FileUtil"); - injectClassIntoSystemLoader("org.embeddedt.modernfix.classloading.api.IHashableTransformer"); - injectClassIntoSystemLoader("org.embeddedt.modernfix.classloading.hashers.CoreModTransformerHasher"); - injectClassIntoSystemLoader("org.embeddedt.modernfix.classloading.hashers.MixinTransformerHasher"); - Class newTransformerClass = injectClassIntoSystemLoader("cpw.mods.modlauncher.ModernFixCachingClassTransformer"); - Constructor constructor = newTransformerClass.getConstructor(TransformStore.class, LaunchPluginHandler.class, TransformingClassLoader.class, TransformerAuditTrail.class); - ClassTransformer newTransformer = (ClassTransformer)constructor.newInstance(store, pluginHandler, loader, trail); - classTransformerField.set(loader, newTransformer); - logger.info("Successfully injected caching transformer"); - } - } catch(RuntimeException | ReflectiveOperationException | IOException e) { - logger.error("Failed to make classloading changes", e); - } } @Override 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 23ed4ede..2096da80 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -76,7 +76,6 @@ public class ModernFixEarlyConfig { /* Keep this off if JEI isn't installed to prevent breaking vanilla gameplay */ this.addMixinRule("perf.blast_search_trees", modPresent("jei")); 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); diff --git a/src/main/java/org/embeddedt/modernfix/load/LoadEvents.java b/src/main/java/org/embeddedt/modernfix/load/LoadEvents.java deleted file mode 100644 index a7f6e158..00000000 --- a/src/main/java/org/embeddedt/modernfix/load/LoadEvents.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.embeddedt.modernfix.load; - -import it.unimi.dsi.fastutil.longs.LongIterator; -import net.minecraft.Util; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.LevelLoadingScreen; -import net.minecraft.client.gui.screens.ProgressScreen; -import net.minecraft.client.server.IntegratedServer; -import net.minecraft.network.chat.TranslatableComponent; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerChunkCache; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.TicketType; -import net.minecraft.server.level.progress.ChunkProgressListener; -import net.minecraft.util.Unit; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.ForcedChunksSavedData; -import net.minecraftforge.client.event.GuiOpenEvent; -import net.minecraftforge.common.world.ForgeChunkManager; -import net.minecraftforge.event.entity.player.PlayerEvent; -import net.minecraftforge.eventbus.api.EventPriority; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent; -import net.minecraftforge.fml.server.ServerLifecycleHooks; -import org.embeddedt.modernfix.core.ModernFixMixinPlugin; -import org.embeddedt.modernfix.screen.DeferredLevelLoadingScreen; - -import java.util.concurrent.locks.LockSupport; -import java.util.function.BooleanSupplier; - -/** - * Handles deferring the world load screen. - *

- * TODO: The vanilla check that at least 441 chunks have been loaded does not check whether they are spawn chunks - * or chunks loaded by the player. Consequently it is possible for loading to finish before every spawn chunk has - * been loaded. However the chunk system has at least been warmed up by this point so the remaining chunks load - * reasonably quickly. - */ -public class LoadEvents { - private boolean hasFirstPlayerJoined = false; - - @SubscribeEvent - public void serverWillStart(FMLServerAboutToStartEvent event) { - hasFirstPlayerJoined = false; - } - - @SubscribeEvent(priority = EventPriority.LOWEST) - public void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) { - if(!hasFirstPlayerJoined && ModernFixMixinPlugin.instance.isOptionEnabled("perf.faster_singleplayer_load.ClientEvents")) { - hasFirstPlayerJoined = true; - MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); - if(server instanceof IntegratedServer) { - handleInitialChunkLoad(); - } - } - } - - @SubscribeEvent(priority = EventPriority.LOWEST) - public void onWorldShow(GuiOpenEvent event) { - if(ServerLifecycleHooks.getCurrentServer() instanceof IntegratedServer) { - if(event.getGui() == null && Minecraft.getInstance().level != null && integratedWorldLoadListener != null) { - /* this means the world is about to be displayed, check if 441 initialized */ - ServerChunkCache provider = ServerLifecycleHooks.getCurrentServer().overworld().getChunkSource(); - BooleanSupplier worldLoadDone = () -> provider.getTickingGenerated() >= 441; - if(!worldLoadDone.getAsBoolean()) { - DeferredLevelLoadingScreen newScreen = new DeferredLevelLoadingScreen(Minecraft.getInstance().progressListener.get(), worldLoadDone); - event.setGui(newScreen); - } - } else if(event.getGui() instanceof LevelLoadingScreen && Minecraft.getInstance().level == null && ModernFixMixinPlugin.instance.isOptionEnabled("perf.faster_singleplayer_load.ClientEvents")) { - ProgressScreen loadscreen = new ProgressScreen(); - loadscreen.progressStartNoAbort(new TranslatableComponent("connect.joining")); - event.setGui(loadscreen); - } - } - } - - public static ChunkProgressListener integratedWorldLoadListener; - - private void handleInitialChunkLoad() { - MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); - ServerLevel overworld = server.overworld(); - ServerChunkCache provider = overworld.getChunkSource(); - provider.getLightEngine().setTaskPerBatch(500); - provider.addRegionTicket(TicketType.START, new ChunkPos(overworld.getSharedSpawnPos()), 11, Unit.INSTANCE); - while(provider.getTickingGenerated() < 441) { - server.runAllTasks(); - Thread.yield(); - LockSupport.parkNanos("waiting for world load", 100000L); - server.nextTickTime = Util.getMillis() + 10; - } - for(ServerLevel serverworld1 : server.getAllLevels()) { - ForcedChunksSavedData forcedchunkssavedata = serverworld1.getDataStorage().get(ForcedChunksSavedData::new, "chunks"); - if (forcedchunkssavedata != null) { - LongIterator longiterator = forcedchunkssavedata.getChunks().iterator(); - - while(longiterator.hasNext()) { - long i = longiterator.nextLong(); - ChunkPos chunkpos = new ChunkPos(i); - serverworld1.getChunkSource().updateChunkForced(chunkpos, true); - } - - ForgeChunkManager.reinstatePersistentChunks(serverworld1, forcedchunkssavedata); - } - } - server.runAllTasks(); - server.nextTickTime = Util.getMillis() + 10; - provider.getLightEngine().setTaskPerBatch(5); - if(integratedWorldLoadListener != null) { - integratedWorldLoadListener.stop(); - integratedWorldLoadListener = null; - } - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_singleplayer_load/MinecraftServerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_singleplayer_load/MinecraftServerMixin.java deleted file mode 100644 index ad0b11a0..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_singleplayer_load/MinecraftServerMixin.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.faster_singleplayer_load; - -import net.minecraft.Util; -import net.minecraft.client.server.IntegratedServer; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerChunkCache; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.progress.ChunkProgressListener; -import org.embeddedt.modernfix.ModernFixClient; -import org.embeddedt.modernfix.load.LoadEvents; -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; - -@Mixin(MinecraftServer.class) -public abstract class MinecraftServerMixin { - @Shadow protected long nextTickTime; - - @Shadow public abstract ServerLevel overworld(); - - @Shadow protected abstract void updateMobSpawningFlags(); - - /** - * @author embeddedt - * @reason defer the 441 chunk load until *after* join game packets are sent to the client, in order to allow - * mods that process advancements, etc. to work on that at the same time - */ - @Inject(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;getChunkSource()Lnet/minecraft/server/level/ServerChunkCache;", ordinal = 0), cancellable = true) - private void skipInitialChunkLoad(ChunkProgressListener arg, CallbackInfo ci) { - if(((Object)this) instanceof IntegratedServer) { - ci.cancel(); - LoadEvents.integratedWorldLoadListener = arg; - this.nextTickTime = Util.getMillis(); - this.overworld().getChunkSource().getLightEngine().setTaskPerBatch(5); - this.updateMobSpawningFlags(); - } - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/AndConditionMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/AndConditionMixin.java deleted file mode 100644 index 94283f0a..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/AndConditionMixin.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates; - -import com.google.common.collect.Streams; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.client.renderer.block.model.multipart.AndCondition; -import net.minecraft.client.renderer.block.model.multipart.Condition; -import net.minecraft.world.level.block.state.StateDefinition; -import org.embeddedt.modernfix.predicate.StatePropertyPredicateHelper; -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.function.Predicate; -import java.util.stream.Collectors; - -@Mixin(AndCondition.class) -public class AndConditionMixin { - @Shadow @Final private Iterable conditions; - - /** - * @author JellySquid - * @reason Flatten predicates - */ - @Overwrite - public Predicate getPredicate(StateDefinition stateManager) { - return StatePropertyPredicateHelper.allMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> { - return multipartModelSelector.getPredicate(stateManager); - }).collect(Collectors.toList())); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/OrConditionMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/OrConditionMixin.java deleted file mode 100644 index f336a997..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/OrConditionMixin.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates; - -import com.google.common.collect.Streams; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.client.renderer.block.model.multipart.Condition; -import net.minecraft.client.renderer.block.model.multipart.OrCondition; -import net.minecraft.world.level.block.state.StateDefinition; -import org.embeddedt.modernfix.predicate.StatePropertyPredicateHelper; -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.function.Predicate; -import java.util.stream.Collectors; - -@Mixin(OrCondition.class) -public class OrConditionMixin { - @Shadow @Final private Iterable conditions; - - /** - * @author JellySquid - * @reason Flatten predicates - */ - @Overwrite - public Predicate getPredicate(StateDefinition stateManager) { - return StatePropertyPredicateHelper.anyMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> { - return multipartModelSelector.getPredicate(stateManager); - }).collect(Collectors.toList())); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/PropertyValueConditionMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/PropertyValueConditionMixin.java deleted file mode 100644 index 80c8991d..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/PropertyValueConditionMixin.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates; - -import com.google.common.base.Splitter; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.client.renderer.block.model.multipart.KeyValueCondition; -import net.minecraft.world.level.block.state.properties.Property; -import net.minecraft.world.level.block.state.StateDefinition; -import org.embeddedt.modernfix.predicate.single.SingleMatchAny; -import org.embeddedt.modernfix.predicate.single.SingleMatchOne; -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.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -@Mixin(KeyValueCondition.class) -public class PropertyValueConditionMixin { - @Shadow @Final private String key; - - @Shadow @Final private String value; - - @Shadow @Final private static Splitter PIPE_SPLITTER; - - /** - * @author JellySquid - * @reason De-duplication - */ - @Overwrite - public Predicate getPredicate(StateDefinition stateManager) { - Property property = stateManager.getProperty(this.key); - - if (property == null) { - throw new RuntimeException(String.format("Unknown property '%s' on '%s'", this.key, stateManager.getOwner().toString())); - } - - String valueString = this.value; - boolean negate = !valueString.isEmpty() && valueString.charAt(0) == '!'; - - if (negate) { - valueString = valueString.substring(1); - } - - List split = PIPE_SPLITTER.splitToList(valueString); - - if (split.isEmpty()) { - throw new RuntimeException(String.format("Empty value '%s' for property '%s' on '%s'", this.value, this.key, stateManager.getOwner().toString())); - } - - Predicate predicate; - - if (split.size() == 1) { - predicate = new SingleMatchOne(property, this.getPropertyValue(stateManager, property, valueString)); - } else { - predicate = SingleMatchAny.create(property, split.stream() - .map(str -> this.getPropertyValue(stateManager, property, str)) - .collect(Collectors.toList())); - } - - return negate ? predicate.negate() : predicate; - } - - private Object getPropertyValue(StateDefinition stateFactory, Property property, String valueString) { - Object value = property.getValue(valueString) - .orElse(null); - - if (value == null) { - throw new RuntimeException(String.format("Unknown value '%s' for property '%s' on '%s' in '%s'", - valueString, this.key, stateFactory.getOwner().toString(), this.value)); - } - - return value; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/preload_block_classes/GameDataMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/preload_block_classes/GameDataMixin.java deleted file mode 100644 index 5f519ffc..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/preload_block_classes/GameDataMixin.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.preload_block_classes; - -import net.minecraftforge.fml.ModLoadingStage; -import net.minecraftforge.registries.GameData; -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.CallbackInfoReturnable; - -import java.util.stream.Stream; - -import org.embeddedt.modernfix.util.BlockClassPreloader; - -@Mixin(GameData.class) -public class GameDataMixin { - @Inject(method = "generateRegistryEvents", at = @At("RETURN"), remap = false) - private static void preloadBlockClasses(CallbackInfoReturnable>> cir) { - BlockClassPreloader.preloadClasses(); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/AllPredicate.java b/src/main/java/org/embeddedt/modernfix/predicate/AllPredicate.java deleted file mode 100644 index e741e58a..00000000 --- a/src/main/java/org/embeddedt/modernfix/predicate/AllPredicate.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.embeddedt.modernfix.predicate; - -import java.util.function.Predicate; - -public class AllPredicate implements Predicate { - private final Predicate[] predicates; - - public AllPredicate(Predicate[] predicates) { - this.predicates = predicates; - } - - @Override - public boolean test(T t) { - for (Predicate predicate : this.predicates) { - if (!predicate.test(t)) { - return false; - } - } - - return true; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/AnyPredicate.java b/src/main/java/org/embeddedt/modernfix/predicate/AnyPredicate.java deleted file mode 100644 index 7c04f5fb..00000000 --- a/src/main/java/org/embeddedt/modernfix/predicate/AnyPredicate.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.embeddedt.modernfix.predicate; - -import java.util.function.Predicate; - -public class AnyPredicate implements Predicate { - private final Predicate[] predicates; - - public AnyPredicate(Predicate[] predicates) { - this.predicates = predicates; - } - - @Override - public boolean test(T t) { - for (Predicate predicate : this.predicates) { - if (predicate.test(t)) { - return true; - } - } - - return false; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/CachedModelPredicate.java b/src/main/java/org/embeddedt/modernfix/predicate/CachedModelPredicate.java deleted file mode 100644 index 8c5a4566..00000000 --- a/src/main/java/org/embeddedt/modernfix/predicate/CachedModelPredicate.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.embeddedt.modernfix.predicate; - -/** - * Calculates the - */ -public class CachedModelPredicate { -} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/StatePropertyPredicateHelper.java b/src/main/java/org/embeddedt/modernfix/predicate/StatePropertyPredicateHelper.java deleted file mode 100644 index 8125e307..00000000 --- a/src/main/java/org/embeddedt/modernfix/predicate/StatePropertyPredicateHelper.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.embeddedt.modernfix.predicate; - -import net.minecraft.world.level.block.state.BlockState; -import org.embeddedt.modernfix.predicate.all.AllMatchOneBoolean; -import org.embeddedt.modernfix.predicate.all.AllMatchOneObject; -import org.embeddedt.modernfix.predicate.any.AllMatchAnyObject; -import org.embeddedt.modernfix.predicate.single.SingleMatchAny; -import org.embeddedt.modernfix.predicate.single.SingleMatchOne; - -import java.util.List; -import java.util.function.Predicate; - -public class StatePropertyPredicateHelper { - @SuppressWarnings("unchecked") - public static Predicate allMatch(List> predicates) { - if (SingleMatchOne.areOfType(predicates)) { - if (SingleMatchOne.valuesMatchType(predicates, Boolean.class)) { - return new AllMatchOneBoolean(predicates); - } - - return new AllMatchOneObject(predicates); - } else if (SingleMatchAny.areOfType(predicates)) { - return new AllMatchAnyObject(predicates); - } - - return new AllPredicate<>(predicates.toArray(new Predicate[0])); - } - - @SuppressWarnings("unchecked") - public static Predicate anyMatch(List> predicates) { - return new AnyPredicate<>(predicates.toArray(new Predicate[0])); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneBoolean.java b/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneBoolean.java deleted file mode 100644 index 06984eb1..00000000 --- a/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneBoolean.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.embeddedt.modernfix.predicate.all; - -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; -import org.embeddedt.modernfix.predicate.single.SingleMatchOne; - -import java.util.List; -import java.util.function.Predicate; - -public class AllMatchOneBoolean implements Predicate { - private final Property[] properties; - private final boolean[] values; - - public AllMatchOneBoolean(List> list) { - int size = list.size(); - - this.properties = new Property[size]; - this.values = new boolean[size]; - - for (int i = 0; i < size; i++) { - SingleMatchOne predicate = (SingleMatchOne) list.get(i); - - this.properties[i] = predicate.property; - this.values[i] = (boolean) predicate.value; - } - } - - public static boolean canReplace(List> list) { - return list.stream() - .allMatch(p -> { - return p instanceof SingleMatchOne && ((SingleMatchOne) p).value instanceof Boolean; - }); - } - - @Override - public boolean test(BlockState blockState) { - for (int i = 0; i < this.properties.length; i++) { - Boolean value = (Boolean) blockState.getValue(this.properties[i]); - - if (value != this.values[i]) { - return false; - } - } - - return true; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneObject.java b/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneObject.java deleted file mode 100644 index 5a90591b..00000000 --- a/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneObject.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.embeddedt.modernfix.predicate.all; - -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; -import org.embeddedt.modernfix.predicate.single.SingleMatchOne; - -import java.util.List; -import java.util.function.Predicate; - -public class AllMatchOneObject implements Predicate { - private final Property[] properties; - private final Object[] values; - - public AllMatchOneObject(List> list) { - int size = list.size(); - - this.properties = new Property[size]; - this.values = new Object[size]; - - for (int i = 0; i < size; i++) { - SingleMatchOne predicate = (SingleMatchOne) list.get(i); - - this.properties[i] = predicate.property; - this.values[i] = predicate.value; - } - } - - @Override - public boolean test(BlockState blockState) { - for (int i = 0; i < this.properties.length; i++) { - if (blockState.getValue(this.properties[i]) != this.values[i]) { - return false; - } - } - - return true; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/any/AllMatchAnyObject.java b/src/main/java/org/embeddedt/modernfix/predicate/any/AllMatchAnyObject.java deleted file mode 100644 index f9585376..00000000 --- a/src/main/java/org/embeddedt/modernfix/predicate/any/AllMatchAnyObject.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.embeddedt.modernfix.predicate.any; - -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; -import org.apache.commons.lang3.ArrayUtils; -import org.embeddedt.modernfix.predicate.single.SingleMatchAny; - -import java.util.List; -import java.util.function.Predicate; - -public class AllMatchAnyObject implements Predicate { - private final Property[] properties; - private final Object[][] values; - - public AllMatchAnyObject(List> list) { - int size = list.size(); - - this.properties = new Property[size]; - this.values = new Object[size][]; - - for (int i = 0; i < size; i++) { - SingleMatchAny predicate = (SingleMatchAny) list.get(i); - - this.properties[i] = predicate.property; - this.values[i] = predicate.values; - } - } - - @Override - public boolean test(BlockState blockState) { - for (int i = 0; i < this.properties.length; i++) { - if (!ArrayUtils.contains(this.values[i], blockState.getValue(this.properties[i]))) { - return false; - } - } - - return true; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchAny.java b/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchAny.java deleted file mode 100644 index 1c7906e9..00000000 --- a/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchAny.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.embeddedt.modernfix.predicate.single; - -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; -import org.apache.commons.lang3.ArrayUtils; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.function.Predicate; - -public class SingleMatchAny implements Predicate { - public static final ObjectOpenHashSet PREDICATES = new ObjectOpenHashSet<>(); - - public final Property property; - public final Object[] values; - - private SingleMatchAny(Property property, List values) { - this.property = property; - this.values = values.toArray(); - } - - public static SingleMatchAny create(Property property, List values) { - return PREDICATES.addOrGet(new SingleMatchAny(property, values)); - } - - public static boolean areOfType(List> predicates) { - return predicates.stream() - .allMatch(p -> { - return p instanceof SingleMatchAny; - }); - } - - public static boolean valuesMatchType(List> predicates, Class type) { - return predicates.stream() - .allMatch(p -> { - return p instanceof SingleMatchAny && - Arrays.stream(((SingleMatchAny) p).values).allMatch(t -> type.isInstance(p)); - }); - } - - @Override - public boolean test(BlockState blockState) { - return ArrayUtils.contains(this.values, blockState.getValue(this.property)); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - SingleMatchAny that = (SingleMatchAny) o; - return Objects.equals(property, that.property) && - Arrays.equals(values, that.values); - } - - @Override - public int hashCode() { - int result = Objects.hash(property); - result = 31 * result + Arrays.hashCode(values); - return result; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchOne.java b/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchOne.java deleted file mode 100644 index e44e0515..00000000 --- a/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchOne.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.embeddedt.modernfix.predicate.single; - -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; - -import java.util.List; -import java.util.function.Predicate; - -public class SingleMatchOne implements Predicate { - public final Property property; - public final Object value; - - public SingleMatchOne(Property property, Object value) { - this.property = property; - this.value = value; - } - - public static boolean areOfType(List> predicates) { - return predicates.stream() - .allMatch(p -> { - return p instanceof SingleMatchOne; - }); - } - - public static boolean valuesMatchType(List> predicates, Class type) { - return predicates.stream() - .allMatch(p -> { - return p instanceof SingleMatchOne && type.isInstance(((SingleMatchOne) p).value); - }); - } - - @Override - public boolean test(BlockState blockState) { - return blockState.getValue(this.property) == this.value; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/registry/DeferredRegisterBaker.java b/src/main/java/org/embeddedt/modernfix/registry/DeferredRegisterBaker.java deleted file mode 100644 index be78ace2..00000000 --- a/src/main/java/org/embeddedt/modernfix/registry/DeferredRegisterBaker.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.embeddedt.modernfix.registry; - -import com.google.common.base.Stopwatch; -import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.fml.ModWorkManager; -import org.embeddedt.modernfix.ModernFix; -import org.embeddedt.modernfix.util.AsyncStopwatch; -import org.embeddedt.modernfix.util.CachedSupplier; -import org.embeddedt.modernfix.util.OrderedParallelModDispatcher; - -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -public class DeferredRegisterBaker { - private static final HashMap>>> supplierMap = new HashMap<>(); - public static Supplier cacheForComputationLater(ResourceLocation registry, String modid, Supplier supplier) { - synchronized (supplierMap) { - HashMap>> registrySupplierMap = supplierMap.computeIfAbsent(registry, reg -> new HashMap<>()); - List> modSupplierList = registrySupplierMap.computeIfAbsent(modid, id -> new ArrayList<>()); - CachedSupplier cacher = new CachedSupplier<>(supplier); - modSupplierList.add(cacher); - return cacher; - } - } - - public static void bakeSuppliers(ResourceLocation registry) { - synchronized (supplierMap) { - Set modErrors = Collections.synchronizedSet(new HashSet<>()); - HashMap>> registrySupplierMap = supplierMap.get(registry); - if(registrySupplierMap == null) - return; - ModernFix.LOGGER.info("Caching suppliers for " + registry); - Stopwatch realtimeStopwatch = Stopwatch.createStarted(); - AsyncStopwatch cpuStopwatch = new AsyncStopwatch(); - OrderedParallelModDispatcher.dispatchBlocking(ModWorkManager.parallelExecutor(), modId -> { - List> suppliersToCompute = registrySupplierMap.get(modId); - if (suppliersToCompute == null || suppliersToCompute.size() == 0) { - return; - } - cpuStopwatch.startMeasuringAsync(); - for (CachedSupplier supplier : suppliersToCompute) { - try { - supplier.compute(); - } catch(RuntimeException e) { - ModernFix.LOGGER.debug("Exception encountered while caching supplier", e); - modErrors.add(modId); - } - } - cpuStopwatch.stopMeasuringAsync(); - }); - realtimeStopwatch.stop(); - if(modErrors.size() > 0) - ModernFix.LOGGER.warn("The following mods had errors while caching " + registry + " suppliers (this is likely safe): [" + String.join(", ", modErrors) + "]"); - ModernFix.LOGGER.info("CPU time spent constructing " + registry + " suppliers: " + cpuStopwatch.getCpuTime()/1000f + " seconds"); - ModernFix.LOGGER.info("Real time spent constructing " + registry + " suppliers: " + realtimeStopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds"); - } - } -} diff --git a/src/main/java/org/embeddedt/modernfix/screen/DeferredLevelLoadingScreen.java b/src/main/java/org/embeddedt/modernfix/screen/DeferredLevelLoadingScreen.java deleted file mode 100644 index cd49687a..00000000 --- a/src/main/java/org/embeddedt/modernfix/screen/DeferredLevelLoadingScreen.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.embeddedt.modernfix.screen; - -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.gui.screens.LevelLoadingScreen; -import net.minecraft.server.level.progress.StoringChunkProgressListener; - -import java.util.function.BooleanSupplier; - -public class DeferredLevelLoadingScreen extends LevelLoadingScreen { - private final BooleanSupplier worldLoadFinished; - public DeferredLevelLoadingScreen(StoringChunkProgressListener arg, BooleanSupplier worldLoadFinished) { - super(arg); - this.worldLoadFinished = worldLoadFinished; - } - - @Override - public void tick() { - super.tick(); - if(this.worldLoadFinished.getAsBoolean()) - this.onClose(); - } - - @Override - public void renderBackground(PoseStack matrixStack, int vOffset) { - renderDirtBackground(vOffset); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/util/AsyncStopwatch.java b/src/main/java/org/embeddedt/modernfix/util/AsyncStopwatch.java deleted file mode 100644 index a45db852..00000000 --- a/src/main/java/org/embeddedt/modernfix/util/AsyncStopwatch.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.embeddedt.modernfix.util; - -import com.google.common.base.Stopwatch; -import org.embeddedt.modernfix.ModernFix; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -public class AsyncStopwatch { - private final AtomicLong cpuTimeMs = new AtomicLong(0); - private final ThreadLocal threadStopwatch = ThreadLocal.withInitial(Stopwatch::createUnstarted); - - public void startMeasuringAsync() { - threadStopwatch.get().start(); - } - - public void stopMeasuringAsync() { - Stopwatch watch = threadStopwatch.get(); - watch.stop(); - long elapsed = watch.elapsed(TimeUnit.MILLISECONDS); - cpuTimeMs.addAndGet(elapsed); - watch.reset(); - } - - public void ensureStoppedAsync() { - Stopwatch watch = threadStopwatch.get(); - if(watch.isRunning()) - stopMeasuringAsync(); - } - - public long getCpuTime() { - return cpuTimeMs.get(); - } - - public static void measureAndLogSerialRunningTime(String label, Runnable runnable) { - ModernFix.LOGGER.info(label + "..."); - Stopwatch stopwatch = Stopwatch.createStarted(); - try { - runnable.run(); - ModernFix.LOGGER.info(label + " took " + stopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds"); - } finally { - stopwatch.stop(); - } - } -} diff --git a/src/main/java/org/embeddedt/modernfix/util/BlockClassPreloader.java b/src/main/java/org/embeddedt/modernfix/util/BlockClassPreloader.java deleted file mode 100644 index 140de108..00000000 --- a/src/main/java/org/embeddedt/modernfix/util/BlockClassPreloader.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.embeddedt.modernfix.util; - -import com.google.common.base.Stopwatch; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.Block; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.ModWorkManager; -import net.minecraftforge.fml.loading.moddiscovery.ModFile; -import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; -import net.minecraftforge.forgespi.language.ModFileScanData; -import org.embeddedt.modernfix.ModernFix; -import org.objectweb.asm.Type; - -import java.lang.reflect.Field; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -public class BlockClassPreloader { - public static void preloadClasses() { - Stopwatch stopwatch = Stopwatch.createStarted(); - ModernFix.LOGGER.warn("Preparing to preload classes..."); - HashMap isABlockClass = new HashMap<>(); - isABlockClass.put(Type.getType(BlockBehaviour.class), true); - isABlockClass.put(Type.getType(Block.class), true); - Field selfField, parentField; - List futures = new ArrayList<>(); - try { - selfField = ModFileScanData.ClassData.class.getDeclaredField("clazz"); - selfField.setAccessible(true); - parentField = ModFileScanData.ClassData.class.getDeclaredField("parent"); - parentField.setAccessible(true); - List currentCandidates = ModList.get().getModFiles().stream() - .map(ModFileInfo::getFile) - .map(ModFile::getScanResult) - .flatMap(data -> data.getClasses().stream()) - .collect(Collectors.toList()); - HashSet blockClasses = new HashSet<>(); - blockClasses.add(Type.getType(BlockBehaviour.class)); - HashSet nonBlockClasses = new HashSet<>(); - int previousSize = -1; - nonBlockClasses.add(Type.getType(Object.class)); - currentCandidates.removeIf(clz -> { - Type self; - try { - self = (Type)selfField.get(clz); - } catch(ReflectiveOperationException e) { - throw new RuntimeException(e); - } - return (nonBlockClasses.contains(self) || blockClasses.contains(self)); - }); - while(blockClasses.size() > previousSize && currentCandidates.size() > 0) { - previousSize = blockClasses.size(); - currentCandidates.removeIf(clz -> { - Type parent, self; - try { - parent = (Type)parentField.get(clz); - self = (Type)selfField.get(clz); - } catch(ReflectiveOperationException e) { - throw new RuntimeException(e); - } - if(nonBlockClasses.contains(parent)) { - nonBlockClasses.add(self); - return true; - } else if(blockClasses.contains(parent)) { - blockClasses.add(self); - futures.add(CompletableFuture.runAsync(() -> { - if(self.getClassName().toLowerCase(Locale.ROOT).contains("mixin")) - return; - try { - Class.forName(self.getClassName()); - } catch(Throwable e) { - ModernFix.LOGGER.warn("Couldn't load " + self.getClassName(), e); - } - }, ModWorkManager.parallelExecutor())); - return true; - } else - return false; - }); - } - futures.forEach(CompletableFuture::join); - } catch(ReflectiveOperationException e) { - throw new RuntimeException(e); - } finally { - ModernFix.LOGGER.warn("Preloading classes took " + stopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds"); - stopwatch.stop(); - } - } -} diff --git a/src/main/java/org/embeddedt/modernfix/util/CachedSupplier.java b/src/main/java/org/embeddedt/modernfix/util/CachedSupplier.java deleted file mode 100644 index 50b2303d..00000000 --- a/src/main/java/org/embeddedt/modernfix/util/CachedSupplier.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.embeddedt.modernfix.util; - -import java.util.function.Supplier; - -/** - * An implementation of Supplier that allows separating the time at which the value is computed from when it is - * retrieved. - */ -public class CachedSupplier implements Supplier { - private T value = null; - - private boolean hasBeenComputed; - private final Supplier delegate; - - public CachedSupplier(Supplier delegate) { - this.delegate = delegate; - } - - public synchronized void compute() { - this.value = this.delegate.get(); - this.hasBeenComputed = true; - } - - @Override - public synchronized T get() { - if(this.hasBeenComputed) { - this.hasBeenComputed = false; - return this.value; - } else { - return this.delegate.get(); - } - } -} diff --git a/src/main/java/org/embeddedt/modernfix/util/JEIUtil.java b/src/main/java/org/embeddedt/modernfix/util/JEIUtil.java index 59e6b244..ac90c3d2 100644 --- a/src/main/java/org/embeddedt/modernfix/util/JEIUtil.java +++ b/src/main/java/org/embeddedt/modernfix/util/JEIUtil.java @@ -10,7 +10,6 @@ import net.minecraft.network.chat.TranslatableComponent; import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.SubscribeEvent; -import org.embeddedt.modernfix.screen.DeferredLevelLoadingScreen; import java.util.function.Supplier; @@ -25,7 +24,7 @@ public class JEIUtil { @SubscribeEvent public static void renderLoad(GuiScreenEvent.DrawScreenEvent.Post event) { /* Don't show the JEI indicator on the level loading screen, that looks weird */ - if(isLoading.get() && !(event.getGui() instanceof DeferredLevelLoadingScreen)) { + if(isLoading.get()) { Gui.drawString(new PoseStack(), Minecraft.getInstance().font, new TranslatableComponent("modernfix.jei_load"), 0, 0, 0xffffff); } } diff --git a/src/main/java/org/embeddedt/modernfix/util/OrderedParallelModDispatcher.java b/src/main/java/org/embeddedt/modernfix/util/OrderedParallelModDispatcher.java deleted file mode 100644 index a08754fe..00000000 --- a/src/main/java/org/embeddedt/modernfix/util/OrderedParallelModDispatcher.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.embeddedt.modernfix.util; - -import com.google.common.base.Preconditions; -import com.google.common.base.Stopwatch; -import net.minecraftforge.fml.ModContainer; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.ModLoadingContext; -import net.minecraftforge.fml.ModWorkManager; -import net.minecraftforge.fml.common.ObfuscationReflectionHelper; -import net.minecraftforge.fml.loading.moddiscovery.ModInfo; -import net.minecraftforge.forgespi.language.IModInfo; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.MarkerManager; -import org.embeddedt.modernfix.ModernFix; - -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -/** - * Iterates over all mods in the game, parallelizing where possible while preserving dependency ordering. - * - * Can also be given a list of mods to skip. - */ -public class OrderedParallelModDispatcher { - private static final Marker DISPATCHER = MarkerManager.getMarker("OrderedParallelModDispatcher"); - public static void dispatchBlocking(Executor executor, Consumer task, Collection modIDsToFilter) { - Set finishedMods = Collections.synchronizedSet(new HashSet<>(modIDsToFilter)); - HashMap> submittedFutures = new HashMap<>(); - Semaphore jobWaitingSemaphore = new Semaphore(0); - ArrayList remainingModList = new ArrayList<>(ModList.get().getMods()); - while(remainingModList.size() > 0) { - remainingModList.removeIf(modInfo -> { - if(finishedMods.contains(modInfo.getModId())) - return true; - List missingDependencies = modInfo.getDependencies().stream() - .filter(IModInfo.ModVersion::isMandatory) - .map(IModInfo.ModVersion::getModId) - .filter(modId -> !finishedMods.contains(modId)) - .collect(Collectors.toList()); - if(missingDependencies.size() > 0) { - ModernFix.LOGGER.debug(DISPATCHER, "Cannot process " + modInfo.getModId() + ", as it is waiting on mods: [" + String.join(", ", missingDependencies) + "]"); - return false; - } - Optional modContainerOpt = ModList.get().getModContainerById(modInfo.getModId()); - if(!modContainerOpt.isPresent()) - throw new IllegalStateException("Can't find mod container"); - ModContainer container = modContainerOpt.get(); - ModernFix.LOGGER.debug(DISPATCHER, "Submitting job for " + modInfo.getModId()); - submittedFutures.put(modInfo.getModId(), CompletableFuture.runAsync(() -> { - Supplier contextExtension = ObfuscationReflectionHelper.getPrivateValue(ModContainer.class, container, "contextExtension"); - ModLoadingContext.get().setActiveContainer(container, contextExtension.get()); - try { - task.accept(modInfo.getModId()); - } catch(RuntimeException e) { - e.printStackTrace(); - } - /* - * We cannot rely on the main thread to correctly mark us as done, as it might start running - * before the future is marked as complete. So we add the mod to the finished set ourselves. - */ - finishedMods.add(modInfo.getModId()); - jobWaitingSemaphore.release(); - //ModLoadingContext.get().setActiveContainer(null, null); - }, executor)); - return true; - }); - Preconditions.checkState(submittedFutures.size() > 0, "The semaphore will block forever!"); - ModernFix.LOGGER.debug(DISPATCHER, "Waiting for one of [" + String.join(", ", submittedFutures.keySet()) + "] to finish..."); - try { - jobWaitingSemaphore.acquire(); - } catch(InterruptedException e) { - throw new RuntimeException("Unexpected interruption", e); - } - submittedFutures.entrySet().removeIf(entry -> { - if(entry.getValue().isDone()) { - ModernFix.LOGGER.debug(DISPATCHER, "Job finished for " + entry.getKey()); - return true; - } - return false; - }); - } - submittedFutures.values().forEach(CompletableFuture::join); - } - - public static void dispatchBlocking(Executor executor, Consumer task) { - dispatchBlocking(executor, task, Collections.emptyList()); - } -} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 776cbbbf..5be112b9 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -27,7 +27,6 @@ "perf.skip_first_datapack_reload.SaveFormatAccessor", "perf.boost_worker_count.UtilMixin", "perf.thread_priorities.UtilMixin", - "perf.preload_block_classes.GameDataMixin", "perf.reduce_blockstate_cache_rebuilds.GameDataMixin", "perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin", "perf.reduce_blockstate_cache_rebuilds.BlocksMixin", @@ -104,9 +103,6 @@ "perf.thread_priorities.IntegratedServerMixin", "safety.BlockColorsMixin", "safety.ItemColorsMixin", - "perf.flatten_model_predicates.AndConditionMixin", - "perf.flatten_model_predicates.OrConditionMixin", - "perf.flatten_model_predicates.PropertyValueConditionMixin", "perf.blast_search_trees.MinecraftMixin", "perf.blast_search_trees.IngredientFilterInvoker", "perf.cache_model_materials.VanillaModelMixin", @@ -121,7 +117,6 @@ "perf.use_integrated_resources.jepb.PiglinBarteringRecipeBuilderMixin", "perf.jeresources_startup.VillagerEntryMixin", "bugfix.mc218112.SynchedEntityDataMixin_Client", - "perf.faster_singleplayer_load.MinecraftServerMixin", "devenv.MinecraftMixin", "devenv.NarratorMixin" ], From 347a61abb62105d7539086964e2d55302df69c24 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 24 Apr 2023 19:23:17 -0400 Subject: [PATCH 02/36] Add changelog generation --- .gitignore | 3 +++ build.gradle | 21 ++++++++++++++++++++- gradle/changelog.mustache | 3 +++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 gradle/changelog.mustache diff --git a/.gitignore b/.gitignore index a3951e7f..c4d1b6c5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ libs media classes/ +# Changelog +CHANGELOG.md + # Created by https://www.gitignore.io/api/gradle,intellij,eclipse,windows,osx,linux ### Gradle ### diff --git a/build.gradle b/build.gradle index 807e3777..32a683c6 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ plugins { id "maven-publish" id 'com.matthewprenger.cursegradle' version '1.4.0' id 'com.palantir.git-version' version '1.0.0' + id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.79.0' } sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8 @@ -120,6 +121,22 @@ tasks.withType(JavaCompile) { */ } +task generateChangelog(type: se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask) { + def details = versionDetails(); + if(details.commitDistance > 0) { + fromRef = details.lastTag; + } else { + def secondLastTagCmd = "git describe --abbrev=0 " + details.lastTag + "^" + def secondLastTag = secondLastTagCmd.execute().text.trim() + fromRef = secondLastTag; + } + + file = new File("CHANGELOG.md"); + def otherTemplateContent = new File('gradle/changelog.mustache').getText('UTF-8'); + templateContent = "## Changes since " + fromRef + "\n" + otherTemplateContent; + toCommit = "HEAD"; +} + java { // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task // if it is present. @@ -165,7 +182,7 @@ curseforge { apiKey = System.getenv("CURSEFORGE_TOKEN") project { id = "790626" - changelog = '[Changelog is not currently available]' + changelog = file('./CHANGELOG.md') changelogType = "markdown" releaseType = "release" addGameVersion "Forge" @@ -174,3 +191,5 @@ curseforge { } } } + +tasks.curseforge.dependsOn(":generateChangelog") diff --git a/gradle/changelog.mustache b/gradle/changelog.mustache new file mode 100644 index 00000000..5f0337f1 --- /dev/null +++ b/gradle/changelog.mustache @@ -0,0 +1,3 @@ +{{#commits}} + * [{{{messageTitle}}}](https://github.com/embeddedt/ModernFix/commit/{{hashFull}}) - {{{authorName}}} +{{/commits}} \ No newline at end of file From f0323d409a3740b5835d701077e36224863a761c Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 24 Apr 2023 19:34:27 -0400 Subject: [PATCH 03/36] Add Modrinth uploading logic --- build.gradle | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/build.gradle b/build.gradle index 32a683c6..a32eb100 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,7 @@ plugins { id 'com.matthewprenger.cursegradle' version '1.4.0' id 'com.palantir.git-version' version '1.0.0' id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.79.0' + id "com.modrinth.minotaur" version "2.+" } sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8 @@ -192,4 +193,24 @@ curseforge { } } +modrinth { + token = System.getenv("MODRINTH_TOKEN") + projectId = "modernfix" // This can be the project ID or the slug. Either will work! + versionType = "release" // This is the default -- can also be `beta` or `alpha` + uploadFile = remapJar + gameVersions = [minecraft_version] + loaders = ["forge"] + File changelogFile = new File("./CHANGELOG.md") + if (changelogFile.exists()) + changelog = changelogFile.getText('UTF-8') + else + changelog = "No changelog was provided." +} + tasks.curseforge.dependsOn(":generateChangelog") +tasks.modrinth.dependsOn(":generateChangelog") + +task publishToModSites { + publishToModSites.dependsOn modrinth + publishToModSites.dependsOn curseforge +} \ No newline at end of file From 3922e54b11b9a6e5e024f79d71cdc1c24557ac5a Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 24 Apr 2023 19:53:58 -0400 Subject: [PATCH 04/36] Tweak changelog gen logic --- build.gradle | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index a32eb100..c4d6480f 100644 --- a/build.gradle +++ b/build.gradle @@ -200,17 +200,24 @@ modrinth { uploadFile = remapJar gameVersions = [minecraft_version] loaders = ["forge"] - File changelogFile = new File("./CHANGELOG.md") - if (changelogFile.exists()) - changelog = changelogFile.getText('UTF-8') - else - changelog = "No changelog was provided." + changelog.set(provider { file("./CHANGELOG.md").getText('UTF-8') }) } +tasks.register('checkCleanTag') { + doLast { + def details = versionDetails() + if (!details.isCleanTag || versionDetails().commitDistance != 0) { + throw new GradleException('Not a clean tree.') + } + } +} + +tasks.curseforge.dependsOn(":checkCleanTag") tasks.curseforge.dependsOn(":generateChangelog") +tasks.modrinth.dependsOn(":checkCleanTag") tasks.modrinth.dependsOn(":generateChangelog") -task publishToModSites { - publishToModSites.dependsOn modrinth - publishToModSites.dependsOn curseforge +tasks.register('publishToModSites') { + publishToModSites.dependsOn(tasks.modrinth) + publishToModSites.dependsOn(tasks.curseforge) } \ No newline at end of file From 35c0c760f006bda6d165b04985862569a34ec25b Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 24 Apr 2023 19:59:02 -0400 Subject: [PATCH 05/36] Exclude merge commits from changelog --- gradle/changelog.mustache | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gradle/changelog.mustache b/gradle/changelog.mustache index 5f0337f1..b3175640 100644 --- a/gradle/changelog.mustache +++ b/gradle/changelog.mustache @@ -1,3 +1,5 @@ {{#commits}} +{{#ifMatches messageTitle "^(?!Merge).*"}} * [{{{messageTitle}}}](https://github.com/embeddedt/ModernFix/commit/{{hashFull}}) - {{{authorName}}} +{{/ifMatches}} {{/commits}} \ No newline at end of file From fdd1174d8ba5cf56a0b9e493df8b82cf3abdeae8 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 24 Apr 2023 20:02:20 -0400 Subject: [PATCH 06/36] Mark 1.19.2 as release --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 403c914c..e1a1605f 100644 --- a/build.gradle +++ b/build.gradle @@ -178,7 +178,7 @@ curseforge { id = "790626" changelog = file('./CHANGELOG.md') changelogType = "markdown" - releaseType = "beta" + releaseType = "release" addGameVersion "Forge" addGameVersion minecraft_version mainArtifact remapJar @@ -213,4 +213,4 @@ tasks.modrinth.dependsOn(":generateChangelog") tasks.register('publishToModSites') { publishToModSites.dependsOn(tasks.modrinth) publishToModSites.dependsOn(tasks.curseforge) -} \ No newline at end of file +} From 16269ee144ebb31dea9cf304ecb23f344d7ac71c Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 09:51:04 -0400 Subject: [PATCH 07/36] Tweak dynamic resources injection point to prevent Connectedness crash --- .../mixin/perf/dynamic_resources/ModelBakeryMixin.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 index 7b1d6769..a41640fa 100644 --- 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 @@ -39,6 +39,7 @@ import org.apache.commons.lang3.tuple.Triple; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.duck.IExtendedModelBakery; import org.embeddedt.modernfix.dynamicresources.*; +import org.objectweb.asm.Opcodes; import org.slf4j.Logger; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; @@ -101,15 +102,16 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { @Shadow public abstract UnbakedModel getModel(ResourceLocation modelLocation); + @Shadow @Final @Mutable private BlockColors blockColors; private Cache, BakedModel> loadedBakedModels; private Cache loadedModels; private HashMap smallLoadingCache = new HashMap<>(); - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/client/model/geometry/GeometryLoaderManager;init()V", remap = false)) - private void replaceTopLevelBakedModels() { - GeometryLoaderManager.init(); + @Redirect(method = "", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/resources/model/ModelBakery;blockColors:Lnet/minecraft/client/color/block/BlockColors;")) + private void replaceTopLevelBakedModels(ModelBakery bakery, BlockColors val) { + this.blockColors = val; this.loadedBakedModels = CacheBuilder.newBuilder() .expireAfterAccess(3, TimeUnit.MINUTES) .maximumSize(1000) From a3e7435c615922cb00318f70dc1d616274b5e631 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 11:10:09 -0400 Subject: [PATCH 08/36] Correctly emulate nullishness of baked top level model map --- .../DynamicBakedModelProvider.java | 24 ++++++++++++++++--- .../dynamic_resources/ModelBakeryMixin.java | 16 ++++++++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java index 74fca3d5..3b21d552 100644 --- a/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java +++ b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java @@ -5,6 +5,7 @@ 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.BuiltInModel; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.resources.ResourceLocation; import org.apache.commons.lang3.tuple.Triple; @@ -22,12 +23,19 @@ public class DynamicBakedModelProvider implements Map, BakedModel> bakedCache; private final Map permanentOverrides; + private BakedModel missingModel; + private static final BakedModel SENTINEL = new BuiltInModel(null, null, null, false); public DynamicBakedModelProvider(ModelBakery bakery, Map, BakedModel> cache) { this.bakery = bakery; this.bakedCache = cache; this.permanentOverrides = new Object2ObjectOpenHashMap<>(); } + + public void setMissingModel(BakedModel model) { + this.missingModel = model; + } + private static Triple vanillaKey(Object o) { return Triple.of((ResourceLocation)o, BlockModelRotation.X0_Y0.getRotation(), false); } @@ -43,7 +51,7 @@ public class DynamicBakedModelProvider implements Map textureGetter); + private Cache, BakedModel> loadedBakedModels; private Cache loadedModels; @@ -322,6 +324,8 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { @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(); + // ensure missing model is a permanent override + this.bakedTopLevelModels.put(MISSING_MODEL_LOCATION, this.getBakedModel(MISSING_MODEL_LOCATION, BlockModelRotation.X0_Y0, this.atlasSet::getSprite)); cir.setReturnValue(atlasSet); } @@ -446,6 +450,8 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { return loadOnlyRelevantBlockState(stateDefinition, location); } + private BakedModel bakedMissingModel = null; + @Inject(method = "getBakedModel", at = @At("HEAD"), cancellable = true) public void getOrLoadBakedModelDynamic(ResourceLocation arg, ModelState arg2, Function textureGetter, CallbackInfoReturnable cir) { Triple triple = Triple.of(arg, arg2.getRotation(), arg2.isUvLocked()); @@ -468,7 +474,15 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { } } if(ibakedmodel == null) { - ibakedmodel = iunbakedmodel.bake((ModelBakery) (Object) this, textureGetter, arg2, arg); + if(iunbakedmodel == missingModel) { + // use a shared baked missing model + if(bakedMissingModel == null) { + bakedMissingModel = iunbakedmodel.bake((ModelBakery) (Object) this, textureGetter, arg2, arg); + ((DynamicBakedModelProvider)this.bakedTopLevelModels).setMissingModel(bakedMissingModel); + } + ibakedmodel = bakedMissingModel; + } else + ibakedmodel = iunbakedmodel.bake((ModelBakery) (Object) this, textureGetter, arg2, arg); } DynamicModelBakeEvent event = new DynamicModelBakeEvent(arg, iunbakedmodel, ibakedmodel, (ModelLoader)(Object)this); MinecraftForge.EVENT_BUS.post(event); From e771af233009391dd511551ee49da3fbe0f4c7d9 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 11:33:26 -0400 Subject: [PATCH 09/36] AE2 model wrapping support --- build.gradle | 1 + .../ae2/RegistrationMixin.java | 58 +++++++++++++++++++ src/main/resources/modernfix.mixins.json | 1 + 3 files changed, 60 insertions(+) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ae2/RegistrationMixin.java diff --git a/build.gradle b/build.gradle index c4d6480f..f5df1f37 100644 --- a/build.gradle +++ b/build.gradle @@ -102,6 +102,7 @@ dependencies { modCompileOnly("curse.maven:supermartijncore-454372:4455378") modCompileOnly("curse.maven:valhesiastructures-347488:3476252") modCompileOnly files("deps/starlight-1.2.jar") + modCompileOnly("appeng:appliedenergistics2:8.4.7") } tasks.withType(JavaCompile) { diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ae2/RegistrationMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ae2/RegistrationMixin.java new file mode 100644 index 00000000..a04946a8 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ae2/RegistrationMixin.java @@ -0,0 +1,58 @@ +package org.embeddedt.modernfix.mixin.perf.dynamic_resources.ae2; + +import appeng.bootstrap.components.IModelBakeComponent; +import appeng.bootstrap.components.ModelOverrideComponent; +import appeng.core.Api; +import appeng.core.AppEng; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.ObfuscationReflectionHelper; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent; +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; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.function.BiFunction; + +@Mixin(targets = { "appeng/core/Registration" }) +public class RegistrationMixin { + private static Field customizerField; + @Inject(method = "registerClientEvents", at = @At("TAIL"), remap = false) + private void doRegisterDynBake(CallbackInfo ci) { + MinecraftForge.EVENT_BUS.addListener(this::onDynamicModelBake); + customizerField = ObfuscationReflectionHelper.findField(ModelOverrideComponent.class, "customizer"); + } + + private void onDynamicModelBake(DynamicModelBakeEvent event) { + if (!event.getLocation().getNamespace().equals(AppEng.MOD_ID)) { + return; + } + BakedModel missing = event.getModelLoader().getBakedTopLevelModels().get(ModelBakery.MISSING_MODEL_LOCATION); + if(event.getModel() == missing) + return; + Api.INSTANCE.definitions().getRegistry().getBootstrapComponents(IModelBakeComponent.class).forEachRemaining(c -> { + if(c instanceof ModelOverrideComponent) + handleModelOverride((ModelOverrideComponent)c, event); + }); + } + + private void handleModelOverride(ModelOverrideComponent c, DynamicModelBakeEvent event) { + Map> customizer; + try { + customizer = (Map>)customizerField.get(c); + } catch(ReflectiveOperationException e) { + ModernFix.LOGGER.error("Can't replace model", e); + return; + } + BiFunction fn = customizer.get(event.getLocation().getPath()); + if(fn != null) { + event.setModel(fn.apply(event.getLocation(), event.getModel())); + } + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 5be112b9..81f58eb4 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -86,6 +86,7 @@ "perf.dynamic_resources.BlockModelShaperMixin", "perf.dynamic_resources.ItemModelShaperMixin", "perf.dynamic_resources.ModelBakeryMixin", + "perf.dynamic_resources.ae2.RegistrationMixin", "perf.dynamic_resources.ctm.TextureMetadataHandlerMixin", "perf.dynamic_resources.ctm.CTMPackReloadListenerMixin", "perf.dynamic_resources.supermartijncore.ClientRegistrationHandlerMixin", From e46910f3c18234e0a57d917a82f09f8ff6caf42d Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 12:03:19 -0400 Subject: [PATCH 10/36] Add Opticrash detection --- .../modernfix/core/ModernFixMixinPlugin.java | 3 +++ .../core/config/ModernFixEarlyConfig.java | 20 +++++++++++++++++-- .../WindowMixin.java | 2 +- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index df6e7933..facdbb8f 100644 --- a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java +++ b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java @@ -53,6 +53,9 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { this.logger.info("Loaded configuration file for ModernFix: {} options available, {} override(s) found", config.getOptionCount(), config.getOptionOverrideCount()); + if(ModernFixEarlyConfig.OPTIFINE_PRESENT) + this.logger.fatal("OptiFine detected. Use of ModernFix with OptiFine is not supported due to its impact on launch time and breakage of Forge features."); + try { Class.forName("sun.misc.Unsafe").getDeclaredMethod("defineAnonymousClass", Class.class, byte[].class, Object[].class); } catch(ReflectiveOperationException | NullPointerException e) { 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 2096da80..b1e487ce 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -14,8 +14,23 @@ public class ModernFixEarlyConfig { private final Map options = new HashMap<>(); + public static final boolean OPTIFINE_PRESENT; + + static { + boolean hasOfClass = false; + try { + Class.forName("optifine.OptiFineTransformationService"); + hasOfClass = true; + } catch(Throwable e) { + } + OPTIFINE_PRESENT = hasOfClass; + } + private static boolean modPresent(String modId) { - return FMLLoader.getLoadingModList().getModFileById(modId) != null; + if(modId.equals("optifine")) + return OPTIFINE_PRESENT; + else + return FMLLoader.getLoadingModList().getModFileById(modId) != null; } private ModernFixEarlyConfig() { @@ -87,11 +102,12 @@ public class ModernFixEarlyConfig { disableIfModPresent("mixin.perf.compress_biome_container", "chocolate", "betterendforge"); disableIfModPresent("mixin.bugfix.mc218112", "performant"); disableIfModPresent("mixin.perf.reuse_datapacks", "tac"); + disableIfModPresent("mixin.launch.class_search_cache", "optifine"); } private void disableIfModPresent(String configName, String... ids) { for(String id : ids) { - if(FMLLoader.getLoadingModList().getModFileById(id) != null) { + if(modPresent(id)) { Option option = this.options.get(configName); if(option != null) option.addModOverride(false, id); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/bugfix/preserve_early_window_pos/WindowMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/preserve_early_window_pos/WindowMixin.java index 1333440b..b3bed93d 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/bugfix/preserve_early_window_pos/WindowMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/preserve_early_window_pos/WindowMixin.java @@ -52,7 +52,7 @@ public class WindowMixin { * Grab the original width/height from the window and inject them into our state variables. */ @SuppressWarnings("unchecked") - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/loading/progress/EarlyProgressVisualization;handOffWindow(Ljava/util/function/IntSupplier;Ljava/util/function/IntSupplier;Ljava/util/function/Supplier;Ljava/util/function/LongSupplier;)J")) + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/loading/progress/EarlyProgressVisualization;handOffWindow(Ljava/util/function/IntSupplier;Ljava/util/function/IntSupplier;Ljava/util/function/Supplier;Ljava/util/function/LongSupplier;)J"), require = 0) private long performHandoff(EarlyProgressVisualization instance, IntSupplier width, IntSupplier height, Supplier title, LongSupplier monitor) { Object visualizer = getEarlyProgressVisualizer(); if(visualizer != null) { From adcfed94b7ef09e01eb6019a1aa3a6a4231a8c26 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:28:46 -0400 Subject: [PATCH 11/36] Add semver plugin --- build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f5df1f37..babf0ddc 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,7 @@ plugins { id 'com.palantir.git-version' version '1.0.0' id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.79.0' id "com.modrinth.minotaur" version "2.+" + id "com.javiersc.semver" version "0.5.0-alpha.2" } sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8 @@ -221,4 +222,4 @@ tasks.modrinth.dependsOn(":generateChangelog") tasks.register('publishToModSites') { publishToModSites.dependsOn(tasks.modrinth) publishToModSites.dependsOn(tasks.curseforge) -} \ No newline at end of file +} From a976cc90fa9b35d3e41cb1f1f9ce11520335b2ff Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:36:52 -0400 Subject: [PATCH 12/36] Add release script --- release.sh | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100755 release.sh diff --git a/release.sh b/release.sh new file mode 100755 index 00000000..dfab16c3 --- /dev/null +++ b/release.sh @@ -0,0 +1,8 @@ +#!/bin/bash +scope="$1" +if [ -z "$scope" ]; then +echo Scope not provided +exit 1 +fi +./gradlew pushSemverTag -Psemver.scope=$scope +./gradlew publishToModSites From 2012b60832d2df38dc8c2c3ae79540b83d93470b Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:40:14 -0400 Subject: [PATCH 13/36] Update release script --- release.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/release.sh b/release.sh index dfab16c3..756cd209 100755 --- a/release.sh +++ b/release.sh @@ -1,8 +1,7 @@ #!/bin/bash -scope="$1" -if [ -z "$scope" ]; then -echo Scope not provided -exit 1 -fi -./gradlew pushSemverTag -Psemver.scope=$scope +echo -n "Currently on: " +git describe +echo -n "New version: " +read newtag +git tag -a $newtag -m "$newtag" ./gradlew publishToModSites From 50c6207f8ce1d63e406ac5b0e4fec8f8895baeaa Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:40:26 -0400 Subject: [PATCH 14/36] Remove semver plugin --- build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle b/build.gradle index babf0ddc..34698add 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,6 @@ plugins { id 'com.palantir.git-version' version '1.0.0' id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.79.0' id "com.modrinth.minotaur" version "2.+" - id "com.javiersc.semver" version "0.5.0-alpha.2" } sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8 From 7c793c7fb65c3b53345f7b729e4de2829e6644e7 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:41:33 -0400 Subject: [PATCH 15/36] Push before release --- release.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/release.sh b/release.sh index 756cd209..092d47c8 100755 --- a/release.sh +++ b/release.sh @@ -4,4 +4,6 @@ git describe echo -n "New version: " read newtag git tag -a $newtag -m "$newtag" +git push +git push --tags ./gradlew publishToModSites From e843f8ed6d567acb67a7bb728c9fbd1e45d48132 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 19:29:26 -0400 Subject: [PATCH 16/36] Clear KubeJS recipe event lists since mods can hold onto the event object --- .../mixin/perf/kubejs/RecipeEventJSMixin.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java index 45fe65cd..a64f754d 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java @@ -7,6 +7,7 @@ import dev.latvian.kubejs.recipe.RecipeJS; import dev.latvian.kubejs.recipe.filter.RecipeFilter; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.crafting.RecipeManager; +import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.util.KubeUtil; import org.embeddedt.modernfix.util.ModUtil; import org.spongepowered.asm.mixin.Final; @@ -17,6 +18,9 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -51,4 +55,37 @@ public class RecipeEventJSMixin { private void clearRecipeRegistry(RecipeManager manager, Map jsonMap, CallbackInfo ci) { KubeUtil.originalRecipesByHash.clear(); } + + /** + * The recipe event object can be leaked in scripts and this wastes 40MB of memory. + */ + @Inject(method = "post", at = @At("RETURN")) + private void clearRecipeLists(CallbackInfo ci) { + ModernFix.LOGGER.info("Clearing KubeJS recipe lists..."); + // Even though we are a mixin class, use reflection so this works across a variety of versions + Field[] fields = RecipeEventJS.class.getDeclaredFields(); + for(Field f : fields) { + try { + if(!Modifier.isStatic(f.getModifiers()) + && (Collection.class.isAssignableFrom(f.getType()) + || Map.class.isAssignableFrom(f.getType())) + ) { + f.setAccessible(true); + Object collection = f.get(this); + int size; + if(collection instanceof Map) { + size = ((Map)collection).size(); + ((Map)collection).clear(); + } else if(collection instanceof Collection) { + size = ((Collection)collection).size(); + ((Collection)collection).clear(); + } else + throw new IllegalStateException(); + ModernFix.LOGGER.debug("Cleared {} with {} entries", f.getName(), size); + } + } catch(RuntimeException | ReflectiveOperationException e) { + ModernFix.LOGGER.debug("Uh oh, couldn't clear field", e); + } + } + } } From d344385aa310826f592a4f8746288fe11c0e809f Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 19:32:27 -0400 Subject: [PATCH 17/36] Fix compile error --- .../modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java index 964a3129..f834daa0 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java @@ -18,7 +18,7 @@ public class RecipeEventJSMixin { /** * The recipe event object can be leaked in scripts and this wastes 40MB of memory. */ - @Inject(method = "post", at = @At("RETURN")) + @Inject(method = "post", at = @At("RETURN"), remap = false) private void clearRecipeLists(CallbackInfo ci) { ModernFix.LOGGER.info("Clearing KubeJS recipe lists..."); // Even though we are a mixin class, use reflection so this works across a variety of versions From c0d27ee01d43f43be6f3d614503571a6f7f80033 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 19:37:48 -0400 Subject: [PATCH 18/36] Update KubeJS mixin for 1.19.2 --- .../modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java index f834daa0..ffbd0e26 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java @@ -1,6 +1,6 @@ package org.embeddedt.modernfix.mixin.perf.kubejs; -import dev.latvian.mods.kubejs.recipe.RecipeEventJS; +import dev.latvian.mods.kubejs.recipe.RecipesEventJS; import org.embeddedt.modernfix.ModernFix; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -12,7 +12,7 @@ import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Map; -@Mixin(RecipeEventJS.class) +@Mixin(RecipesEventJS.class) public class RecipeEventJSMixin { /** @@ -22,7 +22,7 @@ public class RecipeEventJSMixin { private void clearRecipeLists(CallbackInfo ci) { ModernFix.LOGGER.info("Clearing KubeJS recipe lists..."); // Even though we are a mixin class, use reflection so this works across a variety of versions - Field[] fields = RecipeEventJS.class.getDeclaredFields(); + Field[] fields = RecipesEventJS.class.getDeclaredFields(); for(Field f : fields) { try { if(!Modifier.isStatic(f.getModifiers()) From d86344f3099229666758dcbe4b0d786d170926a1 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 21:05:02 -0400 Subject: [PATCH 19/36] Add logic to clear air items of NBT from Patchouli book registry --- build.gradle | 2 + .../core/config/ModernFixEarlyConfig.java | 1 + .../ClientBookRegistryMixin.java | 68 +++++++++++++++++++ src/main/resources/modernfix.mixins.json | 1 + 4 files changed, 72 insertions(+) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java diff --git a/build.gradle b/build.gradle index 34698add..4ab0f76e 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,7 @@ repositories { maven { // CTM url "https://maven.tterrag.com/" } + maven { url 'https://maven.blamejared.com' } } dependencies { @@ -103,6 +104,7 @@ dependencies { modCompileOnly("curse.maven:valhesiastructures-347488:3476252") modCompileOnly files("deps/starlight-1.2.jar") modCompileOnly("appeng:appliedenergistics2:8.4.7") + modCompileOnly("vazkii.patchouli:Patchouli:1.16.4-53.3") } tasks.withType(JavaCompile) { 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 b1e487ce..0a019baf 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -83,6 +83,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.deduplicate_location", false); this.addMixinRule("perf.cache_blockstate_cache_arrays", true); this.addMixinRule("perf.cache_model_materials", true); + this.addMixinRule("perf.patchouli_deduplicate_books", modPresent("patchouli")); this.addMixinRule("perf.datapack_reload_exceptions", true); this.addMixinRule("perf.async_locator", true); this.addMixinRule("perf.faster_texture_stitching", true); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java new file mode 100644 index 00000000..2870f173 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java @@ -0,0 +1,68 @@ +package org.embeddedt.modernfix.mixin.perf.patchouli_deduplicate_books; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraftforge.fml.common.ObfuscationReflectionHelper; +import org.embeddedt.modernfix.ModernFix; +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; +import vazkii.patchouli.client.book.BookEntry; +import vazkii.patchouli.client.book.BookPage; +import vazkii.patchouli.client.book.ClientBookRegistry; +import vazkii.patchouli.client.book.page.PageTemplate; +import vazkii.patchouli.client.book.template.BookTemplate; +import vazkii.patchouli.client.book.template.TemplateComponent; +import vazkii.patchouli.client.book.template.component.ComponentItemStack; +import vazkii.patchouli.common.book.Book; +import vazkii.patchouli.common.book.BookRegistry; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +@Mixin(ClientBookRegistry.class) +public class ClientBookRegistryMixin { + @Inject(method = "reload", at = @At("RETURN"), remap = false) + private void performDeduplication(CallbackInfo ci) { + Field templateField = ObfuscationReflectionHelper.findField(PageTemplate.class, "template"); + Field componentsField = ObfuscationReflectionHelper.findField(BookTemplate.class, "components"); + Field itemsField = ObfuscationReflectionHelper.findField(ComponentItemStack.class, "items"); + int numItemsCleared = 0; + for(Book book : BookRegistry.INSTANCE.books.values()) { + for(BookEntry entry : book.contents.entries.values()) { + for(BookPage page : entry.getPages()) { + if(page instanceof PageTemplate) { + List components; + try { + BookTemplate template = (BookTemplate)templateField.get(page); + components = (List)componentsField.get(template); + for(TemplateComponent component : components) { + if(component instanceof ComponentItemStack) { + ItemStack[] items = (ItemStack[])itemsField.get(component); + for(ItemStack item : items) { + if(item.getItem() == Items.AIR) { + // remove any NBT + CompoundTag tag = item.getTag(); + if(tag != null) { + numItemsCleared++; + List keys = new ArrayList<>(tag.getAllKeys()); + for(String key : keys) + item.removeTagKey(key); + } + } + } + } + } + } catch(ReflectiveOperationException e) { + continue; + } + } + } + } + } + ModernFix.LOGGER.info("Cleared {} unneeded book NBT tags", numItemsCleared); + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 81f58eb4..aab39d97 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -95,6 +95,7 @@ "perf.model_optimizations.TransformationMatrixMixin", "perf.model_optimizations.BooleanPropertyMixin", "perf.model_optimizations.PropertyMixin", + "perf.patchouli_deduplicate_books.ClientBookRegistryMixin", "perf.async_jei.InputConstantsMixin", "perf.async_jei.IngredientListElementFactoryMixin", "perf.async_jei.ClientLifecycleHandlerMixin", From c684cccf74ffb19cd3b0913e6eaf48c81135d477 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 21:20:30 -0400 Subject: [PATCH 20/36] Simplify book deduplication --- .../ClientBookRegistryMixin.java | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java index 2870f173..f31a628f 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java @@ -1,6 +1,5 @@ package org.embeddedt.modernfix.mixin.perf.patchouli_deduplicate_books; -import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraftforge.fml.common.ObfuscationReflectionHelper; @@ -20,7 +19,6 @@ import vazkii.patchouli.common.book.Book; import vazkii.patchouli.common.book.BookRegistry; import java.lang.reflect.Field; -import java.util.ArrayList; import java.util.List; @Mixin(ClientBookRegistry.class) @@ -42,16 +40,9 @@ public class ClientBookRegistryMixin { for(TemplateComponent component : components) { if(component instanceof ComponentItemStack) { ItemStack[] items = (ItemStack[])itemsField.get(component); - for(ItemStack item : items) { - if(item.getItem() == Items.AIR) { - // remove any NBT - CompoundTag tag = item.getTag(); - if(tag != null) { - numItemsCleared++; - List keys = new ArrayList<>(tag.getAllKeys()); - for(String key : keys) - item.removeTagKey(key); - } + for(int i = 0; i < items.length; i++) { + if(items[i] != null && items[i].getItem() == Items.AIR) { + items[i] = ItemStack.EMPTY; } } } From 56aed7284ca22fb319d56e037b7043a8f6f98486 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 21:28:31 -0400 Subject: [PATCH 21/36] More null checks --- .../ClientBookRegistryMixin.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java index f31a628f..4bdced31 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java @@ -8,6 +8,7 @@ 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; +import vazkii.patchouli.client.book.BookContents; import vazkii.patchouli.client.book.BookEntry; import vazkii.patchouli.client.book.BookPage; import vazkii.patchouli.client.book.ClientBookRegistry; @@ -26,32 +27,41 @@ public class ClientBookRegistryMixin { @Inject(method = "reload", at = @At("RETURN"), remap = false) private void performDeduplication(CallbackInfo ci) { Field templateField = ObfuscationReflectionHelper.findField(PageTemplate.class, "template"); + Field contentsField = ObfuscationReflectionHelper.findField(Book.class, "contents"); Field componentsField = ObfuscationReflectionHelper.findField(BookTemplate.class, "components"); Field itemsField = ObfuscationReflectionHelper.findField(ComponentItemStack.class, "items"); int numItemsCleared = 0; for(Book book : BookRegistry.INSTANCE.books.values()) { - for(BookEntry entry : book.contents.entries.values()) { - for(BookPage page : entry.getPages()) { - if(page instanceof PageTemplate) { - List components; - try { - BookTemplate template = (BookTemplate)templateField.get(page); - components = (List)componentsField.get(template); - for(TemplateComponent component : components) { - if(component instanceof ComponentItemStack) { - ItemStack[] items = (ItemStack[])itemsField.get(component); - for(int i = 0; i < items.length; i++) { - if(items[i] != null && items[i].getItem() == Items.AIR) { + try { + BookContents contents = (BookContents)contentsField.get(book); + if(contents == null || contents.entries == null) + continue; + for(BookEntry entry : contents.entries.values()) { + for(BookPage page : entry.getPages()) { + if(page instanceof PageTemplate) { + List components; + BookTemplate template = (BookTemplate) templateField.get(page); + if(template == null) + continue; + components = (List) componentsField.get(template); + if(components == null) + continue; + for (TemplateComponent component : components) { + if (component instanceof ComponentItemStack) { + ItemStack[] items = (ItemStack[]) itemsField.get(component); + if(items == null) + continue; + for (int i = 0; i < items.length; i++) { + if (items[i] != null && items[i].getItem() == Items.AIR) { items[i] = ItemStack.EMPTY; } } } } - } catch(ReflectiveOperationException e) { - continue; } } } + } catch(ReflectiveOperationException ignored) { } } ModernFix.LOGGER.info("Cleared {} unneeded book NBT tags", numItemsCleared); From 110362b85c5aa084c8d1e27efab64bd1c660bfa1 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 21:34:31 -0400 Subject: [PATCH 22/36] Fix log message always saying 0 items were cleared --- .../patchouli_deduplicate_books/ClientBookRegistryMixin.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java index 4bdced31..3760b4e8 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java @@ -53,6 +53,7 @@ public class ClientBookRegistryMixin { continue; for (int i = 0; i < items.length; i++) { if (items[i] != null && items[i].getItem() == Items.AIR) { + numItemsCleared++; items[i] = ItemStack.EMPTY; } } From d356e1ece42c0bfe5e2ff7c9339b5216bcb710da Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 13:42:53 -0400 Subject: [PATCH 23/36] Optimize NBT memory usage --- .../core/config/ModernFixEarlyConfig.java | 1 + .../nbt_memory_usage/CompoundTagMixin.java | 45 ++++++ .../modernfix/util/CanonizingStringMap.java | 147 ++++++++++++++++++ .../resources/META-INF/accesstransformer.cfg | 3 +- src/main/resources/modernfix.mixins.json | 1 + 5 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java 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 0a019baf..647aaed0 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -83,6 +83,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.deduplicate_location", false); this.addMixinRule("perf.cache_blockstate_cache_arrays", true); this.addMixinRule("perf.cache_model_materials", true); + this.addMixinRule("perf.nbt_memory_usage", true); this.addMixinRule("perf.patchouli_deduplicate_books", modPresent("patchouli")); this.addMixinRule("perf.datapack_reload_exceptions", true); this.addMixinRule("perf.async_locator", true); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java new file mode 100644 index 00000000..51072a68 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java @@ -0,0 +1,45 @@ +package org.embeddedt.modernfix.mixin.perf.nbt_memory_usage; + +import com.google.common.collect.Maps; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import org.embeddedt.modernfix.util.CanonizingStringMap; +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.ModifyArg; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Map; + +@Mixin(CompoundTag.class) +public class CompoundTagMixin { + @Shadow @Final @Mutable + private Map tags; + + /** + * Ensure that the backing map is always a CanonizingStringMap. + */ + @Redirect(method = "(Ljava/util/Map;)V", at = @At(value = "FIELD", target = "Lnet/minecraft/nbt/CompoundTag;tags:Ljava/util/Map;", ordinal = 0)) + private void replaceTagMap(CompoundTag tag, Map incomingMap) { + if(incomingMap instanceof CanonizingStringMap) + this.tags = incomingMap; + else { + this.tags = new CanonizingStringMap<>(); + this.tags.putAll(incomingMap); + } + } + + /** + * @author embeddedt + * @reason use more efficient method when copying canonizing string map + */ + @Inject(method = "copy()Lnet/minecraft/nbt/Tag;", at = @At("HEAD"), cancellable = true) + public void copyEfficient(CallbackInfoReturnable cir) { + if(this.tags instanceof CanonizingStringMap) { + cir.setReturnValue(new CompoundTag(CanonizingStringMap.deepCopy((CanonizingStringMap)this.tags, Tag::copy))); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java b/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java new file mode 100644 index 00000000..f4238504 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java @@ -0,0 +1,147 @@ +package org.embeddedt.modernfix.util; + +import com.google.common.collect.Interner; +import com.google.common.collect.Interners; +import it.unimi.dsi.fastutil.objects.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Function; + +/** + * Replacement backing map for CompoundTags. Uses an array map for tags with 4 or less entries, + * and a hash map for larger tags. + */ +public class CanonizingStringMap implements Map { + private Object2ObjectMap backingMap; + + private static final int GROWTH_THRESHOLD = 4; + private static final Interner KEY_INTERNER = Interners.newStrongInterner(); + + public CanonizingStringMap() { + this(new Object2ObjectArrayMap<>()); + } + + protected CanonizingStringMap(Object2ObjectMap newMap) { + this.backingMap = newMap; + } + + @Override + public int size() { + return backingMap.size(); + } + + @Override + public boolean isEmpty() { + return backingMap.isEmpty(); + } + + @Override + public boolean containsKey(Object o) { + return backingMap.containsKey(o); + } + + @Override + public boolean containsValue(Object o) { + return backingMap.containsValue(o); + } + + @Override + public T get(Object o) { + return backingMap.get(o); + } + + @Nullable + @Override + public T put(String s, T t) { + if(backingMap.size() >= GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap) && !backingMap.containsKey(s)) { + // map will grow to GROWTH_THRESHOLD + 1 entries, change to hashmap + backingMap = new Object2ObjectOpenHashMap<>(backingMap); + } + s = KEY_INTERNER.intern(s); + return backingMap.put(s, t); + } + + @Override + public T remove(Object o) { + T value = backingMap.remove(o); + // need to shrink to be consistent with new maps + if(backingMap.size() <= GROWTH_THRESHOLD && backingMap instanceof Object2ObjectOpenHashMap) { + backingMap = new Object2ObjectArrayMap<>(backingMap); + } + return value; + } + + @Override + public void putAll(@NotNull Map map) { + if(map.size() == 0) + return; + map.forEach((String key, T val) -> { + key = KEY_INTERNER.intern(key); + backingMap.put(key, val); + }); + // if it's too big to be an array, grow it + if(backingMap.size() > GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap)) { + backingMap = new Object2ObjectOpenHashMap<>(backingMap); + } + } + + @Override + public void clear() { + if(!(this.backingMap instanceof Object2ObjectArrayMap)) + this.backingMap = new Object2ObjectArrayMap<>(); + else + this.backingMap.clear(); + } + + @NotNull + @Override + public Set keySet() { + return Collections.unmodifiableSet(this.backingMap.keySet()); + } + + @NotNull + @Override + public Collection values() { + return Collections.unmodifiableCollection(this.backingMap.values()); + } + + @NotNull + @Override + public Set> entrySet() { + return Collections.unmodifiableSet(this.backingMap.entrySet()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CanonizingStringMap that = (CanonizingStringMap)o; + if(that.backingMap.size() != backingMap.size()) + return false; + return backingMap.object2ObjectEntrySet().containsAll(that.backingMap.object2ObjectEntrySet()); + } + + /** + * We deliberately use a hashcode that will be consistent regardless of underlying map type. + */ + @Override + public int hashCode() { + final ObjectIterator> i = Object2ObjectMaps.fastIterator(backingMap); + int h = 0, n = backingMap.size(); + while (n-- != 0) + h += i.next().hashCode(); + return h; + } + + public static CanonizingStringMap deepCopy(CanonizingStringMap inputMap, Function deepCopier) { + Object2ObjectMap copiedBackingMap; + if(inputMap.backingMap instanceof Object2ObjectArrayMap) + copiedBackingMap = ((Object2ObjectArrayMap)inputMap.backingMap).clone(); + else + copiedBackingMap = ((Object2ObjectOpenHashMap)inputMap.backingMap).clone(); + copiedBackingMap.replaceAll((k, v) -> deepCopier.apply(v)); + return new CanonizingStringMap<>(copiedBackingMap); + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index a12ea313..ff8cdd75 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -23,4 +23,5 @@ public net.minecraft.block.AbstractBlock$Properties field_235818_t_ # hasPostPro public net.minecraft.block.AbstractBlock$Properties field_235819_u_ # emissiveRendering public net.minecraft.block.AbstractBlock$Properties field_235806_h_ # requiresCorrectToolForDrops public net.minecraft.block.AbstractBlock$Properties field_200959_g # destroyTime -public net.minecraft.world.server.ServerChunkProvider$ChunkExecutor \ No newline at end of file +public net.minecraft.world.server.ServerChunkProvider$ChunkExecutor +public net.minecraft.nbt.CompoundNBT (Ljava/util/Map;)V # \ No newline at end of file diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index aab39d97..80bef802 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -59,6 +59,7 @@ "perf.kubejs.RecipeJSMixin", "perf.kubejs.IDFilterMixin", "perf.kubejs.CustomIngredientMixin", + "perf.nbt_memory_usage.CompoundTagMixin", "perf.fast_registry_validation.ForgeRegistryMixin", "perf.cache_strongholds.ChunkGeneratorMixin", "perf.cache_upgraded_structures.StructureManagerMixin", From be7546810062c5a49560321d387ce8f165124f2b Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 13:53:00 -0400 Subject: [PATCH 24/36] Fix compile error --- .../org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java index 06e63d15..a4871a5b 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java @@ -1,7 +1,6 @@ package org.embeddedt.modernfix.mixin.devenv; import com.mojang.text2speech.Narrator; -import com.mojang.text2speech.NarratorDummy; import net.minecraft.client.GameNarrator; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -11,6 +10,6 @@ import org.spongepowered.asm.mixin.injection.Redirect; 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(); + return Narrator.EMPTY; } } From a4a19be54538f773ac0829a13e261273cf10590b Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 13:58:25 -0400 Subject: [PATCH 25/36] Avoid using replaceAll --- .../modernfix/util/CanonizingStringMap.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java b/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java index f4238504..737e9bca 100644 --- a/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java +++ b/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java @@ -137,11 +137,14 @@ public class CanonizingStringMap implements Map { public static CanonizingStringMap deepCopy(CanonizingStringMap inputMap, Function deepCopier) { Object2ObjectMap copiedBackingMap; - if(inputMap.backingMap instanceof Object2ObjectArrayMap) - copiedBackingMap = ((Object2ObjectArrayMap)inputMap.backingMap).clone(); - else - copiedBackingMap = ((Object2ObjectOpenHashMap)inputMap.backingMap).clone(); - copiedBackingMap.replaceAll((k, v) -> deepCopier.apply(v)); + int size = inputMap.backingMap.size(); + if(size > GROWTH_THRESHOLD) { + copiedBackingMap = new Object2ObjectOpenHashMap<>(size); + } else + copiedBackingMap = new Object2ObjectArrayMap<>(size); + inputMap.backingMap.object2ObjectEntrySet().forEach(entry -> { + copiedBackingMap.put(entry.getKey(), deepCopier.apply(entry.getValue())); + }); return new CanonizingStringMap<>(copiedBackingMap); } } From 821a15ecaafdadcca9077524e3b5ac93c6cc646c Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:15:24 -0400 Subject: [PATCH 26/36] Turn off Forge resource cache --- .../ResourceCacheManagerMixin.java | 15 ++++ .../VanillaPackResourcesMixin.java | 70 +++++++++++++++++-- src/main/resources/modernfix.mixins.json | 1 + 3 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/ResourceCacheManagerMixin.java diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/ResourceCacheManagerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/ResourceCacheManagerMixin.java new file mode 100644 index 00000000..cd05ba6a --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/ResourceCacheManagerMixin.java @@ -0,0 +1,15 @@ +package org.embeddedt.modernfix.mixin.perf.modern_resourcepacks; + +import net.minecraftforge.resource.ResourceCacheManager; +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.CallbackInfoReturnable; + +@Mixin(value = ResourceCacheManager.class, remap = false) +public class ResourceCacheManagerMixin { + @Inject(method = "shouldUseCache", at = @At("HEAD"), cancellable = true) + private static void disableCache(CallbackInfoReturnable cir) { + cir.setReturnValue(false); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java index 8fc0086a..b2fb6978 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java @@ -1,22 +1,84 @@ package org.embeddedt.modernfix.mixin.perf.modern_resourcepacks; -import net.minecraft.resources.ResourceLocation; +import com.google.common.base.Joiner; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.LoadingCache; import net.minecraft.server.packs.PackType; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.VanillaPackResources; +import net.minecraft.server.packs.metadata.pack.PackMetadataSection; +import org.apache.commons.lang3.tuple.Pair; +import org.embeddedt.modernfix.FileWalker; +import org.embeddedt.modernfix.util.FileUtil; +import org.embeddedt.modernfix.util.PackTypeHelper; 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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Map; +import java.nio.file.*; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.stream.Stream; @Mixin(VanillaPackResources.class) public class VanillaPackResourcesMixin { @Shadow @Final private static Map ROOT_DIR_BY_TYPE; + private static LoadingCache, List> pathStreamLoadingCache = CacheBuilder.newBuilder() + .build(FileWalker.INSTANCE); + + private static Set containedPaths = null; + + @Inject(method = "", at = @At("TAIL")) + private void cacheContainedPaths(PackMetadataSection arg, String[] p_i47912_1_, CallbackInfo ci) { + if(containedPaths != null) + return; + containedPaths = new HashSet<>(); + Joiner slashJoiner = Joiner.on('/'); + for(PackType type : PackType.values()) { + if(!PackTypeHelper.isVanillaPackType(type)) + continue; + Path root = ROOT_DIR_BY_TYPE.get(type); + if(root == null) + throw new IllegalStateException("No filesystem for vanilla " + type.name() + " assets"); + try { + try(Stream stream = Files.walk(root)) { + stream + .map(path -> root.relativize(path.toAbsolutePath())) + .forEach(path -> containedPaths.add(slashJoiner.join(type.getDirectory(), path))); + } + } catch(IOException e) { + e.printStackTrace(); + } + } + } + + @Redirect(method = "getResources(Ljava/util/Collection;Ljava/lang/String;Ljava/nio/file/Path;Ljava/lang/String;Ljava/util/function/Predicate;)V", at = @At(value = "INVOKE", target = "Ljava/nio/file/Files;walk(Ljava/nio/file/Path;[Ljava/nio/file/FileVisitOption;)Ljava/util/stream/Stream;")) + private static Stream useCacheForLoading(Path path, FileVisitOption[] fileVisitOptions) throws IOException { + try { + return pathStreamLoadingCache.get(Pair.of(path, Integer.MAX_VALUE)).stream(); + } catch (ExecutionException e) { + if(e.getCause() instanceof IOException) /* generally always should be */ + throw (IOException)e.getCause(); + else + throw new IOException(e); + } + } + + @Inject(method = "hasResource", at = @At(value = "INVOKE", target = "Ljava/lang/Class;getResource(Ljava/lang/String;)Ljava/net/URL;"), cancellable = true) + private void useCacheForExistence(PackType type, ResourceLocation location, CallbackInfoReturnable cir) { + if(!PackTypeHelper.isVanillaPackType(type)) + return; + cir.setReturnValue(containedPaths.contains(type.getDirectory() + "/" + location.getNamespace() + "/" + FileUtil.normalize(location.getPath()))); + } /** * @author embeddedt diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index b6949679..86a50955 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -10,6 +10,7 @@ "bugfix.edge_chunk_not_saved.ChunkManagerMixin", "perf.modern_resourcepacks.VanillaPackResourcesMixin", "perf.modern_resourcepacks.PathPackResourcesMixin", + "perf.modern_resourcepacks.ResourceCacheManagerMixin", "perf.dynamic_structure_manager.StructureManagerMixin", "bugfix.chunk_deadlock.ServerChunkCacheMixin", "perf.dedicated_reload_executor.MinecraftServerMixin", From a4515e8eae7817a282a4202cb73907d67e7de7b6 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:15:35 -0400 Subject: [PATCH 27/36] Fix injector target --- .../modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java index 51072a68..348a8f4b 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java @@ -36,7 +36,7 @@ public class CompoundTagMixin { * @author embeddedt * @reason use more efficient method when copying canonizing string map */ - @Inject(method = "copy()Lnet/minecraft/nbt/Tag;", at = @At("HEAD"), cancellable = true) + @Inject(method = "copy()Lnet/minecraft/nbt/CompoundTag;", at = @At("HEAD"), cancellable = true) public void copyEfficient(CallbackInfoReturnable cir) { if(this.tags instanceof CanonizingStringMap) { cir.setReturnValue(new CompoundTag(CanonizingStringMap.deepCopy((CanonizingStringMap)this.tags, Tag::copy))); From 1fc1e132cbee52c0560b5470a32522d8bde46947 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:22:06 -0400 Subject: [PATCH 28/36] Fix injector target --- .../modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java index 51072a68..348a8f4b 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java @@ -36,7 +36,7 @@ public class CompoundTagMixin { * @author embeddedt * @reason use more efficient method when copying canonizing string map */ - @Inject(method = "copy()Lnet/minecraft/nbt/Tag;", at = @At("HEAD"), cancellable = true) + @Inject(method = "copy()Lnet/minecraft/nbt/CompoundTag;", at = @At("HEAD"), cancellable = true) public void copyEfficient(CallbackInfoReturnable cir) { if(this.tags instanceof CanonizingStringMap) { cir.setReturnValue(new CompoundTag(CanonizingStringMap.deepCopy((CanonizingStringMap)this.tags, Tag::copy))); From 91d1cb3962f260cff8afbb68dc0b20555695b444 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 19:11:39 -0400 Subject: [PATCH 29/36] Bypass slow PngInfo class during texture loading Thanks to @Asek3 for pointing out this bottleneck --- .../core/config/ModernFixEarlyConfig.java | 1 + .../TextureAtlasMixin.java | 110 ++++++++++++++++++ .../resources/META-INF/accesstransformer.cfg | 3 +- src/main/resources/modernfix.mixins.json | 2 + 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_loading/TextureAtlasMixin.java 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 647aaed0..0f859f12 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -88,6 +88,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.datapack_reload_exceptions", true); this.addMixinRule("perf.async_locator", true); this.addMixinRule("perf.faster_texture_stitching", true); + this.addMixinRule("perf.faster_texture_loading", true); this.addMixinRule("perf.kubejs", modPresent("kubejs")); this.addMixinRule("perf.faster_singleplayer_load", false); /* Keep this off if JEI isn't installed to prevent breaking vanilla gameplay */ diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_loading/TextureAtlasMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_loading/TextureAtlasMixin.java new file mode 100644 index 00000000..1e326451 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_loading/TextureAtlasMixin.java @@ -0,0 +1,110 @@ +package org.embeddedt.modernfix.mixin.perf.faster_texture_loading; + +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.datafixers.util.Pair; +import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.metadata.animation.AnimationMetadataSection; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.fml.ModLoader; +import org.apache.commons.lang3.tuple.Triple; +import org.embeddedt.modernfix.ModernFix; +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.CallbackInfoReturnable; + +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +@Mixin(TextureAtlas.class) +public abstract class TextureAtlasMixin { + @Shadow protected abstract ResourceLocation getResourceLocation(ResourceLocation location); + + @Shadow protected abstract Collection getBasicSpriteInfos(ResourceManager resourceManager, Set spriteLocations); + + private Map> loadedImages; + private boolean usingFasterLoad; + /** + * @author embeddedt + * @reason simplify texture loading by loading whole image once, avoid slow PngInfo code + */ + @Redirect(method = "prepareToStitch", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/texture/TextureAtlas;getBasicSpriteInfos(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/Set;)Ljava/util/Collection;")) + private Collection loadImages(TextureAtlas atlas, ResourceManager manager, Set imageLocations) { + usingFasterLoad = ModLoader.isLoadingStateValid(); + // bail if Forge is erroring to avoid AT crashes + if(!usingFasterLoad) { + return getBasicSpriteInfos(manager, imageLocations); + } + List> futures = new ArrayList<>(); + ConcurrentLinkedQueue results = new ConcurrentLinkedQueue<>(); + loadedImages = new ConcurrentHashMap<>(); + for(ResourceLocation location : imageLocations) { + if(MissingTextureAtlasSprite.getLocation().equals(location)) + continue; + futures.add(CompletableFuture.runAsync(() -> { + try { + ResourceLocation fileLocation = this.getResourceLocation(location); + Resource resource = manager.getResource(fileLocation); + NativeImage image = NativeImage.read(resource.getInputStream()); + AnimationMetadataSection animData = resource.getMetadata(AnimationMetadataSection.SERIALIZER); + if (animData == null) { + animData = AnimationMetadataSection.EMPTY; + } + Pair dimensions = animData.getFrameSize(image.getWidth(), image.getHeight()); + loadedImages.put(location, Pair.of(resource, image)); + results.add(new TextureAtlasSprite.Info(location, dimensions.getFirst(), dimensions.getSecond(), animData)); + } catch(IOException e) { + ModernFix.LOGGER.error("Using missing texture, unable to load {} : {}", location, e); + } catch(RuntimeException e) { + ModernFix.LOGGER.error("Unable to parse metadata from {} : {}", location, e); + } + }, ModernFix.resourceReloadExecutor())); + } + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); + return results; + } + + @Inject(method = "prepareToStitch", at = @At("RETURN")) + private void clearLoadedImages(CallbackInfoReturnable cir) { + loadedImages = Collections.emptyMap(); + } + + @Inject(method = "load(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$Info;IIIII)Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;", + at = @At("HEAD"), cancellable = true) + private void loadFromExisting(ResourceManager resourceManager, TextureAtlasSprite.Info spriteInfo, int width, int height, int mipmapLevel, int originX, int originY, CallbackInfoReturnable cir) { + if(!usingFasterLoad) + return; + Pair pair = loadedImages.get(spriteInfo.name()); + if(pair == null) { + ModernFix.LOGGER.error("Texture {} was not loaded in early stage", spriteInfo.name()); + cir.setReturnValue(null); + } else { + TextureAtlasSprite sprite = null; + try { + sprite = ForgeHooksClient.loadTextureAtlasSprite((TextureAtlas)(Object)this, resourceManager, spriteInfo, pair.getFirst(), width, height, originX, originY, mipmapLevel, pair.getSecond()); + if(sprite == null) + sprite = new TextureAtlasSprite((TextureAtlas)(Object)this, spriteInfo, mipmapLevel, width, height, originX, originY, pair.getSecond()); + } catch(RuntimeException e) { + ModernFix.LOGGER.error("Error loading texture {}: {}", spriteInfo.name(), e); + } finally { + try { + pair.getFirst().close(); + } catch(IOException ignored) { + // not much we can do + } + } + cir.setReturnValue(sprite); + } + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index ff8cdd75..22dba02c 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -24,4 +24,5 @@ public net.minecraft.block.AbstractBlock$Properties field_235819_u_ # emissiveRe public net.minecraft.block.AbstractBlock$Properties field_235806_h_ # requiresCorrectToolForDrops public net.minecraft.block.AbstractBlock$Properties field_200959_g # destroyTime public net.minecraft.world.server.ServerChunkProvider$ChunkExecutor -public net.minecraft.nbt.CompoundNBT (Ljava/util/Map;)V # \ No newline at end of file +public net.minecraft.nbt.CompoundNBT (Ljava/util/Map;)V # +public net.minecraft.client.renderer.texture.TextureAtlasSprite (Lnet/minecraft/client/renderer/texture/TextureAtlas;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$Info;IIIIILcom/mojang/blaze3d/platform/NativeImage;)V # \ No newline at end of file diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 80bef802..c96497ec 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -75,6 +75,7 @@ "client": [ "core.MinecraftMixin", "core.SynchedEntityDataMixin", + "core.ModelBakeryMixin", "feature.measure_time.MinecraftMixin", "feature.reduce_loading_screen_freezes.ModelBakeryMixin", "perf.skip_first_datapack_reload.MinecraftMixin", @@ -112,6 +113,7 @@ "perf.cache_model_materials.BlockModelMixin", "perf.cache_model_materials.MultipartMixin", "perf.faster_texture_stitching.StitcherMixin", + "perf.faster_texture_loading.TextureAtlasMixin", "bugfix.packet_leak.ClientPlayNetHandlerMixin", "bugfix.packet_leak.SCustomPayloadPlayPacketMixin", "perf.reuse_datapacks.MinecraftMixin", From 934da3660f0bc0cf9ed131974c398469152864a0 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 19:16:07 -0400 Subject: [PATCH 30/36] Fix debug mixin being checked in --- src/main/resources/modernfix.mixins.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index c96497ec..51765862 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -75,7 +75,6 @@ "client": [ "core.MinecraftMixin", "core.SynchedEntityDataMixin", - "core.ModelBakeryMixin", "feature.measure_time.MinecraftMixin", "feature.reduce_loading_screen_freezes.ModelBakeryMixin", "perf.skip_first_datapack_reload.MinecraftMixin", From 55e7831c57465f552dc04cef8e127546d8f18a44 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 19:41:05 -0400 Subject: [PATCH 31/36] Mark AE2 and Patchouli as compile only --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d0825054..63ad555a 100644 --- a/build.gradle +++ b/build.gradle @@ -100,8 +100,8 @@ dependencies { modRuntimeOnly("curse.maven:ferritecore-429235:4074294") modCompileOnly("team.chisel.ctm:CTM:${ctm_version}") modCompileOnly("curse.maven:supermartijncore-454372:4455384") - modImplementation("appeng:appliedenergistics2-forge:11.7.3") - modImplementation("vazkii.patchouli:Patchouli:1.18.2-71.1") + modCompileOnly("appeng:appliedenergistics2-forge:11.7.3") + modCompileOnly("vazkii.patchouli:Patchouli:1.18.2-71.1") } tasks.withType(JavaCompile) { From ba3d418260932a3f621bc0e995ffa2432598ed39 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 20:37:37 -0400 Subject: [PATCH 32/36] Speed up processing of dummy registry entries --- .../core/config/ModernFixEarlyConfig.java | 1 + .../NamespacedHolderHelperMixin.java | 46 +++++++++++++++++++ src/main/resources/modernfix.mixins.json | 1 + 3 files changed, 48 insertions(+) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java 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 228627f1..ef65ce46 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -59,6 +59,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.compress_blockstate", false); this.addMixinRule("bugfix.concurrency", true); this.addMixinRule("bugfix.edge_chunk_not_saved", true); + this.addMixinRule("perf.fast_forge_dummies", true); this.addMixinRule("perf.dynamic_structure_manager", true); this.addMixinRule("bugfix.chunk_deadlock", true); this.addMixinRule("perf.thread_priorities", true); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java new file mode 100644 index 00000000..d1e586a8 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java @@ -0,0 +1,46 @@ +package org.embeddedt.modernfix.mixin.perf.fast_forge_dummies; + +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.registries.IForgeRegistryEntry; +import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.Opcodes; +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.callback.CallbackInfoReturnable; + +import java.util.Map; +import java.util.function.Function; + +@Mixin(targets = { "net/minecraftforge/registries/NamespacedHolderHelper" }) +public class NamespacedHolderHelperMixin> { + @Shadow private Map> holdersByName; + + @Shadow @Final private @Nullable Function> holderLookup; + + @Shadow private Map> holders; + + @Shadow @Final private Registry self; + + @Inject(method = "freeze", at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraftforge/registries/NamespacedHolderHelper;holdersByName:Ljava/util/Map;"), cancellable = true, remap = false) + private void fastDummyCheck(CallbackInfoReturnable> cir) { + // Quickly iterate without making any streams, etc. to see if everything is fine + // Use the slow path (by returning without cancelling) when there is an error + for(Holder.Reference ref : this.holdersByName.values()) { + if(!ref.isBound()) + return; + } + if (this.holderLookup != null) { + for(Holder.Reference ref : this.holders.values()) { + if(ref.getType() == Holder.Reference.Type.INTRUSIVE && !ref.isBound()) + return; + } + } + // Skip the creation of streams + cir.setReturnValue(this.self); + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 6dfac2a9..fb3e4bfe 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -13,6 +13,7 @@ "perf.dynamic_structure_manager.StructureManagerMixin", "bugfix.chunk_deadlock.ServerChunkCacheMixin", "perf.dedicated_reload_executor.MinecraftServerMixin", + "perf.fast_forge_dummies.NamespacedHolderHelperMixin", "perf.remove_biome_temperature_cache.BiomeMixin", "perf.reduce_blockstate_cache_rebuilds.GameDataMixin", "perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin", From d3bf2271fc3aa6ae3113476d9818222ab098e8dd Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 20:56:03 -0400 Subject: [PATCH 33/36] Avoid recreating tag ID strings --- .../core/config/ModernFixEarlyConfig.java | 1 + .../perf/tag_id_caching/TagEntryMixin.java | 30 +++++++++++++++++++ .../TagOrElementLocationMixin.java | 29 ++++++++++++++++++ src/main/resources/modernfix.mixins.json | 2 ++ 4 files changed, 62 insertions(+) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java 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 630d1f82..44fc0865 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -65,6 +65,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.scan_cache", true); this.addMixinRule("perf.kubejs", modPresent("kubejs")); this.addMixinRule("perf.flatten_model_predicates", true); + this.addMixinRule("perf.tag_id_caching", true); this.addMixinRule("perf.deduplicate_location", false); this.addMixinRule("perf.cache_blockstate_cache_arrays", true); this.addMixinRule("perf.cache_model_materials", true); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java new file mode 100644 index 00000000..2827fdb0 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java @@ -0,0 +1,30 @@ +package org.embeddedt.modernfix.mixin.perf.tag_id_caching; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagEntry; +import net.minecraft.util.ExtraCodecs; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(TagEntry.class) +public class TagEntryMixin { + @Shadow @Final private boolean tag; + @Shadow @Final private ResourceLocation id; + private ExtraCodecs.TagOrElementLocation cachedLoc; + + /** + * @author embeddedt + * @reason use cached location, overwrite rather than inject to avoid allocs + */ + @Overwrite + private ExtraCodecs.TagOrElementLocation elementOrTag() { + ExtraCodecs.TagOrElementLocation loc = cachedLoc; + if(loc == null) { + loc = new ExtraCodecs.TagOrElementLocation(this.id, this.tag); + cachedLoc = loc; + } + return loc; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java new file mode 100644 index 00000000..6e4c0b06 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java @@ -0,0 +1,29 @@ +package org.embeddedt.modernfix.mixin.perf.tag_id_caching; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.ExtraCodecs; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ExtraCodecs.TagOrElementLocation.class) +public class TagOrElementLocationMixin { + @Shadow @Final private boolean tag; + @Shadow @Final private ResourceLocation id; + private String cachedDecoratedId; + + /** + * @author embeddedt + * @reason use cached ID, overwrite rather than inject to avoid allocs + */ + @Overwrite + private String decoratedId() { + String id = cachedDecoratedId; + if(id == null) { + id = this.tag ? "#" + this.id : this.id.toString(); + cachedDecoratedId = id; + } + return id; + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 41a48386..d0cd4ebf 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -33,6 +33,8 @@ "perf.nbt_memory_usage.CompoundTagMixin", "perf.fast_registry_validation.ForgeRegistryMixin", "perf.kubejs.RecipeEventJSMixin", + "perf.tag_id_caching.TagEntryMixin", + "perf.tag_id_caching.TagOrElementLocationMixin", "perf.cache_strongholds.ChunkGeneratorMixin", "perf.cache_upgraded_structures.StructureManagerMixin", "perf.cache_strongholds.ServerLevelMixin", From 4a7bcbfd3877b91e7f7d558684eb60b86c8628e6 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 20:58:19 -0400 Subject: [PATCH 34/36] Remove reference to IForgeRegistryEntry --- .../perf/fast_forge_dummies/NamespacedHolderHelperMixin.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java index d1e586a8..0da4a052 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java @@ -3,7 +3,6 @@ package org.embeddedt.modernfix.mixin.perf.fast_forge_dummies; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.registries.IForgeRegistryEntry; import org.jetbrains.annotations.Nullable; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Final; @@ -17,7 +16,7 @@ import java.util.Map; import java.util.function.Function; @Mixin(targets = { "net/minecraftforge/registries/NamespacedHolderHelper" }) -public class NamespacedHolderHelperMixin> { +public class NamespacedHolderHelperMixin { @Shadow private Map> holdersByName; @Shadow @Final private @Nullable Function> holderLookup; From 57b7c4785a07766c2f0b2fc42f09e94610c321dc Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 20:59:49 -0400 Subject: [PATCH 35/36] Use correct access level for overwrites --- .../modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java | 2 +- .../mixin/perf/tag_id_caching/TagOrElementLocationMixin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java index 2827fdb0..82ffcda6 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java @@ -19,7 +19,7 @@ public class TagEntryMixin { * @reason use cached location, overwrite rather than inject to avoid allocs */ @Overwrite - private ExtraCodecs.TagOrElementLocation elementOrTag() { + public ExtraCodecs.TagOrElementLocation elementOrTag() { ExtraCodecs.TagOrElementLocation loc = cachedLoc; if(loc == null) { loc = new ExtraCodecs.TagOrElementLocation(this.id, this.tag); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java index 6e4c0b06..6b3e1d55 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java @@ -18,7 +18,7 @@ public class TagOrElementLocationMixin { * @reason use cached ID, overwrite rather than inject to avoid allocs */ @Overwrite - private String decoratedId() { + public String decoratedId() { String id = cachedDecoratedId; if(id == null) { id = this.tag ? "#" + this.id : this.id.toString(); From c861c99c7909e7562ca078d249fd993303ec72ad Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:03:38 -0400 Subject: [PATCH 36/36] Speed up FontManager loading --- .../core/config/ModernFixEarlyConfig.java | 1 + .../LegacyUnicodeBitmapsProviderMixin.java | 64 +++++++++++++++++++ src/main/resources/modernfix.mixins.json | 1 + 3 files changed, 66 insertions(+) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/faster_font_loading/LegacyUnicodeBitmapsProviderMixin.java 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 0f859f12..08bbeb4b 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -89,6 +89,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.async_locator", true); this.addMixinRule("perf.faster_texture_stitching", true); this.addMixinRule("perf.faster_texture_loading", true); + this.addMixinRule("perf.faster_font_loading", true); this.addMixinRule("perf.kubejs", modPresent("kubejs")); this.addMixinRule("perf.faster_singleplayer_load", false); /* Keep this off if JEI isn't installed to prevent breaking vanilla gameplay */ diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_font_loading/LegacyUnicodeBitmapsProviderMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_font_loading/LegacyUnicodeBitmapsProviderMixin.java new file mode 100644 index 00000000..beb327b7 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_font_loading/LegacyUnicodeBitmapsProviderMixin.java @@ -0,0 +1,64 @@ +package org.embeddedt.modernfix.mixin.perf.faster_font_loading; + +import com.mojang.blaze3d.platform.NativeImage; +import net.minecraft.client.gui.font.providers.LegacyUnicodeBitmapsProvider; +import net.minecraft.resources.ResourceLocation; +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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * Objective: avoid recomputing locations many times, as well as loading all the font sheets in the constructor + * only to do it again later. + */ +@Mixin(LegacyUnicodeBitmapsProvider.class) +public abstract class LegacyUnicodeBitmapsProviderMixin { + @Shadow protected abstract ResourceLocation getSheetLocation(int i); + + @Shadow @Final private Map textures; + private final ResourceLocation[] glyphLocations = new ResourceLocation[256]; + + private ResourceLocation currentCharIdx; + + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/font/providers/LegacyUnicodeBitmapsProvider;getSheetLocation(I)Lnet/minecraft/resources/ResourceLocation;")) + private ResourceLocation storeCurrentCharIdx(LegacyUnicodeBitmapsProvider provider, int i) { + ResourceLocation location = getSheetLocation(i); + currentCharIdx = location; + return location; + } + + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/NativeImage;read(Lcom/mojang/blaze3d/platform/NativeImage$Format;Ljava/io/InputStream;)Lcom/mojang/blaze3d/platform/NativeImage;")) + private NativeImage storeLoadedFontSheet(NativeImage.Format format, InputStream stream) throws IOException { + NativeImage image = NativeImage.read(format, stream); + textures.put(currentCharIdx, image); + return image; + } + + @Inject(method = "", at = @At("RETURN")) + private void clearLocation(CallbackInfo ci) { + currentCharIdx = null; + } + + @Inject(method = "getSheetLocation", at = @At("HEAD"), cancellable = true) + private void useCachedLocation(int idx, CallbackInfoReturnable cir) { + int cachedIdx = idx / 256; + if(cachedIdx >= 0 && cachedIdx < glyphLocations.length && glyphLocations[cachedIdx] != null) + cir.setReturnValue(glyphLocations[cachedIdx]); + } + + @Inject(method = "getSheetLocation", at = @At("RETURN")) + private void saveCachedLocation(int idx, CallbackInfoReturnable cir) { + int cachedIdx = idx / 256; + if(cachedIdx >= 0 && cachedIdx < glyphLocations.length) + glyphLocations[cachedIdx] = cir.getReturnValue(); + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 51765862..ddd419c7 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -113,6 +113,7 @@ "perf.cache_model_materials.MultipartMixin", "perf.faster_texture_stitching.StitcherMixin", "perf.faster_texture_loading.TextureAtlasMixin", + "perf.faster_font_loading.LegacyUnicodeBitmapsProviderMixin", "bugfix.packet_leak.ClientPlayNetHandlerMixin", "bugfix.packet_leak.SCustomPayloadPlayPacketMixin", "perf.reuse_datapacks.MinecraftMixin",