From e72e5cc523f5186ac42fb9fcda9ea837a142240a Mon Sep 17 00:00:00 2001 From: Alexdoru <57050655+Alexdoru@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:14:52 +0100 Subject: [PATCH 1/4] rearrange class --- .../lib/asm/ClassHeirachyManager.java | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java b/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java index 88e8291..1c255c3 100644 --- a/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java +++ b/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java @@ -6,7 +6,6 @@ import net.minecraft.launchwrapper.IClassTransformer; import net.minecraft.launchwrapper.Launch; -import net.minecraft.launchwrapper.LaunchClassLoader; import org.objectweb.asm.tree.ClassNode; @@ -18,32 +17,7 @@ */ public class ClassHeirachyManager implements IClassTransformer { - public static class SuperCache { - - String superclass; - public HashSet parents = new HashSet(); - private boolean flattened; - - public void add(String parent) { - parents.add(parent); - } - - public void flatten() { - if (flattened) return; - - for (String s : new ArrayList(parents)) { - SuperCache c = declareClass(s); - if (c != null) { - c.flatten(); - parents.addAll(c.parents); - } - } - flattened = true; - } - } - - public static HashMap superclasses = new HashMap(); - private static LaunchClassLoader cl = Launch.classLoader; + public static final HashMap superclasses = new HashMap<>(); public static String toKey(String name) { if (ObfMapping.obfuscated) @@ -83,7 +57,7 @@ private static SuperCache declareClass(String name) { if (cache != null) return cache; try { - byte[] bytes = cl.getClassBytes(unKey(name)); + byte[] bytes = Launch.classLoader.getClassBytes(unKey(name)); if (bytes != null) cache = declareASM(bytes); } catch (Exception e) {} @@ -147,4 +121,27 @@ public static String getSuperClass(String name, boolean runtime) { if (!runtime) s = FMLDeobfuscatingRemapper.INSTANCE.unmap(s); return s; } + + public static class SuperCache { + + String superclass; + public HashSet parents = new HashSet<>(); + private boolean flattened; + + public void add(String parent) { + parents.add(parent); + } + + public void flatten() { + if (flattened) return; + for (String s : new ArrayList<>(parents)) { + SuperCache c = declareClass(s); + if (c != null) { + c.flatten(); + parents.addAll(c.parents); + } + } + flattened = true; + } + } } From 69e2252c4b743f16c9abfb9fe574810131ae476f Mon Sep 17 00:00:00 2001 From: Alexdoru <57050655+Alexdoru@users.noreply.github.com> Date: Thu, 4 Dec 2025 22:18:54 +0100 Subject: [PATCH 2/4] use ClassReader.SKIP_CODE to speed up things --- .../codechicken/lib/asm/ClassHeirachyManager.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java b/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java index 1c255c3..8f85e6e 100644 --- a/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java +++ b/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java @@ -7,6 +7,7 @@ import net.minecraft.launchwrapper.IClassTransformer; import net.minecraft.launchwrapper.Launch; +import org.objectweb.asm.ClassReader; import org.objectweb.asm.tree.ClassNode; import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; @@ -85,13 +86,15 @@ private static SuperCache declareReflection(String name) throws ClassNotFoundExc } private static SuperCache declareASM(byte[] bytes) { - ClassNode node = ASMHelper.createClassNode(bytes); + ClassNode node = ASMHelper.createClassNode(bytes,ClassReader.SKIP_CODE); String name = toKey(node.name); SuperCache cache = getOrCreateCache(name); cache.superclass = toKey(node.superName.replace('/', '.')); cache.add(cache.superclass); - for (String iclass : node.interfaces) cache.add(toKey(iclass.replace('/', '.'))); + for (String iclass : node.interfaces) { + cache.add(toKey(iclass.replace('/', '.'))); + } return cache; } @@ -100,7 +103,9 @@ private static SuperCache declareASM(byte[] bytes) { public byte[] transform(String name, String tname, byte[] bytes) { if (bytes == null) return null; - if (!superclasses.containsKey(tname)) declareASM(bytes); + if (!superclasses.containsKey(tname)) { + declareASM(bytes); + } return bytes; } From 10570b4ac639b84743b63b528b2d896b937641d2 Mon Sep 17 00:00:00 2001 From: Alexdoru <57050655+Alexdoru@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:56:31 +0100 Subject: [PATCH 3/4] update bs --- gradle.properties | 10 +++++++++- settings.gradle | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 60abdc1..c0aecb5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -90,7 +90,9 @@ usesMixinDebug = false # Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise. mixinPlugin = -# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail! +# Specify the package that contains all of your Mixins. The package must exist or +# the build will fail. If you have a package property defined in your mixins..json, +# it must match with this or the build will fail. mixinsPackage = # Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin! @@ -166,6 +168,12 @@ curseForgeRelations = # projects. New projects should not use this parameter. # customArchiveBaseName = +# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory. +# runClientWorkingDirectory = run/client + +# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory. +# runServerWorkingDirectory = run/server + # Optional parameter to have the build automatically fail if an illegal version is used. # This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'. # The check is ONLY performed if the version is a git tag. diff --git a/settings.gradle b/settings.gradle index 7622cde..e883289 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,7 +17,7 @@ pluginManagement { } plugins { - id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.41' + id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.48' } From 3fc1607109e9c874a6823769b19b7f6781a728ad Mon Sep 17 00:00:00 2001 From: Alexdoru <57050655+Alexdoru@users.noreply.github.com> Date: Fri, 5 Dec 2025 20:29:17 +0100 Subject: [PATCH 4/4] reduce memory footprint of ClassHeirachyManager --- .../lib/asm/ClassHeirachyManager.java | 215 ++++++++++-------- 1 file changed, 118 insertions(+), 97 deletions(-) diff --git a/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java b/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java index 8f85e6e..f14932d 100644 --- a/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java +++ b/src/main/java/codechicken/lib/asm/ClassHeirachyManager.java @@ -1,8 +1,7 @@ package codechicken.lib.asm; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import net.minecraft.launchwrapper.IClassTransformer; import net.minecraft.launchwrapper.Launch; @@ -18,135 +17,157 @@ */ public class ClassHeirachyManager implements IClassTransformer { - public static final HashMap superclasses = new HashMap<>(); + private static final Map superclasses = new ConcurrentHashMap<>(1000); - public static String toKey(String name) { - if (ObfMapping.obfuscated) - name = FMLDeobfuscatingRemapper.INSTANCE.map(name.replace('.', '/')).replace('/', '.'); - return name; + static { + superclasses.put("java.lang.Object", ClassInfo.OBJECT); } - public static String unKey(String name) { - if (ObfMapping.obfuscated) - name = FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace('.', '/')).replace('/', '.'); - return name; + @Override + public byte[] transform(String name, String transformedName, byte[] bytes) { + if (bytes == null) return null; + if (!superclasses.containsKey(transformedName)) { + declareASM(transformedName, bytes); + } + return bytes; } - /** - * @param name The class in question - * @param superclass The class being extended - * @return true if clazz extends, either directly or indirectly, superclass. - */ - public static boolean classExtends(String name, String superclass) { - name = toKey(name); - superclass = toKey(superclass); - - if (name.equals(superclass)) return true; - - SuperCache cache = declareClass(name); - if (cache == null) // just can't handle this - return false; - - cache.flatten(); - return cache.parents.contains(superclass); + private static ClassInfo declareASM(String name, byte[] bytes) { + final ClassNode node = ASMHelper.createClassNode(bytes, ClassReader.SKIP_CODE); + final ClassInfo classInfo = new ClassInfo(node); + superclasses.put(name, classInfo); + return classInfo; } - private static SuperCache declareClass(String name) { - name = toKey(name); - SuperCache cache = superclasses.get(name); - - if (cache != null) return cache; + private static ClassInfo declareReflection(String name) throws ClassNotFoundException { + final Class aclass = Class.forName(name); + final ClassInfo classInfo = new ClassInfo(aclass); + superclasses.put(name, classInfo); + return classInfo; + } + private static ClassInfo declareClass(String name) { try { byte[] bytes = Launch.classLoader.getClassBytes(unKey(name)); - if (bytes != null) cache = declareASM(bytes); - } catch (Exception e) {} - - if (cache != null) return cache; - + if (bytes != null) { + return declareASM(name, bytes); + } + } catch (Exception ignored) {} try { - cache = declareReflection(name); - } catch (ClassNotFoundException e) {} - - return cache; + return declareReflection(name); + } catch (ClassNotFoundException ignored) {} + return null; } - private static SuperCache declareReflection(String name) throws ClassNotFoundException { - Class aclass = Class.forName(name); - - SuperCache cache = getOrCreateCache(name); - if (aclass.isInterface()) cache.superclass = "java.lang.Object"; - else if (name.equals("java.lang.Object")) return cache; - else cache.superclass = toKey(aclass.getSuperclass().getName()); - - cache.add(cache.superclass); - for (Class iclass : aclass.getInterfaces()) cache.add(toKey(iclass.getName())); - - return cache; + private static String toKey(String name) { + if (ObfMapping.obfuscated) { + return FMLDeobfuscatingRemapper.INSTANCE.map(name.replace('.', '/')).replace('/', '.'); + } + return name.replace('/', '.'); } - private static SuperCache declareASM(byte[] bytes) { - ClassNode node = ASMHelper.createClassNode(bytes,ClassReader.SKIP_CODE); - String name = toKey(node.name); - - SuperCache cache = getOrCreateCache(name); - cache.superclass = toKey(node.superName.replace('/', '.')); - cache.add(cache.superclass); - for (String iclass : node.interfaces) { - cache.add(toKey(iclass.replace('/', '.'))); + private static String unKey(String name) { + if (ObfMapping.obfuscated) { + return FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace('.', '/')).replace('/', '.'); } - - return cache; + return name.replace('/', '.'); } - @Override - public byte[] transform(String name, String tname, byte[] bytes) { - if (bytes == null) return null; + /** + * @param name The class in question + * @param superclass The class being extended + * @return true if clazz extends, either directly or indirectly, superclass. + */ + public static boolean classExtends(String name, String superclass) { + name = toKey(name); + superclass = toKey(superclass); - if (!superclasses.containsKey(tname)) { - declareASM(bytes); - } + if (name.equals(superclass)) return true; - return bytes; - } + ClassInfo classInfo = superclasses.get(name); + if (classInfo == null) classInfo = declareClass(name); + if (classInfo == null) return false; - public static SuperCache getOrCreateCache(String name) { - SuperCache cache = superclasses.get(name); - if (cache == null) superclasses.put(name, cache = new SuperCache()); - return cache; + return classInfo.hasSuper(superclass); } public static String getSuperClass(String name, boolean runtime) { name = toKey(name); - SuperCache cache = declareClass(name); - if (cache == null) return "java.lang.Object"; - cache.flatten(); - String s = cache.superclass; - if (!runtime) s = FMLDeobfuscatingRemapper.INSTANCE.unmap(s); + ClassInfo classInfo = superclasses.get(name); + if (classInfo == null) classInfo = declareClass(name); + if (classInfo == null || classInfo.superclass == null) return "java.lang.Object"; + + final String s = classInfo.superclass; + if (!runtime) { + return FMLDeobfuscatingRemapper.INSTANCE.unmap(s); + } return s; } - public static class SuperCache { + private static class ClassInfo { + + public static final ClassInfo OBJECT = new ClassInfo(); - String superclass; - public HashSet parents = new HashSet<>(); - private boolean flattened; + public final String superclass; + public final String[] interfaces; + public ClassInfo parent; - public void add(String parent) { - parents.add(parent); + private ClassInfo() { + this.superclass = null; + this.interfaces = null; } - public void flatten() { - if (flattened) return; - for (String s : new ArrayList<>(parents)) { - SuperCache c = declareClass(s); - if (c != null) { - c.flatten(); - parents.addAll(c.parents); + public ClassInfo(ClassNode node) { + if ("java/lang/Object".equals(node.superName)) { + superclass = "java.lang.Object"; + parent = OBJECT; + } else { + superclass = toKey(node.superName); + } + if (node.interfaces.isEmpty()) { + interfaces = null; + } else { + interfaces = new String[node.interfaces.size()]; + for (int i = 0; i < node.interfaces.size(); i++) { + interfaces[i] = toKey(node.interfaces.get(i)); } } - flattened = true; + } + + public ClassInfo(Class aclass) { + if (aclass.isInterface()) { + this.superclass = "java.lang.Object"; + this.parent = OBJECT; + } else { + final Class superclass = aclass.getSuperclass(); + if (superclass == null || "java.lang.Object".equals(superclass.getName())) { + this.superclass = "java.lang.Object"; + this.parent = OBJECT; + } else { + this.superclass = toKey(superclass.getName()); + } + } + final Class[] interfaces = aclass.getInterfaces(); + if (interfaces.length == 0) { + this.interfaces = null; + } else { + this.interfaces = new String[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + this.interfaces[i] = toKey(interfaces[i].getName()); + } + } + } + + public boolean hasSuper(String superclass) { + if (this.superclass == null) return false; + if (this.superclass.equals(superclass)) return true; + ClassInfo parentInfo = this.parent; + if (parentInfo == null) parentInfo = superclasses.get(this.superclass); + if (parentInfo == null) parentInfo = declareClass(this.superclass); + if (parentInfo == null) return false; + this.parent = parentInfo; + return parentInfo.hasSuper(superclass); } } }