From 5c8c3e81a891da36fa752d5f67886bc02ed504dd Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Thu, 7 Mar 2024 21:55:09 +0100 Subject: Fix wrong mod loader warning on modern versions of minecraft (#1030) --- build.gradle.kts | 16 ++- .../notenoughupdates/envcheck/EnvironmentScan.java | 1 + .../loader/ClassLoaderExtUtil.java | 37 ++++++ .../notenoughupdates/loader/JARLoadingTweaker.java | 132 +++++++++++++++++++ .../loader/KotlinLoadingTweaker.java | 139 ++++++--------------- .../loader/MixinLoadingTweaker.java | 47 +++++++ .../loader/NEUDelegatingTweaker.java | 3 +- src/main/resources/META-INF/mods.toml | 2 - 8 files changed, 269 insertions(+), 108 deletions(-) create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/loader/ClassLoaderExtUtil.java create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/loader/JARLoadingTweaker.java create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/loader/MixinLoadingTweaker.java diff --git a/build.gradle.kts b/build.gradle.kts index 3ed5d4e4..9c859169 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -105,6 +105,10 @@ val kotlinDependencies: Configuration by configurations.creating { configurations.implementation.get().extendsFrom(this) } +val mixinRTDependencies: Configuration by configurations.creating { + configurations.implementation.get().extendsFrom(this) +} + val oneconfigQuarantineSourceSet: SourceSet = sourceSets.create("oneconfig") { java { srcDir(layout.projectDirectory.dir("src/main/oneconfig")) @@ -148,7 +152,7 @@ dependencies { shadowImplementation("com.mojang:brigadier:1.0.18") - shadowImplementation("org.spongepowered:mixin:0.7.11-SNAPSHOT") { + mixinRTDependencies("org.spongepowered:mixin:0.7.11-SNAPSHOT") { isTransitive = false // Dependencies of mixin are already bundled by minecraft } annotationProcessor("net.fabricmc:sponge-mixin:0.11.4+mixin.0.8.5") @@ -224,10 +228,16 @@ tasks.remapSourcesJar { // Use Zip instead of Jar as to not include META-INF val kotlinDependencyCollectionJar by tasks.creating(Zip::class) { archiveFileName.set("kotlin-libraries-wrapped.jar") - destinationDirectory.set(project.layout.buildDirectory.dir("kotlinwrapper")) + destinationDirectory.set(project.layout.buildDirectory.dir("wrapperjars")) from(kotlinDependencies) into("neu-kotlin-libraries-wrapped") } +val mixinDependencyCollectionJar by tasks.creating(Zip::class) { + archiveFileName.set("mixin-libraries-wrapped.jar") + destinationDirectory.set(project.layout.buildDirectory.dir("wrapperjars")) + from(mixinRTDependencies) + into("neu-mixin-libraries-wrapped") +} tasks.register("includeBackupRepo") { doLast { @@ -257,7 +267,9 @@ tasks.shadowJar { } from(oneconfigQuarantineSourceSet.output) from(kotlinDependencyCollectionJar) + from(mixinDependencyCollectionJar) dependsOn(kotlinDependencyCollectionJar) + dependsOn(mixinDependencyCollectionJar) fun relocate(name: String) = relocate(name, "io.github.moulberry.notenoughupdates.deps.$name") relocate("com.mojang.brigadier") relocate("io.github.moulberry.moulconfig") diff --git a/src/main/java/io/github/moulberry/notenoughupdates/envcheck/EnvironmentScan.java b/src/main/java/io/github/moulberry/notenoughupdates/envcheck/EnvironmentScan.java index 1ae5199a..bedbc87b 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/envcheck/EnvironmentScan.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/envcheck/EnvironmentScan.java @@ -78,6 +78,7 @@ public class EnvironmentScan { public static void showErrorMessage(String... messages) { String message = String.join("\n", messages); + System.setProperty("java.awt.headless", "false"); JOptionPane.showMessageDialog( null, message, "NotEnoughUpdates - Problematic System Configuration", JOptionPane.ERROR_MESSAGE ); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/loader/ClassLoaderExtUtil.java b/src/main/java/io/github/moulberry/notenoughupdates/loader/ClassLoaderExtUtil.java new file mode 100644 index 00000000..4c410b3d --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/loader/ClassLoaderExtUtil.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see . + */ + +package io.github.moulberry.notenoughupdates.loader; + +import net.minecraft.launchwrapper.LaunchClassLoader; + +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; + +class ClassLoaderExtUtil { + public static void addClassSourceTwice(LaunchClassLoader classLoader, URL url) throws Exception { + classLoader.addURL(url); + ClassLoader classLoaderYetAgain = classLoader.getClass().getClassLoader(); + Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + addURL.setAccessible(true); + addURL.invoke(classLoaderYetAgain, url); + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/loader/JARLoadingTweaker.java b/src/main/java/io/github/moulberry/notenoughupdates/loader/JARLoadingTweaker.java new file mode 100644 index 00000000..abd4712b --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/loader/JARLoadingTweaker.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2024 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see . + */ + +package io.github.moulberry.notenoughupdates.loader; + +import lombok.SneakyThrows; +import net.minecraft.launchwrapper.ITweaker; +import net.minecraft.launchwrapper.Launch; +import net.minecraft.launchwrapper.LaunchClassLoader; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * Tweaker used to load library JARs conditionally. See the subclasses for concrete implementations. + */ +public abstract class JARLoadingTweaker implements ITweaker { + + /** + * @return a path pointing to a folder filled with JAR files + */ + protected abstract Path getFilesToLoad(); + + protected abstract String getTestClass(); + + @SneakyThrows + protected @Nullable Path getShadowedElement(String path) { + URI uri = Objects.requireNonNull(getClass().getResource(path)).toURI(); + if ("jar".equals(uri.getScheme())) { + FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap()); + closeFileSystemLater(fs); + return fs.getPath(path); + } else { + return Paths.get(uri); + } + } + + private List toClose = new ArrayList<>(); + + protected void closeFileSystemLater(FileSystem fileSystem) { + toClose.add(fileSystem); + } + + @Override + public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { + + } + + protected void performLoading(LaunchClassLoader classLoader) { + try { + if (Launch.blackboard.get("fml.deobfuscatedEnvironment") == Boolean.TRUE) { + System.out.println("Skipping JAR loading in development environment."); + return; + } + + Path p = getFilesToLoad(); + + System.out.println("Loading a JAR from " + p.toAbsolutePath()); + Path tempDirectory = Files.createTempDirectory("notenoughupdates-extracted-" + getClass().getSimpleName()); + System.out.println("Using temporary directory " + tempDirectory + " to store extracted jars."); + tempDirectory.toFile().deleteOnExit(); + try (Stream libraries = Files.walk(p, 1)) { + libraries.filter(it -> it.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".jar")) + .forEach(it -> { + try { + Path extractedPath = tempDirectory.resolve(it.getFileName().toString()); + extractedPath.toFile().deleteOnExit(); + Files.copy(it, extractedPath); + ClassLoaderExtUtil.addClassSourceTwice(classLoader, extractedPath.toUri().toURL()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + classLoader.loadClass(getTestClass()); + System.out.println("Could successfully load a class from loaded library."); + } catch (Throwable e) { + System.err.println("Failed to load a JAR into NEU. This is most likely a bad thing."); + e.printStackTrace(); + } finally { + Iterator iterator = toClose.iterator(); + while (iterator.hasNext()) + try { + iterator.next().close(); + } catch (Throwable e) { + e.printStackTrace(); + } finally { + iterator.remove(); + } + } + + } + + @Override + public String getLaunchTarget() { + return null; + } + + @Override + public String[] getLaunchArguments() { + return new String[0]; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/loader/KotlinLoadingTweaker.java b/src/main/java/io/github/moulberry/notenoughupdates/loader/KotlinLoadingTweaker.java index 2647fe34..0ce682dd 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/loader/KotlinLoadingTweaker.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/loader/KotlinLoadingTweaker.java @@ -19,25 +19,11 @@ package io.github.moulberry.notenoughupdates.loader; -import net.minecraft.launchwrapper.ITweaker; import net.minecraft.launchwrapper.Launch; import net.minecraft.launchwrapper.LaunchClassLoader; -import java.io.File; -import java.lang.reflect.Method; -import java.net.URI; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.stream.Stream; /** *

TO OTHER MOD AUTHORS THAT WANT TO BUNDLE KOTLIN AND ARE RUNNING INTO CONFLICTS WITH NEU:

@@ -75,7 +61,7 @@ import java.util.stream.Stream; * so we have to make due with our crude hack. *

*/ -public class KotlinLoadingTweaker implements ITweaker { +public class KotlinLoadingTweaker extends JARLoadingTweaker { /** * Full version format: [1, 7, 20] (1.7.20) @@ -84,8 +70,41 @@ public class KotlinLoadingTweaker implements ITweaker { public static final int[] BUNDLED_KOTLIN_VERSION = new int[]{1, 8, 21}; @Override - public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { + protected Path getFilesToLoad() { + if ("1".equals(System.getProperty("neu.relinquishkotlin"))) { + System.out.println("NEU is forced to relinquish Kotlin by user configuration."); + return null; + } + if (Launch.blackboard.get("fml.deobfuscatedEnvironment") == Boolean.TRUE) { + System.out.println("Skipping NEU Kotlin loading in development environment."); + return null; + } + Object relinquishAlways = Launch.blackboard.get("neu.relinquishkotlin.always"); + if (relinquishAlways == Boolean.TRUE) { + System.err.println( + "NEU is forced to blanket relinquish loading Kotlin. This is probably a bad judgement call by another developer."); + return null; + } + Object relinquishIfBelow = Launch.blackboard.get("neu.relinquishkotlin.ifbelow"); + if ((relinquishIfBelow instanceof int[])) { + int[] requiredVersion = (int[]) relinquishIfBelow; + if (!areWeBundlingAKotlinVersionHigherThan(requiredVersion)) { + System.err.println( + "NEU is relinquishing loading Kotlin because a higher version is requested. This may lead to errors if the advertised Kotlin version is not found. (" + + Arrays.toString(requiredVersion) + " required, " + Arrays.toString(BUNDLED_KOTLIN_VERSION) + + " available)"); + return null; + } + } + System.out.println("Attempting to load Kotlin from NEU wrapped libraries."); + + return getShadowedElement("/neu-kotlin-libraries-wrapped"); + } + + @Override + protected String getTestClass() { + return "kotlin.KotlinVersion"; } public boolean areWeBundlingAKotlinVersionHigherThan(int[] x) { @@ -101,92 +120,6 @@ public class KotlinLoadingTweaker implements ITweaker { @Override public void injectIntoClassLoader(LaunchClassLoader classLoader) { - FileSystem fs = null; - try { - if ("1".equals(System.getProperty("neu.relinquishkotlin"))) { - System.out.println("NEU is forced to relinquish Kotlin by user configuration."); - return; - } - if (Launch.blackboard.get("fml.deobfuscatedEnvironment") == Boolean.TRUE) { - System.out.println("Skipping NEU Kotlin loading in development environment."); - return; - } - Object relinquishAlways = Launch.blackboard.get("neu.relinquishkotlin.always"); - if (relinquishAlways == Boolean.TRUE) { - System.err.println( - "NEU is forced to blanket relinquish loading Kotlin. This is probably a bad judgement call by another developer."); - return; - } - Object relinquishIfBelow = Launch.blackboard.get("neu.relinquishkotlin.ifbelow"); - if ((relinquishIfBelow instanceof int[])) { - int[] requiredVersion = (int[]) relinquishIfBelow; - if (!areWeBundlingAKotlinVersionHigherThan(requiredVersion)) { - System.err.println( - "NEU is relinquishing loading Kotlin because a higher version is requested. This may lead to errors if the advertised Kotlin version is not found. (" + - Arrays.toString(requiredVersion) + " required, " + Arrays.toString(BUNDLED_KOTLIN_VERSION) + " available)"); - return; - } - } - - System.out.println("Attempting to load Kotlin from NEU wrapped libraries."); - - URI uri = getClass().getResource("/neu-kotlin-libraries-wrapped").toURI(); - Path p; - if ("jar".equals(uri.getScheme())) { - fs = FileSystems.newFileSystem(uri, Collections.emptyMap()); - p = fs.getPath( - "/neu-kotlin-libraries-wrapped"); - } else { - p = Paths.get(uri); - } - System.out.println("Loading NEU Kotlin from " + p.toAbsolutePath()); - Path tempDirectory = Files.createTempDirectory("notenoughupdates-extracted-kotlin"); - System.out.println("Using temporary directory " + tempDirectory + " to store extracted kotlin."); - tempDirectory.toFile().deleteOnExit(); - try (Stream libraries = Files.walk(p, 1)) { - libraries.filter(it -> it.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".jar")) - .forEach(it -> { - try { - Path extractedPath = tempDirectory.resolve(it.getFileName().toString()); - extractedPath.toFile().deleteOnExit(); - Files.copy(it, extractedPath); - addClassSourceTwice(classLoader, extractedPath.toUri().toURL()); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - } - classLoader.loadClass("kotlin.KotlinVersion"); - System.out.println("Could successfully load a Kotlin class."); - } catch (Throwable e) { - System.err.println("Failed to load Kotlin into NEU. This is most likely a bad thing."); - e.printStackTrace(); - } finally { - if (fs != null) { - try { - fs.close(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - } - } - - private void addClassSourceTwice(LaunchClassLoader classLoader, URL url) throws Exception { - classLoader.addURL(url); - ClassLoader classLoaderYetAgain = classLoader.getClass().getClassLoader(); - Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); - addURL.setAccessible(true); - addURL.invoke(classLoaderYetAgain, url); - } - - @Override - public String getLaunchTarget() { - return null; - } - - @Override - public String[] getLaunchArguments() { - return new String[0]; + performLoading(classLoader); } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/loader/MixinLoadingTweaker.java b/src/main/java/io/github/moulberry/notenoughupdates/loader/MixinLoadingTweaker.java new file mode 100644 index 00000000..84ad90e8 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/loader/MixinLoadingTweaker.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see . + */ + +package io.github.moulberry.notenoughupdates.loader; + +import net.minecraft.launchwrapper.Launch; +import net.minecraft.launchwrapper.LaunchClassLoader; + +import java.nio.file.Path; + +public class MixinLoadingTweaker extends JARLoadingTweaker { + + @Override + protected Path getFilesToLoad() { + return getShadowedElement("/neu-mixin-libraries-wrapped"); + } + + @Override + protected String getTestClass() { + return "org.spongepowered.asm.launch.MixinBootstrap"; + } + + @Override + public void injectIntoClassLoader(LaunchClassLoader classLoader) { + // Explicitly don't inject in here. Instead use the constructor. + } + + public MixinLoadingTweaker() { + performLoading(Launch.classLoader); + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/loader/NEUDelegatingTweaker.java b/src/main/java/io/github/moulberry/notenoughupdates/loader/NEUDelegatingTweaker.java index 1c2cd285..15f848cb 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/loader/NEUDelegatingTweaker.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/loader/NEUDelegatingTweaker.java @@ -57,10 +57,11 @@ public class NEUDelegatingTweaker implements ITweaker { } private void discoverTweakers() { + delegates.add(MixinLoadingTweaker.class.getName()); if (BuildFlags.ENABLE_ONECONFIG_COMPAT_LAYER) { delegates.add("cc.polyfrost.oneconfig.loader.stage0.LaunchWrapperTweaker"); } - delegates.add(MixinTweaker.class.getName()); + delegates.add("org.spongepowered.asm.launch.MixinTweaker"); delegates.add(ModLoadingTweaker.class.getName()); delegates.add(KotlinLoadingTweaker.class.getName()); } diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 412917d1..c05af117 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,6 +1,4 @@ # This mods.toml is loaded by forge 1.13-1.19 and is only here to make forge display an error message -modLoader = "javafml" -loaderVersion = "[12,)" license = "LGPL" [[mods]] -- cgit