diff options
Diffstat (limited to 'versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin')
8 files changed, 474 insertions, 0 deletions
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ClassTransformer.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ClassTransformer.java new file mode 100644 index 0000000..52256ca --- /dev/null +++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ClassTransformer.java @@ -0,0 +1,102 @@ +package cc.polyfrost.oneconfig.internal.plugin.asm; + +import cc.polyfrost.oneconfig.internal.plugin.asm.tweakers.NanoVGGLConfigTransformer; +import cc.polyfrost.oneconfig.internal.plugin.asm.tweakers.VigilantTransformer; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import net.minecraft.launchwrapper.IClassTransformer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.ClassNode; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collection; + +/** + * Taken from LWJGLTwoPointFive under The Unlicense + * <a href="https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/">https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/</a> + * <p>also half taken from asmworkspace by asbyth ty</p> + */ +@SuppressWarnings("unused") +public class ClassTransformer implements IClassTransformer { + private static final Logger logger = LogManager.getLogger("OneConfig ASM"); + private final Multimap<String, ITransformer> transformerMap = ArrayListMultimap.create(); + private static final boolean outputBytecode = Boolean.parseBoolean(System.getProperty("debugBytecode", "false")); + + public ClassTransformer() { + registerTransformer(new NanoVGGLConfigTransformer()); + registerTransformer(new VigilantTransformer()); + } + + private void registerTransformer(ITransformer transformer) { + // loop through names of classes + for (String cls : transformer.getClassName()) { + // put the classes into the transformer map + transformerMap.put(cls, transformer); + } + } + + @Override + public byte[] transform(String name, String transformedName, byte[] bytes) { + if (bytes == null) return null; + + Collection<ITransformer> transformers = transformerMap.get(transformedName); + if (transformers.isEmpty()) return bytes; + + ClassReader reader = new ClassReader(bytes); + ClassNode node = new ClassNode(); + reader.accept(node, ClassReader.EXPAND_FRAMES); + + for (ITransformer transformer : transformers) { + transformer.transform(transformedName, node); + } + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + try { + node.accept(cw); + } catch (Throwable t) { + logger.error("Exception when transforming " + transformedName + " : " + t.getClass().getSimpleName()); + t.printStackTrace(); + } + + if (outputBytecode) { + File bytecodeDirectory = new File("bytecode"); + String transformedClassName; + + // anonymous classes + if (transformedName.contains("$")) { + transformedClassName = transformedName.replace('$', '.') + ".class"; + } else { + transformedClassName = transformedName + ".class"; + } + + if (!bytecodeDirectory.exists()) { + bytecodeDirectory.mkdirs(); + } + + File bytecodeOutput = new File(bytecodeDirectory, transformedClassName); + + try { + if (!bytecodeOutput.exists()) { + bytecodeOutput.createNewFile(); + } + } catch (Exception e) { + e.printStackTrace(); + } + + try (FileOutputStream os = new FileOutputStream(bytecodeOutput)) { + // write to the generated class to /run/bytecode/classfile.class + // with the class bytes from transforming + os.write(cw.toByteArray()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return cw.toByteArray(); + } +}
\ No newline at end of file diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ITransformer.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ITransformer.java new file mode 100644 index 0000000..1bc50d1 --- /dev/null +++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ITransformer.java @@ -0,0 +1,24 @@ +package cc.polyfrost.oneconfig.internal.plugin.asm; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +public interface ITransformer { + String[] getClassName(); + + void transform(String transformedName, ClassNode node); + + default void clearInstructions(MethodNode methodNode) { + methodNode.instructions.clear(); + + // dont waste time clearing local variables if they're empty + if (!methodNode.localVariables.isEmpty()) { + methodNode.localVariables.clear(); + } + + // dont waste time clearing try-catches if they're empty + if (!methodNode.tryCatchBlocks.isEmpty()) { + methodNode.tryCatchBlocks.clear(); + } + } +} diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/OneConfigTweaker.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/OneConfigTweaker.java new file mode 100644 index 0000000..d302b9d --- /dev/null +++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/OneConfigTweaker.java @@ -0,0 +1,117 @@ +package cc.polyfrost.oneconfig.internal.plugin.asm; + +import cc.polyfrost.oneconfig.internal.init.OneConfigInit; +import net.minecraft.launchwrapper.ITweaker; +import net.minecraft.launchwrapper.Launch; +import net.minecraft.launchwrapper.LaunchClassLoader; +import net.minecraftforge.fml.relauncher.CoreModManager; +import org.spongepowered.asm.launch.MixinBootstrap; +import org.spongepowered.asm.launch.MixinTweaker; +import org.spongepowered.asm.mixin.Mixins; + +import java.io.File; +import java.lang.reflect.Field; +import java.net.URI; +import java.net.URL; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarFile; + +public class OneConfigTweaker implements ITweaker { + + public OneConfigTweaker() { + for (URL url : Launch.classLoader.getSources()) { + doMagicMixinStuff(url); + } + } + + private void doMagicMixinStuff(URL url) { + try { + URI uri = url.toURI(); + if (Objects.equals(uri.getScheme(), "file")) { + File file = new File(uri); + if (file.exists() && file.isFile()) { + try (JarFile jarFile = new JarFile(file)) { + if (jarFile.getManifest() != null) { + Attributes attributes = jarFile.getManifest().getMainAttributes(); + String tweakerClass = attributes.getValue("TweakClass"); + if (Objects.equals(tweakerClass, "cc.polyfrost.oneconfigwrapper.OneConfigWrapper")) { + CoreModManager.getIgnoredMods().remove(file.getName()); + CoreModManager.getReparseableCoremods().add(file.getName()); + String mixinConfig = attributes.getValue("MixinConfigs"); + if (mixinConfig != null) { + try { + try { + List<String> tweakClasses = (List<String>) Launch.blackboard.get("TweakClasses"); // tweak classes before other mod trolling + if (tweakClasses.contains("org.spongepowered.asm.launch.MixinTweaker")) { // if there's already a mixin tweaker, we'll just load it like "usual" + new MixinTweaker(); // also we might not need to make a new mixin tweawker all the time but im just making sure + } else if (!Launch.blackboard.containsKey("mixin.initialised")) { // if there isnt, we do our own trolling + List<ITweaker> tweaks = (List<ITweaker>) Launch.blackboard.get("Tweaks"); + tweaks.add(new MixinTweaker()); + } + } catch (Exception ignored) { + // if it fails i *think* we can just ignore it + } + MixinBootstrap.getPlatform().addContainer(uri); + } catch (Exception ignored) { + + } + } + } + } + } + } + } + } catch (Exception ignored) { + + } + } + + @Override + public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) { + MixinBootstrap.init(); + boolean captureNext = false; + for (String arg : args) { + if (captureNext) { + Mixins.addConfiguration(arg); + } + captureNext = "--mixin".equals(arg); + } + } + + @Override + public void injectIntoClassLoader(LaunchClassLoader classLoader) { + removeLWJGLException(); + Launch.classLoader.registerTransformer(ClassTransformer.class.getName()); + OneConfigInit.initialize(new String[]{}); + Launch.blackboard.put("oneconfig.init.initialized", true); + Launch.classLoader.addClassLoaderExclusion("cc.polyfrost.oneconfig.internal.plugin.asm."); + } + + /** + * Taken from LWJGLTwoPointFive under The Unlicense + * <a href="https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/">https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/</a> + */ + private void removeLWJGLException() { + try { + Field f_exceptions = LaunchClassLoader.class.getDeclaredField("classLoaderExceptions"); + f_exceptions.setAccessible(true); + Set<String> exceptions = (Set<String>) f_exceptions.get(Launch.classLoader); + exceptions.remove("org.lwjgl."); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public String getLaunchTarget() { + return null; + } + + @Override + public String[] getLaunchArguments() { + return new String[0]; + } +}
\ No newline at end of file diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/NanoVGGLConfigTransformer.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/NanoVGGLConfigTransformer.java new file mode 100644 index 0000000..bbbf4a1 --- /dev/null +++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/NanoVGGLConfigTransformer.java @@ -0,0 +1,47 @@ +package cc.polyfrost.oneconfig.internal.plugin.asm.tweakers; + +import cc.polyfrost.oneconfig.internal.plugin.asm.ITransformer; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.*; + +/** + * Taken from LWJGLTwoPointFive under The Unlicense + * <a href="https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/">https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/</a> + */ +public class NanoVGGLConfigTransformer implements ITransformer { + @Override + public String[] getClassName() { + return new String[]{"org.lwjgl.nanovg.NanoVGGLConfig"}; + } + + @Override + public void transform(String transformedName, ClassNode node) { + for (MethodNode method : node.methods) { + if (method.name.equals("configGL")) { + InsnList list = new InsnList(); + + list.add(new VarInsnNode(Opcodes.LLOAD, 0)); + list.add(new TypeInsnNode(Opcodes.NEW, "cc/polyfrost/oneconfig/internal/plugin/hooks/Lwjgl2FunctionProvider")); + list.add(new InsnNode(Opcodes.DUP)); + list.add(new MethodInsnNode( + Opcodes.INVOKESPECIAL, + "cc/polyfrost/oneconfig/internal/plugin/hooks/Lwjgl2FunctionProvider", + "<init>", + "()V", + false + )); + list.add(new MethodInsnNode( + Opcodes.INVOKESTATIC, + "org/lwjgl/nanovg/NanoVGGLConfig", + "config", + "(JLorg/lwjgl/system/FunctionProvider;)V", + false + )); + list.add(new InsnNode(Opcodes.RETURN)); + + clearInstructions(method); + method.instructions.insert(list); + } + } + } +} diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/VigilantTransformer.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/VigilantTransformer.java new file mode 100644 index 0000000..8dd60cf --- /dev/null +++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/VigilantTransformer.java @@ -0,0 +1,89 @@ +package cc.polyfrost.oneconfig.internal.plugin.asm.tweakers; + +import cc.polyfrost.oneconfig.internal.plugin.asm.ITransformer; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.*; + +import java.io.File; + +public class VigilantTransformer implements ITransformer { + + @Override + public String[] getClassName() { + return new String[]{"gg.essential.vigilance.Vigilant"}; + } + + /** + * If anything here is changed, edit the corresponding method in OneConfigMixinPlugin! + */ + @Override + public void transform(String transformedName, ClassNode node) { + if (!node.interfaces.contains("cc/polyfrost/oneconfig/config/compatibility/vigilance/VigilantAccessor")) { + node.fields.add(new FieldNode(Opcodes.ACC_PUBLIC, "oneconfig$config", "Lcc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig;", null, null)); + node.fields.add(new FieldNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "oneconfig$file", Type.getDescriptor(File.class), null, null)); + + node.interfaces.add("cc/polyfrost/oneconfig/config/compatibility/vigilance/VigilantAccessor"); + MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC, "getPropertyCollector", "()Lgg/essential/vigilance/data/PropertyCollector;", null, null); + LabelNode labelNode = new LabelNode(); + methodNode.instructions.add(labelNode); + methodNode.instructions.add(new LineNumberNode(421421, labelNode)); + methodNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, "gg/essential/vigilance/Vigilant", "propertyCollector", "Lgg/essential/vigilance/data/PropertyCollector;")); + methodNode.instructions.add(new InsnNode(Opcodes.ARETURN)); + node.methods.add(methodNode); + + MethodNode methodNode2 = new MethodNode(Opcodes.ACC_PUBLIC, "handleOneConfigDependency", "(Lgg/essential/vigilance/data/PropertyData;Lgg/essential/vigilance/data/PropertyData;)V", null, null); + LabelNode labelNode2 = new LabelNode(); + LabelNode labelNode3 = new LabelNode(); + LabelNode labelNode4 = new LabelNode(); + methodNode2.instructions.add(labelNode2); + methodNode2.instructions.add(new LineNumberNode(15636436, labelNode2)); + methodNode2.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode2.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, "gg/essential/vigilance/Vigilant", "oneconfig$config", "Lcc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig;")); + + methodNode2.instructions.add(new JumpInsnNode(Opcodes.IFNULL, labelNode4)); + + methodNode2.instructions.add(labelNode3); + methodNode2.instructions.add(new LineNumberNode(15636437, labelNode3)); + methodNode2.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode2.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, "gg/essential/vigilance/Vigilant", "oneconfig$config", "Lcc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig;")); + methodNode2.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); + methodNode2.instructions.add(new VarInsnNode(Opcodes.ALOAD, 2)); + methodNode2.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "cc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig", "addDependency", "(Lgg/essential/vigilance/data/PropertyData;Lgg/essential/vigilance/data/PropertyData;)V", false)); + + methodNode2.instructions.add(labelNode4); + methodNode2.instructions.add(new LineNumberNode(15636438, labelNode4)); + methodNode2.instructions.add(new InsnNode(Opcodes.RETURN)); + node.methods.add(methodNode2); + + for (MethodNode method : node.methods) { + if (method.name.equals("initialize")) { + InsnList list = new InsnList(); + list.add(new VarInsnNode(Opcodes.ALOAD, 0)); + list.add(new VarInsnNode(Opcodes.ALOAD, 0)); + list.add(new VarInsnNode(Opcodes.ALOAD, 0)); + list.add(new FieldInsnNode(Opcodes.GETFIELD, "gg/essential/vigilance/Vigilant", "oneconfig$file", Type.getDescriptor(File.class))); + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "cc/polyfrost/oneconfig/internal/plugin/hooks/VigilantHook", "returnNewConfig", "(Lgg/essential/vigilance/Vigilant;Ljava/io/File;)Lcc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig;", false)); + list.add(new FieldInsnNode(Opcodes.PUTFIELD, "gg/essential/vigilance/Vigilant", "oneconfig$config", "Lcc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig;")); + method.instructions.insertBefore(method.instructions.getLast().getPrevious(), list); + } else if (method.name.equals("addDependency") && method.desc.equals("(Lgg/essential/vigilance/data/PropertyData;Lgg/essential/vigilance/data/PropertyData;)V")) { + InsnList list = new InsnList(); + + list.add(new VarInsnNode(Opcodes.ALOAD, 0)); + list.add(new VarInsnNode(Opcodes.ALOAD, 1)); + list.add(new VarInsnNode(Opcodes.ALOAD, 2)); + list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "gg/essential/vigilance/Vigilant", "handleOneConfigDependency", "(Lgg/essential/vigilance/data/PropertyData;Lgg/essential/vigilance/data/PropertyData;)V", false)); + + method.instructions.insertBefore(method.instructions.getLast().getPrevious(), list); + } else if (method.name.equals("<init>") && method.desc.equals("(Ljava/io/File;Ljava/lang/String;Lgg/essential/vigilance/data/PropertyCollector;Lgg/essential/vigilance/data/SortingBehavior;)V")) { + InsnList list = new InsnList(); + list.add(new VarInsnNode(Opcodes.ALOAD, 0)); + list.add(new VarInsnNode(Opcodes.ALOAD, 1)); + list.add(new FieldInsnNode(Opcodes.PUTFIELD, "gg/essential/vigilance/Vigilant", "oneconfig$file", Type.getDescriptor(File.class))); + method.instructions.insertBefore(method.instructions.getLast().getPrevious(), list); + } + } + } + } +} diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/Lwjgl2FunctionProvider.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/Lwjgl2FunctionProvider.java new file mode 100644 index 0000000..297c84d --- /dev/null +++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/Lwjgl2FunctionProvider.java @@ -0,0 +1,47 @@ +package cc.polyfrost.oneconfig.internal.plugin.hooks; + +import org.lwjgl.system.FunctionProvider; + +import java.lang.reflect.Method; +import java.nio.ByteBuffer; + +/** + * Taken from LWJGLTwoPointFive under The Unlicense + * <a href="https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/">https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/</a> + */ +public class Lwjgl2FunctionProvider implements FunctionProvider { + + private static final Class<?> GLContext; + private final Method m_getFunctionAddress; + + static { + try { + GLContext = Class.forName("org.lwjgl.opengl.GLContext"); + } catch (Throwable t) { + throw new RuntimeException("Unable to initialize LWJGL2FunctionProvider", t); + } + } + + public Lwjgl2FunctionProvider() { + try { + m_getFunctionAddress = GLContext.getDeclaredMethod("getFunctionAddress", String.class); + m_getFunctionAddress.setAccessible(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public long getFunctionAddress(CharSequence functionName) { + try { + return (long) m_getFunctionAddress.invoke(null, functionName.toString()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public long getFunctionAddress(ByteBuffer byteBuffer) { + throw new UnsupportedOperationException(); + } +}
\ No newline at end of file diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/OptifineConfigHook.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/OptifineConfigHook.java new file mode 100644 index 0000000..dea68cf --- /dev/null +++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/OptifineConfigHook.java @@ -0,0 +1,22 @@ +package cc.polyfrost.oneconfig.internal.plugin.hooks; + +import cc.polyfrost.oneconfig.gui.OneConfigGui; +import cc.polyfrost.oneconfig.platform.Platform; +import cc.polyfrost.oneconfig.utils.gui.GuiUtils; + +import java.util.Optional; + +public class OptifineConfigHook { + + public static boolean shouldNotApplyFastRender() { + if (Platform.getGuiPlatform().getCurrentScreen() instanceof OneConfigGui) { + return true; + } + for (Optional screen : GuiUtils.getScreenQueue()) { + if (screen.isPresent() && screen.get() instanceof OneConfigGui) { + return true; + } + } + return false; + } +} diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/VigilantHook.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/VigilantHook.java new file mode 100644 index 0000000..a012773 --- /dev/null +++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/VigilantHook.java @@ -0,0 +1,26 @@ +package cc.polyfrost.oneconfig.internal.plugin.hooks; + +import cc.polyfrost.oneconfig.config.compatibility.vigilance.VigilanceConfig; +import cc.polyfrost.oneconfig.config.data.Mod; +import cc.polyfrost.oneconfig.config.data.ModType; +import cc.polyfrost.oneconfig.internal.config.core.ConfigCore; +import cc.polyfrost.oneconfig.platform.Platform; +import gg.essential.vigilance.Vigilant; + +import java.io.File; + +@SuppressWarnings("unused") +public class VigilantHook { + public static VigilanceConfig returnNewConfig(Vigilant vigilant, File file) { + if (vigilant != null && Platform.getInstance().isCallingFromMinecraftThread()) { + String name = !vigilant.getGuiTitle().equals("Settings") ? vigilant.getGuiTitle() : !Platform.getLoaderPlatform().hasActiveModContainer() ? "Unknown" : Platform.getLoaderPlatform().getActiveModContainer().name; + if (name.equals("OneConfig")) name = "Essential"; + String finalName = name; + // duplicate fix + if (ConfigCore.mods.stream().anyMatch(mod -> mod.name.equals(finalName))) return null; + return new VigilanceConfig(new Mod(name, ModType.THIRD_PARTY), file.getAbsolutePath(), vigilant); + } else { + return null; + } + } +} |