diff --git a/.github/workflows/test_jvms.yml b/.github/workflows/test_jvms.yml index f79ff24..d4486c2 100644 --- a/.github/workflows/test_jvms.yml +++ b/.github/workflows/test_jvms.yml @@ -1,12 +1,7 @@ name: Test JVMs and publish Jmh results on: - push: - branches: - - main workflow_dispatch: - pull_request: - types: [opened, synchronize] jobs: testjdks: diff --git a/.gitignore b/.gitignore index f788ec8..7ff06fb 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,7 @@ **/logs/ **/profile.jfr /jmh_results.md +/src/main/resources/META-INF/MANIFEST.MF +/test_results.html +/artifacts/ +/test_artifacts.zip diff --git a/build.gradle b/build.gradle index a5b3e5f..5ab94b2 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,8 @@ plugins { id 'java-library' id 'eclipse' id 'net.minecraftforge.gradleutils' version '2.+' - id 'de.jjohannes.extra-java-module-info' version '0.11' + id 'org.gradlex.extra-java-module-info' version '1.5' + id 'com.github.ben-manes.versions' version '0.49.0' id 'maven-publish' } @@ -26,19 +27,20 @@ repositories { dependencies { api(libs.bundles.asm) - + implementation(libs.securemodules) implementation(libs.bundles.log4j.runtime) implementation(libs.jopt.simple) - + implementation(libs.bootstrap.api) + compileOnly(libs.nulls) - + annotationProcessor(libs.log4j.core) } extraJavaModuleInfo { failOnMissingModuleInfo = false - automaticModule('jopt-simple-5.0.4.jar', 'jopt.simple') + automaticModule('net.sf.jopt-simple:jopt-simple', 'jopt.simple') } license { @@ -47,24 +49,27 @@ license { jar { manifest { - attributes([ + attributes([ + 'Forge-Module-Layer': 'boot' + ] as LinkedHashMap) + attributes([ 'Specification-Title': 'modlauncher', - 'Specification-Vendor': 'Forge Development LLC', - 'Specification-Version': gradleutils.gitInfo.tag, - 'Implementation-Title': project.name, - 'Implementation-Version': project.version, - 'Implementation-Vendor': 'Forge Development LLC' - ] as LinkedHashMap, 'cpw/mods/modlauncher/api/') - - attributes([ + 'Specification-Vendor': 'Forge Development LLC', + 'Specification-Version': gradleutils.gitInfo.tag, + 'Implementation-Title': project.name, + 'Implementation-Version': project.version, + 'Implementation-Vendor': 'Forge Development LLC' + ] as LinkedHashMap, 'cpw/mods/modlauncher/api/') + + attributes([ 'Specification-Title': 'modlauncherserviceapi', - 'Specification-Vendor': 'Forge Development LLC', - 'Specification-Version': gradleutils.gitInfo.tag, - "Implementation-Title": project.name, - 'Implementation-Version': project.version, - 'Implementation-Vendor': 'Forge Development LLC' - ] as LinkedHashMap, 'cpw/mods/modlauncher/serviceapi/') - } + 'Specification-Vendor': 'Forge Development LLC', + 'Specification-Version': gradleutils.gitInfo.tag, + 'Implementation-Title': project.name, + 'Implementation-Version': project.version, + 'Implementation-Vendor': 'Forge Development LLC' + ] as LinkedHashMap, 'cpw/mods/modlauncher/serviceapi/') + } } tasks.withType(JavaCompile) { @@ -86,7 +91,7 @@ publishing { description = 'Common ModLauncher framework' url = 'https://github.com/MinecraftForge/ModLauncher' PomUtils.setGitHubDetails(pom, 'ModLauncher') - + license PomUtils.Licenses.LGPLv2_1 developers { developer PomUtils.Developers.cpw @@ -99,6 +104,16 @@ publishing { } } +tasks.register('writeManifest') { + doLast { + jar.manifest.writeTo(file('src/main/resources/META-INF/MANIFEST.MF')) + } +} + +eclipse { + autoBuildTasks writeManifest +} + // Hack eclipse into knowing that the gradle deps are modules eclipse.classpath { containers 'org.eclipse.buildship.core.gradleclasspathcontainer' diff --git a/ml-jmh/build.gradle b/ml-jmh/build.gradle index cd73b9f..3577652 100644 --- a/ml-jmh/build.gradle +++ b/ml-jmh/build.gradle @@ -2,7 +2,8 @@ plugins { id 'org.cadixdev.licenser' version '0.6.1' id 'eclipse' id 'java-library' - id 'org.gradlex.extra-java-module-info' version '1.4.2' + id 'org.gradlex.extra-java-module-info' version '1.5' + id 'com.github.ben-manes.versions' version '0.49.0' id 'net.minecraftforge.gradleutils' version '2.+' id 'com.diffplug.eclipse.apt' version '3.43.0' } @@ -10,7 +11,6 @@ plugins { repositories { mavenCentral() maven gradleutils.forgeMaven - mavenLocal() } java { @@ -20,22 +20,22 @@ java { license { header = rootProject.file("LICENSE-header.txt") } - dependencies { implementation(rootProject) + implementation(libs.securemodules) implementation(project(':ml-test-jar')) implementation(libs.bundles.powermock) - implementation('org.openjdk.jmh:jmh-core:1.35') - runtimeOnly('org.openjdk.jmh:jmh-generator-annprocess:1.35') + implementation(libs.jmh.core) + runtimeOnly(libs.jmh.ap) - annotationProcessor('org.openjdk.jmh:jmh-generator-annprocess:1.35') + annotationProcessor(libs.jmh.ap) } extraJavaModuleInfo { failOnMissingModuleInfo = false - automaticModule('jmh-core-1.35.jar', 'jmh.core') - automaticModule('powermock-core-2.0.9.jar', 'powermock.core') - automaticModule('powermock-reflect-2.0.9.jar', 'powermock.reflect') + automaticModule('org.openjdk.jmh:jmh-core', 'jmh.core') + automaticModule('org.powermock:powermock-core', 'powermock.core') + automaticModule('org.powermock:powermock-reflect', 'powermock.reflect') } def jmhArgs = [ diff --git a/ml-test/build.gradle b/ml-test/build.gradle index dea24d1..c5e3e49 100644 --- a/ml-test/build.gradle +++ b/ml-test/build.gradle @@ -1,15 +1,15 @@ plugins { id 'org.cadixdev.licenser' version '0.6.1' - id 'eclipse' id 'java-library' - id 'org.gradlex.extra-java-module-info' version '1.4.2' + id 'eclipse' + id 'org.gradlex.extra-java-module-info' version '1.5' + id 'com.github.ben-manes.versions' version '0.49.0' id 'net.minecraftforge.gradleutils' version '2.+' } repositories { mavenCentral() maven gradleutils.forgeMaven - mavenLocal() } java { @@ -41,10 +41,10 @@ dependencies { extraJavaModuleInfo { failOnMissingModuleInfo = false - automaticModule('jmh-core-1.35.jar', 'jmh.core') - automaticModule('jopt-simple-5.0.4.jar', 'jopt.simple') - automaticModule('powermock-core-2.0.9.jar', 'powermock.core') - automaticModule('powermock-reflect-2.0.9.jar', 'powermock.reflect') + automaticModule('net.sf.jopt-simple:jopt-simple', 'jopt.simple') + automaticModule('org.openjdk.jmh:jmh-core', 'jmh.core') + automaticModule('org.powermock:powermock-core', 'powermock.core') + automaticModule('org.powermock:powermock-reflect', 'powermock.reflect') } // If we are being told a specific vendor then we are probably being run in parallel diff --git a/ml-test/src/test/java/net/minecraftforge/modlauncher/test/ClassTransformerTests.java b/ml-test/src/test/java/net/minecraftforge/modlauncher/test/ClassTransformerTests.java index 9b28d16..756cfb9 100644 --- a/ml-test/src/test/java/net/minecraftforge/modlauncher/test/ClassTransformerTests.java +++ b/ml-test/src/test/java/net/minecraftforge/modlauncher/test/ClassTransformerTests.java @@ -35,6 +35,7 @@ class ClassTransformerTests { void testClassTransformer() throws Exception { MarkerManager.getMarker("CLASSDUMP"); Configurator.setLevel(ClassTransformer.class.getName(), Level.TRACE); + UnsafeHacksUtil.hackPowermock(); final TransformStore transformStore = new TransformStore(); final ModuleLayerHandler layerHandler = Whitebox.invokeConstructor(ModuleLayerHandler.class); final LaunchPluginHandler lph = new LaunchPluginHandler(layerHandler); diff --git a/ml-test/src/test/java/net/minecraftforge/modlauncher/test/TransformingClassLoaderTests.java b/ml-test/src/test/java/net/minecraftforge/modlauncher/test/TransformingClassLoaderTests.java index dbd6538..f7f68ea 100644 --- a/ml-test/src/test/java/net/minecraftforge/modlauncher/test/TransformingClassLoaderTests.java +++ b/ml-test/src/test/java/net/minecraftforge/modlauncher/test/TransformingClassLoaderTests.java @@ -52,10 +52,16 @@ public List transformers() { Environment environment = Whitebox.invokeConstructor(Environment.class, new Class[]{ Launcher.class }, new Object[]{ null }); new TypesafeMap(IEnvironment.class); - Class builderClass = Class.forName("cpw.mods.modlauncher.TransformingClassLoaderBuilder"); - Constructor constructor = Whitebox.getConstructor(TransformingClassLoader.class, TransformStore.class, LaunchPluginHandler.class, builderClass, Environment.class, Configuration.class, List.class); + Constructor constructor = Whitebox.getConstructor(TransformingClassLoader.class, + String.class, ClassLoader.class, Configuration.class, List.class, List.class, + TransformStore.class, LaunchPluginHandler.class, Environment.class + ); + Configuration configuration = createTestJarsConfiguration(); - TransformingClassLoader tcl = constructor.newInstance(transformStore, lph, null, environment, configuration, List.of(ModuleLayer.boot())); + TransformingClassLoader tcl = constructor.newInstance( + "TRANSFORMER", null, configuration, List.of(ModuleLayer.boot()), List.of(), + transformStore, lph, environment + ); ModuleLayer.boot().defineModules(configuration, s -> tcl); final Class aClass = Class.forName(TARGET_CLASS, true, tcl); diff --git a/run_workflow_tests.sh b/run_workflow_tests.sh index 4f6f4ca..58d1ce9 100755 --- a/run_workflow_tests.sh +++ b/run_workflow_tests.sh @@ -3,7 +3,7 @@ clear #Remove last run just in case rm -rf artifacts/ -act --artifact-server-path ./artifacts +act --artifact-server-path ./artifacts --workflows ./.github/workflows/test_jvms.yml # Uncompress all artifacts find ./artifacts/ -name *.gz__ | while read filename; do gunzip --suffix=.gz__ "$filename"; done; diff --git a/settings.gradle b/settings.gradle index 6741fa9..fb9f9c1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,27 +17,32 @@ dependencyResolutionManagement { library('asm-tree', 'org.ow2.asm', 'asm-tree' ).versionRef('asm') library('asm-commons', 'org.ow2.asm', 'asm-commons').versionRef('asm') bundle('asm', ['asm', 'asm-tree', 'asm-commons']) - - version('junit', '5.10.0') - library('junit-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit') - library('junit-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit') - library('junit-platform-launcher', 'org.junit.platform:junit-platform-launcher:1.10.0') - bundle('junit-runtime', ['junit-engine', 'junit-platform-launcher']) // Force Gradle to load the JUnit Platform Launcher from the module-path - + + version('junit', '5.10.1') + library('junit-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit') + library('junit-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit') + library('junit-platform-launcher', 'org.junit.platform:junit-platform-launcher:1.10.1') + bundle('junit-runtime', ['junit-engine', 'junit-platform-launcher']) // Force Gradle to load the JUnit Platform Launcher from the module-path + library('nulls', 'org.jetbrains:annotations:23.0.0') - library('unsafe', 'net.minecraftforge:unsafe:0.9.2') - library('securemodules', 'net.minecraftforge:securemodules:2.2.2') + library('unsafe', 'net.minecraftforge:unsafe:0.9.2') + library('securemodules', 'net.minecraftforge:securemodules:2.2.6') library('jopt-simple', 'net.sf.jopt-simple:jopt-simple:5.0.4') - + library('bootstrap-api', 'net.minecraftforge:bootstrap-api:2.0.0') + version('log4j', '2.17.1') library('log4j-api', 'org.apache.logging.log4j', 'log4j-api' ).versionRef('log4j') library('log4j-core', 'org.apache.logging.log4j', 'log4j-core').versionRef('log4j') bundle('log4j-runtime', ['log4j-api', 'log4j-core']) - + version('powermock', '2.0.9') library('powermock-core', 'org.powermock', 'powermock-core').versionRef('powermock') library('powermock-reflect', 'org.powermock', 'powermock-reflect').versionRef('powermock') bundle('powermock', ['powermock-core', 'powermock-reflect']) + + version('jmh', '1.37') + library('jmh-core', 'org.openjdk.jmh', 'jmh-core').versionRef('jmh') + library('jmh-ap', 'org.openjdk.jmh', 'jmh-generator-annprocess').versionRef('jmh') } } } diff --git a/src/main/java/cpw/mods/modlauncher/BootstrapEntry.java b/src/main/java/cpw/mods/modlauncher/BootstrapEntry.java new file mode 100644 index 0000000..79af5d7 --- /dev/null +++ b/src/main/java/cpw/mods/modlauncher/BootstrapEntry.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-3.0-only + */ + +package cpw.mods.modlauncher; + +import net.minecraftforge.bootstrap.api.BootstrapEntryPoint; + +/** + * Internal Service so that Bootstrap can find us. + * Considered internal API, so may break at any time do not reference. + */ +public class BootstrapEntry implements BootstrapEntryPoint { + @Override + public void main(String... strings) { + Launcher.main(strings); + } +} diff --git a/src/main/java/cpw/mods/modlauncher/BootstrapLaunchConsumer.java b/src/main/java/cpw/mods/modlauncher/BootstrapLaunchConsumer.java index f88779e..4f9150c 100644 --- a/src/main/java/cpw/mods/modlauncher/BootstrapLaunchConsumer.java +++ b/src/main/java/cpw/mods/modlauncher/BootstrapLaunchConsumer.java @@ -7,6 +7,7 @@ import java.util.function.Consumer; +@Deprecated(forRemoval = true, since = "10.1") // Use proper Service type. public class BootstrapLaunchConsumer implements Consumer { @Override public void accept(final String[] strings) { diff --git a/src/main/java/cpw/mods/modlauncher/DefaultLaunchHandlerService.java b/src/main/java/cpw/mods/modlauncher/DefaultLaunchHandlerService.java index f3651e5..34bfd19 100644 --- a/src/main/java/cpw/mods/modlauncher/DefaultLaunchHandlerService.java +++ b/src/main/java/cpw/mods/modlauncher/DefaultLaunchHandlerService.java @@ -9,11 +9,11 @@ import java.lang.reflect.*; import java.nio.file.*; -import java.util.concurrent.*; /** - * Default launch handler service - will launch minecraft + * This has not worked in years */ +@Deprecated(forRemoval = true, since = "10.1") public class DefaultLaunchHandlerService implements ILaunchHandlerService { public static final String LAUNCH_PROPERTY = "minecraft.client.jar"; public static final String LAUNCH_PATH_STRING = System.getProperty(LAUNCH_PROPERTY); diff --git a/src/main/java/cpw/mods/modlauncher/LaunchPluginHandler.java b/src/main/java/cpw/mods/modlauncher/LaunchPluginHandler.java index 9b9bc85..d6e9f58 100644 --- a/src/main/java/cpw/mods/modlauncher/LaunchPluginHandler.java +++ b/src/main/java/cpw/mods/modlauncher/LaunchPluginHandler.java @@ -7,53 +7,69 @@ import cpw.mods.jarhandling.SecureJar; import cpw.mods.modlauncher.api.IEnvironment; -import cpw.mods.modlauncher.api.IModuleLayerManager; +import cpw.mods.modlauncher.api.IModuleLayerManager.Layer; import cpw.mods.modlauncher.api.NamedPath; -import cpw.mods.modlauncher.util.ServiceLoaderUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; -import org.objectweb.asm.*; -import org.objectweb.asm.tree.*; -import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.ClassNode; -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; +import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; +import cpw.mods.modlauncher.serviceapi.ILaunchPluginService.Phase; import static cpw.mods.modlauncher.LogMarkers.*; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; + public class LaunchPluginHandler { private static final Logger LOGGER = LogManager.getLogger(); - private final Map plugins; + private final Map plugins = new HashMap<>(); + + public LaunchPluginHandler(ModuleLayerHandler layerHandler) { + var boot = layerHandler.getLayer(Layer.BOOT).orElseThrow(); + var modlist = Launcher.INSTANCE == null ? null : Launcher.INSTANCE.environment() + .getProperty(IEnvironment.Keys.MODLIST.get()) + .orElseThrow(() -> new IllegalStateException("Invalid environment, Missing MODLIST")); - public LaunchPluginHandler(final ModuleLayerHandler layerHandler) { - this.plugins = ServiceLoaderUtils.streamServiceLoader(()->ServiceLoader.load(layerHandler.getLayer(IModuleLayerManager.Layer.BOOT).orElseThrow(), ILaunchPluginService.class), - e->LOGGER.fatal(MODLAUNCHER, "Encountered serious error loading launch plugin service. Things will not work well", e)) - .collect(Collectors.toMap(ILaunchPluginService::name, Function.identity())); - final var modlist = plugins.entrySet().stream().map(e->Map.of( - "name", e.getKey(), - "type", "PLUGINSERVICE", - "file", ServiceLoaderUtils.fileNameFor(e.getValue().getClass()))) - .toList(); - if (Launcher.INSTANCE!=null) { - Launcher.INSTANCE.environment().getProperty(IEnvironment.Keys.MODLIST.get()) - .ifPresentOrElse(mods->mods.addAll(modlist),() -> { - throw new RuntimeException("The MODLIST isn't set, huh?"); - }); + for (var itr = ServiceLoader.load(boot, ILaunchPluginService.class).iterator(); itr.hasNext(); ) { + try { + var srvc = itr.next(); + @SuppressWarnings("removal") + var file = cpw.mods.modlauncher.util.ServiceLoaderUtils.fileNameFor(srvc.getClass()); + plugins.put(srvc.name(), srvc); + if (modlist != null) { + modlist.add(Map.of( + "name", srvc.name(), + "type", "PLUGINSERVICE", + "file", file + )); + } + } catch (ServiceConfigurationError e) { + LOGGER.fatal(MODLAUNCHER, "Encountered serious error loading launch plugin service. Things will not work well", e); + } } - LOGGER.debug(MODLAUNCHER,"Found launch plugins: [{}]", ()-> String.join(",", plugins.keySet())); + + LOGGER.debug(MODLAUNCHER, "Found launch plugins: [{}]", () -> String.join(",", plugins.keySet())); } - public Optional get(final String name) { + public Optional get(String name) { return Optional.ofNullable(plugins.get(name)); } - public EnumMap> computeLaunchPluginTransformerSet(final Type className, final boolean isEmpty, final String reason, final TransformerAuditTrail auditTrail) { - Set uniqueValues = new HashSet<>(); - final EnumMap> phaseObjectEnumMap = new EnumMap<>(ILaunchPluginService.Phase.class); - for (ILaunchPluginService plugin : plugins.values()) { - for (ILaunchPluginService.Phase ph : plugin.handlesClass(className, isEmpty, reason)) { + public EnumMap> computeLaunchPluginTransformerSet(Type className, boolean isEmpty, String reason, TransformerAuditTrail auditTrail) { + var uniqueValues = new HashSet(); + EnumMap> phaseObjectEnumMap = new EnumMap<>(Phase.class); + for (var plugin : plugins.values()) { + for (var ph : plugin.handlesClass(className, isEmpty, reason)) { phaseObjectEnumMap.computeIfAbsent(ph, e -> new ArrayList<>()).add(plugin); if (uniqueValues.add(plugin)) { plugin.customAuditConsumer(className.getClassName(), strings -> auditTrail.addPluginCustomAuditTrail(className.getClassName(), plugin, strings)); @@ -65,17 +81,17 @@ public EnumMap> computeLa } void offerScanResultsToPlugins(List scanResults) { - plugins.forEach((n,p)->p.addResources(scanResults)); + plugins.values().forEach(p -> p.addResources(scanResults)); } - int offerClassNodeToPlugins(final ILaunchPluginService.Phase phase, final List plugins, @Nullable final ClassNode node, final Type className, TransformerAuditTrail auditTrail, final String reason) { + int offerClassNodeToPlugins(Phase phase, List plugins, @Nullable ClassNode node, Type className, TransformerAuditTrail auditTrail, String reason) { int flags = 0; - for (ILaunchPluginService iLaunchPluginService : plugins) { - LOGGER.debug(LAUNCHPLUGIN, "LauncherPluginService {} offering transform {}", iLaunchPluginService.name(), className.getClassName()); - final int pluginFlags = iLaunchPluginService.processClassWithFlags(phase, node, className, reason); + for (var plugin : plugins) { + LOGGER.debug(LAUNCHPLUGIN, "LauncherPluginService {} offering transform {}", plugin.name(), className.getClassName()); + var pluginFlags = plugin.processClassWithFlags(phase, node, className, reason); if (pluginFlags != ILaunchPluginService.ComputeFlags.NO_REWRITE) { - auditTrail.addPluginAuditTrail(className.getClassName(), iLaunchPluginService, phase); - LOGGER.debug(LAUNCHPLUGIN, "LauncherPluginService {} transformed {} with class compute flags {}", iLaunchPluginService.name(), className.getClassName(), pluginFlags); + auditTrail.addPluginAuditTrail(className.getClassName(), plugin, phase); + LOGGER.debug(LAUNCHPLUGIN, "LauncherPluginService {} transformed {} with class compute flags {}", plugin.name(), className.getClassName(), pluginFlags); flags |= pluginFlags; } } @@ -83,7 +99,10 @@ int offerClassNodeToPlugins(final ILaunchPluginService.Phase phase, final Listp.initializeLaunch((s->transformerLoader.buildTransformedClassNodeFor(s, k)), specialPaths)); + plugins.forEach((name, plugin) -> { + plugin.initializeLaunch(clazzName -> transformerLoader.buildTransformedClassNodeFor(clazzName, name), specialPaths); + }); } } diff --git a/src/main/java/cpw/mods/modlauncher/LaunchServiceHandler.java b/src/main/java/cpw/mods/modlauncher/LaunchServiceHandler.java index 822c75c..04b8825 100644 --- a/src/main/java/cpw/mods/modlauncher/LaunchServiceHandler.java +++ b/src/main/java/cpw/mods/modlauncher/LaunchServiceHandler.java @@ -5,72 +5,92 @@ package cpw.mods.modlauncher; -import cpw.mods.modlauncher.api.*; -import cpw.mods.modlauncher.util.ServiceLoaderUtils; +import cpw.mods.modlauncher.api.ILaunchHandlerService; +import cpw.mods.modlauncher.api.IModuleLayerManager.Layer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.*; -import java.util.stream.*; +import static cpw.mods.modlauncher.LogMarkers.MODLAUNCHER; -import static cpw.mods.modlauncher.LogMarkers.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** * Identifies the launch target and dispatches to it */ class LaunchServiceHandler { private static final Logger LOGGER = LogManager.getLogger(); - private final Map launchHandlerLookup; + private final Map handlers = new HashMap<>(); - public LaunchServiceHandler(final ModuleLayerHandler layerHandler) { - this.launchHandlerLookup = ServiceLoaderUtils.streamServiceLoader(()->ServiceLoader.load(layerHandler.getLayer(IModuleLayerManager.Layer.BOOT).orElseThrow(), ILaunchHandlerService.class), sce -> LOGGER.fatal("Encountered serious error loading transformation service, expect problems", sce)) - .collect(Collectors.toMap(ILaunchHandlerService::name, LaunchServiceHandlerDecorator::new)); - LOGGER.debug(MODLAUNCHER,"Found launch services [{}]", () -> String.join(",",launchHandlerLookup.keySet())); + public LaunchServiceHandler(ModuleLayerHandler layerHandler) { + var services = ServiceLoader.load(layerHandler.getLayer(Layer.BOOT).orElseThrow(), ILaunchHandlerService.class); + for (var loader = services.iterator(); loader.hasNext(); ) { + try { + var srvc = loader.next(); + handlers.put(srvc.name(), srvc); + } catch (ServiceConfigurationError sce) { + LOGGER.fatal("Encountered serious error loading transformation service, expect problems", sce); + } + } + LOGGER.debug(MODLAUNCHER, "Found launch services [{}]", () -> String.join(",", handlers.keySet())); } public Optional findLaunchHandler(final String name) { - return Optional.ofNullable(launchHandlerLookup.getOrDefault(name, null)).map(LaunchServiceHandlerDecorator::service); + return Optional.ofNullable(handlers.getOrDefault(name, null)); } - private void launch(String target, String[] arguments, ModuleLayer gameLayer, TransformingClassLoader classLoader, final LaunchPluginHandler launchPluginHandler) { - final LaunchServiceHandlerDecorator launchServiceHandlerDecorator = launchHandlerLookup.get(target); - final NamedPath[] paths = launchServiceHandlerDecorator.service().getPaths(); + private void launch(String target, String[] arguments, ModuleLayer gameLayer, TransformingClassLoader classLoader, LaunchPluginHandler launchPluginHandler) { + var launchServiceHandlerDecorator = handlers.get(target); + var paths = launchServiceHandlerDecorator.getPaths(); launchPluginHandler.announceLaunch(classLoader, paths); LOGGER.info(MODLAUNCHER, "Launching target '{}' with arguments {}", target, hideAccessToken(arguments)); - launchServiceHandlerDecorator.launch(arguments, gameLayer); + try { + launchServiceHandlerDecorator.launchService(arguments, gameLayer).run(); + } catch (Throwable e) { + sneak(e); + } } static List hideAccessToken(String[] arguments) { - final ArrayList output = new ArrayList<>(); + var output = new ArrayList(); for (int i = 0; i < arguments.length; i++) { - if (i > 0 && Objects.equals(arguments[i-1], "--accessToken")) { - output.add("❄❄❄❄❄❄❄❄"); - } else { + if (i > 0 && Objects.equals(arguments[i-1], "--accessToken")) + output.add("**********"); + else output.add(arguments[i]); - } } return output; } public void launch(ArgumentHandler argumentHandler, ModuleLayer gameLayer, TransformingClassLoader classLoader, final LaunchPluginHandler launchPluginHandler) { - String launchTarget = argumentHandler.getLaunchTarget(); - String[] args = argumentHandler.buildArgumentList(); + var launchTarget = argumentHandler.getLaunchTarget(); + var args = argumentHandler.buildArgumentList(); launch(launchTarget, args, gameLayer, classLoader, launchPluginHandler); } TransformingClassLoaderBuilder identifyTransformationTargets(final ArgumentHandler argumentHandler) { - final String launchTarget = argumentHandler.getLaunchTarget(); - final TransformingClassLoaderBuilder builder = new TransformingClassLoaderBuilder(); - Arrays.stream(argumentHandler.getSpecialJars()).forEach(builder::addTransformationPath); - launchHandlerLookup.get(launchTarget).configureTransformationClassLoaderBuilder(builder); + var builder = new TransformingClassLoaderBuilder(); + for (var path : argumentHandler.getSpecialJars()) + builder.addTransformationPath(path); return builder; } void validateLaunchTarget(final ArgumentHandler argumentHandler) { - if (!launchHandlerLookup.containsKey(argumentHandler.getLaunchTarget())) { - LOGGER.error(MODLAUNCHER, "Cannot find launch target {}, unable to launch", - argumentHandler.getLaunchTarget()); - throw new RuntimeException("Cannot find launch target"); + var target = argumentHandler.getLaunchTarget(); + if (!handlers.containsKey(target)) { + LOGGER.error(MODLAUNCHER, "Cannot find launch target {}, unable to launch", target); + throw new IllegalArgumentException("Cannot find launch target " + target + " Known: " + String.join(",", handlers.keySet())); } } + + @SuppressWarnings("unchecked") + private static R sneak(Throwable exception) throws E { + throw (E)exception; + } } diff --git a/src/main/java/cpw/mods/modlauncher/LaunchServiceHandlerDecorator.java b/src/main/java/cpw/mods/modlauncher/LaunchServiceHandlerDecorator.java deleted file mode 100644 index 352c594..0000000 --- a/src/main/java/cpw/mods/modlauncher/LaunchServiceHandlerDecorator.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) Forge Development LLC - * SPDX-License-Identifier: LGPL-3.0-only - */ - -package cpw.mods.modlauncher; - -import cpw.mods.modlauncher.api.*; - -/** - * Decorates {@link ILaunchHandlerService} for use by the system - */ -record LaunchServiceHandlerDecorator(ILaunchHandlerService service) { - - public void launch(String[] arguments, ModuleLayer gameLayer) { - try { - this.service.launchService(arguments, gameLayer).run(); - } catch (Throwable e) { - throw new RuntimeException(e); - } - } - - public void configureTransformationClassLoaderBuilder(ITransformingClassLoaderBuilder builder) { - this.service.configureTransformationClassLoader(builder); - } -} diff --git a/src/main/java/cpw/mods/modlauncher/Launcher.java b/src/main/java/cpw/mods/modlauncher/Launcher.java index 235d745..fb8da34 100644 --- a/src/main/java/cpw/mods/modlauncher/Launcher.java +++ b/src/main/java/cpw/mods/modlauncher/Launcher.java @@ -6,15 +6,22 @@ package cpw.mods.modlauncher; import cpw.mods.jarhandling.SecureJar; -import cpw.mods.modlauncher.api.*; import org.apache.logging.log4j.LogManager; + +import cpw.mods.modlauncher.api.IEnvironment; +import cpw.mods.modlauncher.api.ILaunchHandlerService; +import cpw.mods.modlauncher.api.IModuleLayerManager; +import cpw.mods.modlauncher.api.IModuleLayerManager.Layer; +import cpw.mods.modlauncher.api.INameMappingService; +import cpw.mods.modlauncher.api.ITransformationService.Resource; +import cpw.mods.modlauncher.api.TypesafeMap; import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; -import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; import java.util.function.BiFunction; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static cpw.mods.modlauncher.LogMarkers.*; @@ -71,29 +78,43 @@ public final TypesafeMap blackboard() { } private void run(String... args) { - final ArgumentHandler.DiscoveryData discoveryData = this.argumentHandler.setArgs(args); - this.transformationServicesHandler.discoverServices(discoveryData); - final var scanResults = this.transformationServicesHandler.initializeTransformationServices(this.argumentHandler, this.environment, this.nameMappingServiceHandler) - .stream().collect(Collectors.groupingBy(ITransformationService.Resource::target)); - scanResults.getOrDefault(IModuleLayerManager.Layer.PLUGIN, List.of()) - .stream() - .mapMulti((resource, action) -> resource.resources().forEach(action)) - .forEach(np->this.moduleLayerHandler.addToLayer(IModuleLayerManager.Layer.PLUGIN, np)); - this.moduleLayerHandler.buildLayer(IModuleLayerManager.Layer.PLUGIN); - final var gameResults = this.transformationServicesHandler.triggerScanCompletion(this.moduleLayerHandler) - .stream().collect(Collectors.groupingBy(ITransformationService.Resource::target)); - final var gameContents = Stream.of(scanResults, gameResults) - .flatMap(m -> m.getOrDefault(IModuleLayerManager.Layer.GAME, List.of()).stream()) - .mapMulti((resource, action) -> resource.resources().forEach(action)) - .toList(); - gameContents.forEach(j->this.moduleLayerHandler.addToLayer(IModuleLayerManager.Layer.GAME, j)); + var discoveryData = argumentHandler.setArgs(args); + transformationServicesHandler.discoverServices(discoveryData); + + var scanResults = new HashMap>(); + + for (var resource : transformationServicesHandler.initializeTransformationServices(argumentHandler, environment, nameMappingServiceHandler)) + scanResults.computeIfAbsent(resource.target(), k -> new ArrayList<>()).add(resource); + + for (var resource : scanResults.getOrDefault(Layer.PLUGIN, List.of())) { + for (var jar : resource.resources()) + moduleLayerHandler.addToLayer(Layer.PLUGIN, jar); + } + moduleLayerHandler.build(Layer.PLUGIN); + + for (var resource : transformationServicesHandler.triggerScanCompletion(moduleLayerHandler)) + scanResults.computeIfAbsent(resource.target(), k -> new ArrayList<>()).add(resource); + + var gameContents = new ArrayList(); + for (var resource : scanResults.getOrDefault(Layer.GAME, List.of())) { + for (var jar : resource.resources()) { + moduleLayerHandler.addToLayer(Layer.GAME, jar); + gameContents.add(jar); + } + } + this.transformationServicesHandler.initialiseServiceTransformers(); this.launchPlugins.offerScanResultsToPlugins(gameContents); this.launchService.validateLaunchTarget(this.argumentHandler); - final TransformingClassLoaderBuilder classLoaderBuilder = this.launchService.identifyTransformationTargets(this.argumentHandler); + var classLoaderBuilder = this.launchService.identifyTransformationTargets(this.argumentHandler); this.classLoader = this.transformationServicesHandler.buildTransformingClassLoader(this.launchPlugins, classLoaderBuilder, this.environment, this.moduleLayerHandler); - Thread.currentThread().setContextClassLoader(this.classLoader); - this.launchService.launch(this.argumentHandler, this.moduleLayerHandler.getLayer(IModuleLayerManager.Layer.GAME).orElseThrow(), this.classLoader, this.launchPlugins); + var oldCL = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(this.classLoader); + this.launchService.launch(this.argumentHandler, this.moduleLayerHandler.getLayer(Layer.GAME).orElseThrow(), this.classLoader, this.launchPlugins); + } finally { + Thread.currentThread().setContextClassLoader(oldCL); + } } public Environment environment() { diff --git a/src/main/java/cpw/mods/modlauncher/ModuleLayerHandler.java b/src/main/java/cpw/mods/modlauncher/ModuleLayerHandler.java index 3bfc8ac..525c845 100644 --- a/src/main/java/cpw/mods/modlauncher/ModuleLayerHandler.java +++ b/src/main/java/cpw/mods/modlauncher/ModuleLayerHandler.java @@ -5,11 +5,10 @@ package cpw.mods.modlauncher; +import net.minecraftforge.securemodules.SecureModuleClassLoader; import net.minecraftforge.securemodules.SecureModuleFinder; -import cpw.mods.cl.ModuleClassLoader; import cpw.mods.jarhandling.SecureJar; import cpw.mods.modlauncher.api.IModuleLayerManager; -import cpw.mods.modlauncher.api.NamedPath; import java.lang.module.Configuration; import java.lang.module.ModuleFinder; @@ -17,79 +16,90 @@ import java.util.function.BiFunction; import java.util.function.Consumer; -@SuppressWarnings("removal") public final class ModuleLayerHandler implements IModuleLayerManager { - record LayerInfo(ModuleLayer layer, ModuleClassLoader cl) {} - - private record PathOrJar(NamedPath path, SecureJar jar) { - static PathOrJar from(SecureJar jar) { - return new PathOrJar(null, jar); - } - static PathOrJar from(NamedPath path) { - return new PathOrJar(path, null); - } - - SecureJar build() { - return jar != null ? jar : SecureJar.from(path.paths()); - } - } - private final EnumMap> layers = new EnumMap<>(Layer.class); + record LayerInfo(ModuleLayer layer, ClassLoader cl) {} + private final EnumMap> layers = new EnumMap<>(Layer.class); private final EnumMap completedLayers = new EnumMap<>(Layer.class); ModuleLayerHandler() { - ClassLoader classLoader = getClass().getClassLoader(); - // Create a new ModuleClassLoader from the boot module layer if it doesn't exist already. - // This allows us to launch without BootstrapLauncher. - ModuleClassLoader cl = null; + var classLoader = getClass().getClassLoader(); var layer = getClass().getModule().getLayer(); - if (classLoader instanceof ModuleClassLoader moduleCl) { - cl = moduleCl; - } else { - var cfg = ModuleLayer.boot().configuration().resolveAndBind(SecureModuleFinder.of(), ModuleFinder.ofSystem(), List.of()); - var tmpcl = new ModuleClassLoader("BOOT", cfg, List.of(ModuleLayer.boot())); - layer = ModuleLayer.boot().defineModules(cfg, m -> tmpcl); - cl = tmpcl; + // If we have not booted into a Module layer, lets stick ourselves in one. This is here for unit tests. + if (layer == null) { + var cfg = ModuleLayer.boot().configuration().resolveAndBind(ModuleFinder.of(), ModuleFinder.ofSystem(), List.of()); + var cl = new SecureModuleClassLoader("BOOT", classLoader, cfg, List.of(ModuleLayer.boot())); + layer = ModuleLayer.boot().defineModules(cfg, m -> cl); + System.out.println("Making new classloader: " + classLoader); + classLoader = cl; } - completedLayers.put(Layer.BOOT, new LayerInfo(layer, cl)); + completedLayers.put(Layer.BOOT, new LayerInfo(layer, classLoader)); } - void addToLayer(final Layer layer, final SecureJar jar) { - if (completedLayers.containsKey(layer)) throw new IllegalStateException("Layer already populated"); - layers.computeIfAbsent(layer, l->new ArrayList<>()).add(PathOrJar.from(jar)); + @Override + public Optional getLayer(Layer layer) { + return Optional.ofNullable(completedLayers.get(layer)).map(LayerInfo::layer); } - void addToLayer(final Layer layer, final NamedPath namedPath) { - if (completedLayers.containsKey(layer)) throw new IllegalStateException("Layer already populated"); - layers.computeIfAbsent(layer, l->new ArrayList<>()).add(PathOrJar.from(namedPath)); + void addToLayer(Layer layer, SecureJar jar) { + if (completedLayers.containsKey(layer)) + throw new IllegalStateException("Layer already populated"); + layers.computeIfAbsent(layer, l -> new ArrayList<>()).add(jar); } + /** TODO: Make package private */ @SuppressWarnings("exports") - public LayerInfo buildLayer(final Layer layer, BiFunction, ModuleClassLoader> classLoaderSupplier) { - final var finder = layers.getOrDefault(layer, List.of()).stream() - .map(PathOrJar::build) - .toArray(SecureJar[]::new); - final var targets = Arrays.stream(finder).map(SecureJar::name).toList(); - final var newConf = Configuration.resolveAndBind(SecureModuleFinder.of(finder), Arrays.stream(layer.getParent()).map(completedLayers::get).map(li->li.layer().configuration()).toList(), ModuleFinder.of(), targets); - final var allParents = Arrays.stream(layer.getParent()).map(completedLayers::get).map(LayerInfo::layer).mapMulti((moduleLayer, comp)-> { - comp.accept(moduleLayer); - moduleLayer.parents().forEach(comp); - }).toList(); - final var classLoader = classLoaderSupplier.apply(newConf, allParents); - final var modController = ModuleLayer.defineModules(newConf, Arrays.stream(layer.getParent()).map(completedLayers::get).map(LayerInfo::layer).toList(), f->classLoader); - completedLayers.put(layer, new LayerInfo(modController.layer(), classLoader)); - classLoader.setFallbackClassLoader(completedLayers.get(Layer.BOOT).cl()); - return new LayerInfo(modController.layer(), classLoader); + @Deprecated(since = "10.1") + public LayerInfo buildLayer(Layer layer, BiFunction, ClassLoader> classLoaderSupplier) { + return build(layer, (cfg, layers, loaders) -> classLoaderSupplier.apply(cfg, layers)); } - public LayerInfo buildLayer(final Layer layer) { - return buildLayer(layer, (cf, p) -> new ModuleClassLoader("LAYER "+layer.name(), cf, p)); + + LayerInfo build(Layer layer) { + return build(layer, (cfg, layers, loaders) -> new SecureModuleClassLoader("LAYER " + layer.name(), null, cfg, layers, loaders)); } - @Override - public Optional getLayer(final Layer layer) { - return Optional.ofNullable(completedLayers.get(layer)).map(LayerInfo::layer); + LayerInfo build(Layer layer, ClassLoaderFactory classLoaderSupplier) { + var jars = layers.getOrDefault(layer, List.of()).stream().toArray(SecureJar[]::new); + var targets = Arrays.stream(jars).map(SecureJar::name).toList(); + var parentLayers = new ArrayList(); + var parentConfigs = new ArrayList(); + var parentLoaders = new ArrayList(); + + for (var parent : layer.getParents()) { + var info = completedLayers.get(parent); + if (info == null) + throw new IllegalStateException("Attempted to build " + layer + " before it's parent " + parent + " was populated"); + parentLayers.add(info.layer()); + parentConfigs.add(info.layer().configuration()); + parentLoaders.add(info.cl()); + } + + var cfg = Configuration.resolveAndBind(SecureModuleFinder.of(jars), parentConfigs, ModuleFinder.of(), targets); + + var classLoader = classLoaderSupplier.create(cfg, parentLayers, parentLoaders); + + // TODO: [ML] This should actually find the correct CL for each module, not just use the newly created one + var newLayer = ModuleLayer.defineModules(cfg,parentLayers, module -> classLoader).layer(); + + var info = new LayerInfo(newLayer, classLoader); + completedLayers.put(layer, info); + return info; } + /** TODO: Make package private */ + @SuppressWarnings("exports") + @Deprecated(since = "10.1") + public LayerInfo buildLayer(Layer layer) { + return build(layer); + } + + /** TODO: Make package private */ + @SuppressWarnings("exports") + @Deprecated(since = "10.1") public void updateLayer(Layer layer, Consumer action) { action.accept(completedLayers.get(layer)); } + + interface ClassLoaderFactory { + ClassLoader create(Configuration config, List parentLayers, List parentLoaders); + } } diff --git a/src/main/java/cpw/mods/modlauncher/NameMappingServiceDecorator.java b/src/main/java/cpw/mods/modlauncher/NameMappingServiceDecorator.java deleted file mode 100644 index 63228e3..0000000 --- a/src/main/java/cpw/mods/modlauncher/NameMappingServiceDecorator.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) Forge Development LLC - * SPDX-License-Identifier: LGPL-3.0-only - */ - -package cpw.mods.modlauncher; - -import cpw.mods.modlauncher.api.*; - -import java.util.Objects; -import java.util.function.BiFunction; - -/** - * Decorator for Naming Services - */ -class NameMappingServiceDecorator { - private final INameMappingService service; - - public NameMappingServiceDecorator(INameMappingService service) { - this.service = service; - } - - public boolean validTarget(final String origin) { - return Objects.equals(this.service.understanding().getValue(), origin); - } - public String understands() { - return this.service.understanding().getKey(); - } - - public BiFunction function() { - return this.service.namingFunction(); - } - - public String toString() { - return this.service.mappingName() + ":" + this.service.mappingVersion(); - } -} diff --git a/src/main/java/cpw/mods/modlauncher/NameMappingServiceHandler.java b/src/main/java/cpw/mods/modlauncher/NameMappingServiceHandler.java index c80c678..6defb7b 100644 --- a/src/main/java/cpw/mods/modlauncher/NameMappingServiceHandler.java +++ b/src/main/java/cpw/mods/modlauncher/NameMappingServiceHandler.java @@ -5,42 +5,55 @@ package cpw.mods.modlauncher; -import cpw.mods.modlauncher.api.*; -import cpw.mods.modlauncher.util.ServiceLoaderUtils; +import cpw.mods.modlauncher.api.IModuleLayerManager.Layer; +import cpw.mods.modlauncher.api.INameMappingService; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static cpw.mods.modlauncher.LogMarkers.*; /** * Allow names to be transformed between naming domains. */ class NameMappingServiceHandler { private static final Logger LOGGER = LogManager.getLogger(); - private final Map namingTable; - private Map nameBindings; - - public NameMappingServiceHandler(final ModuleLayerHandler layerHandler) { - namingTable = ServiceLoaderUtils.streamServiceLoader(()->ServiceLoader.load(layerHandler.getLayer(IModuleLayerManager.Layer.BOOT).orElseThrow(), INameMappingService.class), sce -> LOGGER.fatal("Encountered serious error loading naming service, expect problems", sce)) - .collect(Collectors.toMap(INameMappingService::mappingName, NameMappingServiceDecorator::new)); - LOGGER.debug(MODLAUNCHER,"Found naming services : [{}]", () -> String.join(",", namingTable.keySet())); + private final Map allKnown = new HashMap<>(); + private final Map active = new HashMap<>(); + + public NameMappingServiceHandler(ModuleLayerHandler layerHandler) { + var services = ServiceLoader.load(layerHandler.getLayer(Layer.BOOT).orElseThrow(), INameMappingService.class); + for (var itr = services.iterator(); itr.hasNext(); ) { + try { + var srvc = itr.next(); + allKnown.put(srvc.mappingName(), srvc); + } catch (ServiceConfigurationError sce) { + LOGGER.fatal("Encountered serious error loading naming service, expect problems", sce); + } + } + LOGGER.debug(LogMarkers.MODLAUNCHER, "Found naming services : [{}]", () -> String.join(", ", allKnown.keySet())); } - - public Optional> findNameTranslator(final String targetNaming) { - return Optional.ofNullable(nameBindings.get(targetNaming)).map(NameMappingServiceDecorator::function); + public Optional> findNameTranslator(String targetNaming) { + var ret = active.get(targetNaming); + if (ret == null) + return Optional.empty(); + return Optional.of(ret.namingFunction()); } - public void bindNamingServices(final String currentNaming) { - LOGGER.debug(MODLAUNCHER, "Current naming domain is '{}'", currentNaming); - nameBindings = namingTable.values().stream(). - filter(nameMappingServiceDecorator -> nameMappingServiceDecorator.validTarget(currentNaming)). - collect(Collectors.toMap(NameMappingServiceDecorator::understands, Function.identity())); - LOGGER.debug(MODLAUNCHER, "Identified name mapping providers {}", nameBindings); + public void bindNamingServices(String currentNaming) { + LOGGER.debug(LogMarkers.MODLAUNCHER, "Current naming domain is '{}'", currentNaming); + this.active.clear(); + for (var service : allKnown.values()) { + if (!Objects.equals(service.understanding().getValue(), currentNaming)) + continue; + this.active.put(service.understanding().getKey(), service); + } + LOGGER.debug(LogMarkers.MODLAUNCHER, "Identified name mapping providers {}", active); } } diff --git a/src/main/java/cpw/mods/modlauncher/TestingLaunchHandlerService.java b/src/main/java/cpw/mods/modlauncher/TestingLaunchHandlerService.java index 056186a..b5f5e1d 100644 --- a/src/main/java/cpw/mods/modlauncher/TestingLaunchHandlerService.java +++ b/src/main/java/cpw/mods/modlauncher/TestingLaunchHandlerService.java @@ -20,10 +20,6 @@ public String name() { return "testharness"; } - @Override - public void configureTransformationClassLoader(final ITransformingClassLoaderBuilder builder) { - } - public ServiceRunner launchService(String[] arguments, ModuleLayer gameLayer) { try { Class callableLaunch = Class.forName(System.getProperty("test.harness.callable"), true, Thread.currentThread().getContextClassLoader()); diff --git a/src/main/java/cpw/mods/modlauncher/TransformTargetLabel.java b/src/main/java/cpw/mods/modlauncher/TransformTargetLabel.java index a0c04aa..2bbae54 100644 --- a/src/main/java/cpw/mods/modlauncher/TransformTargetLabel.java +++ b/src/main/java/cpw/mods/modlauncher/TransformTargetLabel.java @@ -5,13 +5,17 @@ package cpw.mods.modlauncher; -import cpw.mods.modlauncher.api.*; -import org.objectweb.asm.*; -import org.objectweb.asm.tree.*; -import java.util.*; -import java.util.function.*; -import java.util.stream.Collectors; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +import cpw.mods.modlauncher.api.ITransformer; + +import java.util.EnumMap; +import java.util.Objects; +import java.util.function.Supplier; import static cpw.mods.modlauncher.TransformTargetLabel.LabelType.*; @@ -90,7 +94,10 @@ public String toString() { } public enum LabelType { - FIELD(FieldNode.class), METHOD(MethodNode.class), CLASS(ClassNode.class), PRE_CLASS(ClassNode.class); + FIELD(FieldNode.class), + METHOD(MethodNode.class), + CLASS(ClassNode.class), + PRE_CLASS(ClassNode.class); private final Class nodeType; @@ -98,20 +105,6 @@ public enum LabelType { this.nodeType = nodeType; } - private static final Map> TYPE_LOOKUP; - static { - final Map> tmpTypes = new HashMap<>(); - for (LabelType type : values()) { - tmpTypes.computeIfAbsent(type.nodeType.getName(), s -> new ArrayList<>()).add(type); - } - final Map> unmodifiableTypes = tmpTypes.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, pair -> Collections.unmodifiableList(pair.getValue()))); - TYPE_LOOKUP = Collections.unmodifiableMap(unmodifiableTypes); - } - - public static List getTypeFor(java.lang.reflect.Type type) { - return TYPE_LOOKUP.getOrDefault(type.getTypeName(), Collections.emptyList()); - } - public Class getNodeType() { return nodeType; } diff --git a/src/main/java/cpw/mods/modlauncher/TransformationServiceDecorator.java b/src/main/java/cpw/mods/modlauncher/TransformationServiceDecorator.java index 5659355..01301f8 100644 --- a/src/main/java/cpw/mods/modlauncher/TransformationServiceDecorator.java +++ b/src/main/java/cpw/mods/modlauncher/TransformationServiceDecorator.java @@ -5,26 +5,16 @@ package cpw.mods.modlauncher; +import cpw.mods.modlauncher.TransformTargetLabel.LabelType; import cpw.mods.modlauncher.api.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.Nullable; - import java.lang.reflect.Type; import java.lang.reflect.ParameterizedType; -import java.net.URL; -import java.util.Collection; -import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - import static cpw.mods.modlauncher.LogMarkers.*; /** @@ -34,8 +24,6 @@ public class TransformationServiceDecorator { private static final Logger LOGGER = LogManager.getLogger(); private final ITransformationService service; private boolean isValid; - private static Set classPrefixes = new HashSet<>(); - private static Set resourceNames = new HashSet<>(); TransformationServiceDecorator(ITransformationService service) { this.service = service; @@ -64,37 +52,41 @@ void onInitialize(IEnvironment environment) { } public void gatherTransformers(TransformStore transformStore) { - LOGGER.debug(MODLAUNCHER,"Initializing transformers for transformation service {}", this.service::name); - final List transformers = this.service.transformers(); + LOGGER.debug(MODLAUNCHER, "Initializing transformers for transformation service {}", this.service::name); + var transformers = this.service.transformers(); Objects.requireNonNull(transformers, "The transformers list should not be null"); - final Map> transformersByType = transformers.stream().collect(Collectors.groupingBy( - t ->{ - final Type[] genericInterfaces = t.getClass().getGenericInterfaces(); - for (Type typ : genericInterfaces) { - if (typ instanceof ParameterizedType pt && pt.getRawType().equals(ITransformer.class)) { - return pt.getActualTypeArguments()[0]; - } - } - throw new RuntimeException("How did a non-transformer get here????"); + + for (ITransformer transformer : transformers) { + Type type = null; + var genericInterfaces = transformer.getClass().getGenericInterfaces(); + for (var typ : genericInterfaces) { + if (typ instanceof ParameterizedType pt && pt.getRawType().equals(ITransformer.class)) { + type = pt.getActualTypeArguments()[0]; + break; } - )); - for (Type type : transformersByType.keySet()) { - final List labelTypes = TransformTargetLabel.LabelType.getTypeFor(type); - if (labelTypes.isEmpty()) { - throw new IllegalArgumentException("Invalid transformer type found"); } - for (ITransformer xform : transformersByType.get(type)) { - final Set targets = xform.targets(); - if (targets.isEmpty()) continue; - final Map> labelTypeListMap = targets.stream().map(TransformTargetLabel::new).collect(Collectors.groupingBy(TransformTargetLabel::getLabelType)); - if (labelTypeListMap.keySet().size() > 1 || labelTypes.stream().noneMatch(labelTypeListMap::containsKey)) { - LOGGER.error(MODLAUNCHER,"Invalid target {} for transformer {}", labelTypes, xform); - throw new IllegalArgumentException("The transformer contains invalid targets"); + + if (type == null) { + LOGGER.error(MODLAUNCHER, "Invalid Transformer, could not determine generic type {}", transformer.getClass().getSimpleName()); + throw new IllegalArgumentException("Invalid Transformer, could not determine generic type " + transformer.getClass().getSimpleName()); + } + + LabelType seen = null; + for (var target : transformer.targets()) { + var label = new TransformTargetLabel(target); + if ( + (seen != null && label.getLabelType() != seen) || + !label.getLabelType().getNodeType().getName().equals(type.getTypeName()) + ) { + LOGGER.info(MODLAUNCHER, "Invalid target {} for transformer {}", label.getLabelType(), transformer); + throw new IllegalArgumentException("Invalid target " + label.getLabelType() + " for transformer " + transformer); } - labelTypeListMap.values().stream().flatMap(Collection::stream).forEach(target -> transformStore.addTransformer(target, xform, service)); + + seen = label.getLabelType(); + transformStore.addTransformer(label, transformer, service); } } - LOGGER.debug(MODLAUNCHER,"Initialized transformers for transformation service {}", this.service::name); + LOGGER.debug(MODLAUNCHER, "Initialized transformers for transformation service {}", this.service::name); } ITransformationService getService() { @@ -108,62 +100,6 @@ List runScan(final Environment environment) { return scanResults; } - Function> getClassLoader() { - final Map.Entry, Supplier>>> classesLocator = this.service.additionalClassesLocator(); - if (classesLocator != null) { - final HashSet packagePrefixes = new HashSet<>(classesLocator.getKey()); - final Set badPrefixes = packagePrefixes.stream(). - filter(s -> - // No prefixes starting with net.minecraft. - s.startsWith("net.minecraft.") || - // No prefixes starting with net.minecraftforge. - s.startsWith("net.minecraftforge.") || - // No prefixes already claimed - classPrefixes.contains(s) || - // No prefixes not ending in a dot - !s.endsWith(".") || - // No prefixes starting without a dot after the first character - s.indexOf('.') <= 0). - collect(Collectors.toSet()); - if (!badPrefixes.isEmpty()) { - badPrefixes.forEach(s -> LOGGER.error("Illegal prefix specified for {} : {}", this.service.name(), s)); - throw new IllegalArgumentException("Bad prefixes specified"); - } - classPrefixes.addAll(classesLocator.getKey()); - } - - final Map.Entry, Supplier>>> resourcesLocator = this.service.additionalResourcesLocator(); - if (resourcesLocator!=null) { - final HashSet resNames = new HashSet<>(resourcesLocator.getKey()); - final Set badResourceNames = resNames.stream(). - filter(s -> s.endsWith(".class") || resourceNames.contains(s)). - collect(Collectors.toSet()); - if (!badResourceNames.isEmpty()) { - badResourceNames.forEach(s -> LOGGER.error("Illegal resource name specified for {} : {}", this.service.name(), s)); - throw new IllegalArgumentException("Bad resources specified"); - } - resourceNames.addAll(resourcesLocator.getKey()); - } - return s -> getOptionalURL(classesLocator, resourcesLocator, s); - } - - private Optional getOptionalURL(@Nullable Map.Entry, Supplier>>> classes, @org.jetbrains.annotations.Nullable Map.Entry, Supplier>>> resources, final String name) { - if (classes != null && name.endsWith(".class")) { - for (String pfx : classes.getKey()) { - if (name.startsWith(pfx.replace('.','/'))) { - return classes.getValue().get().apply(name); - } - } - } else if (resources != null && !name.endsWith(".class")) { - for (String pfx : resources.getKey()) { - if (Objects.equals(name, pfx)) { - return resources.getValue().get().apply(name); - } - } - } - return Optional.empty(); - } - public List onCompleteScan(IModuleLayerManager moduleLayerManager) { return this.service.completeScan(moduleLayerManager); } diff --git a/src/main/java/cpw/mods/modlauncher/TransformationServicesHandler.java b/src/main/java/cpw/mods/modlauncher/TransformationServicesHandler.java index cc87a8d..0bbefbb 100644 --- a/src/main/java/cpw/mods/modlauncher/TransformationServicesHandler.java +++ b/src/main/java/cpw/mods/modlauncher/TransformationServicesHandler.java @@ -5,18 +5,25 @@ package cpw.mods.modlauncher; -import cpw.mods.modlauncher.api.*; +import cpw.mods.jarhandling.SecureJar; +import cpw.mods.modlauncher.api.IEnvironment; +import cpw.mods.modlauncher.api.IModuleLayerManager; +import cpw.mods.modlauncher.api.IModuleLayerManager.Layer; +import cpw.mods.modlauncher.api.ITransformationService; import cpw.mods.modlauncher.serviceapi.ITransformerDiscoveryService; -import cpw.mods.modlauncher.util.ServiceLoaderUtils; -import joptsimple.*; +import joptsimple.OptionParser; +import joptsimple.OptionSet; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - -import java.net.URL; -import java.util.*; -import java.util.function.*; -import java.util.stream.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.function.BiFunction; import static cpw.mods.modlauncher.LogMarkers.*; @@ -42,11 +49,13 @@ List initializeTransformationServices(ArgumentH return runScanningTransformationServices(environment); } - TransformingClassLoader buildTransformingClassLoader(final LaunchPluginHandler pluginHandler, final TransformingClassLoaderBuilder builder, final Environment environment, final ModuleLayerHandler layerHandler) { - final List>> classLocatorList = serviceLookup.values().stream().map(TransformationServiceDecorator::getClassLoader).filter(Objects::nonNull).collect(Collectors.toList()); - final var layerInfo = layerHandler.buildLayer(IModuleLayerManager.Layer.GAME, (cf, parents)->new TransformingClassLoader(transformStore, pluginHandler, builder, environment, cf, parents)); - layerHandler.updateLayer(IModuleLayerManager.Layer.PLUGIN, li->li.cl().setFallbackClassLoader(layerInfo.cl())); - return (TransformingClassLoader) layerInfo.cl(); + TransformingClassLoader buildTransformingClassLoader(LaunchPluginHandler pluginHandler, TransformingClassLoaderBuilder builder, Environment environment, ModuleLayerHandler layerHandler) { + return (TransformingClassLoader)layerHandler.build(Layer.GAME, + (cfg, layers, loaders) -> new TransformingClassLoader( + "TRANSFORMER", null, cfg, layers, loaders, + transformStore, pluginHandler, environment + ) + ).cl(); } private void processArguments(ArgumentHandler argumentHandler, Environment environment) { @@ -90,10 +99,14 @@ private List runScanningTransformationServices( } private void validateTransformationServices() { - if (serviceLookup.values().stream().filter(d -> !d.isValid()).count() > 0) { - final List services = serviceLookup.values().stream().filter(d -> !d.isValid()).map(TransformationServiceDecorator::getService).collect(Collectors.toList()); - final String names = services.stream().map(ITransformationService::name).collect(Collectors.joining(",")); - LOGGER.error(MODLAUNCHER,"Found {} services that failed to load : [{}]", services.size(), names); + var failed = serviceLookup.values().stream() + .filter(d -> !d.isValid()) + .map(d -> d.getService().name()) + .toList(); + + if (!failed.isEmpty()) { + var names = String.join(", ", failed); + LOGGER.error(MODLAUNCHER,"Found {} services that failed to load : [{}]", failed.size(), names); throw new InvalidLauncherSetupException("Invalid Services found "+names); } } @@ -105,26 +118,52 @@ private void loadTransformationServices(Environment environment) { void discoverServices(final ArgumentHandler.DiscoveryData discoveryData) { LOGGER.debug(MODLAUNCHER, "Discovering transformation services"); - var bootLayer = layerHandler.getLayer(IModuleLayerManager.Layer.BOOT).orElseThrow(); - var earlyDiscoveryServices = ServiceLoaderUtils.streamServiceLoader(()->ServiceLoader.load(bootLayer, ITransformerDiscoveryService.class), sce -> LOGGER.fatal(MODLAUNCHER, "Encountered serious error loading transformation discoverer, expect problems", sce)) - .toList(); - var additionalPaths = earlyDiscoveryServices.stream() - .map(s->s.candidates(discoveryData.gameDir(), discoveryData.launchTarget())) - .mapMulti(Iterable::forEach) - .toList(); - LOGGER.debug(MODLAUNCHER, "Found additional transformation services from discovery services: {}", ()->additionalPaths.stream().map(ap->Arrays.toString(ap.paths())).collect(Collectors.joining())); - additionalPaths.forEach(np->layerHandler.addToLayer(IModuleLayerManager.Layer.SERVICE, np)); - var serviceLayer = layerHandler.buildLayer(IModuleLayerManager.Layer.SERVICE); - earlyDiscoveryServices.forEach(s->s.earlyInitialization(discoveryData.launchTarget(), discoveryData.arguments())); - serviceLookup = ServiceLoaderUtils.streamServiceLoader(()->ServiceLoader.load(serviceLayer.layer(), ITransformationService.class), sce -> LOGGER.fatal(MODLAUNCHER, "Encountered serious error loading transformation service, expect problems", sce)) - .collect(Collectors.toMap(ITransformationService::name, TransformationServiceDecorator::new)); - var modlist = serviceLookup.entrySet().stream().map(e->Map.of( - "name", e.getKey(), - "type", "TRANSFORMATIONSERVICE", - "file", ServiceLoaderUtils.fileNameFor(e.getValue().getClass()) - )).toList(); - Launcher.INSTANCE.environment().getProperty(IEnvironment.Keys.MODLIST.get()).ifPresent(ml->ml.addAll(modlist)); - LOGGER.debug(MODLAUNCHER,"Found transformer services : [{}]", () -> String.join(",",serviceLookup.keySet())); + var bootLayer = layerHandler.getLayer(Layer.BOOT).orElseThrow(); + + var discovery = new ArrayList(); + for (var itr = ServiceLoader.load(bootLayer, ITransformerDiscoveryService.class).iterator(); itr.hasNext(); ) { + try { + var srvc = itr.next(); + discovery.add(srvc); + var paths = srvc.candidates(discoveryData.gameDir(), discoveryData.launchTarget()); + + if (!paths.isEmpty()) { + LOGGER.debug(MODLAUNCHER, "Found additional transformation services from discovery service: {}", srvc.getClass().getName()); + for (var path : paths) { + LOGGER.debug(MODLAUNCHER, "\t{}", path.toString()); + layerHandler.addToLayer(Layer.SERVICE, SecureJar.from(path.paths())); + } + } + } catch (ServiceConfigurationError sce) { + LOGGER.fatal("Encountered serious error loading transformation discoverer service, expect problems", sce); + } + } + + var serviceLayer = layerHandler.build(Layer.SERVICE).layer(); + for (var service : discovery) + service.earlyInitialization(discoveryData.launchTarget(), discoveryData.arguments()); + + var transformers = new HashMap(); + var modlist = Launcher.INSTANCE.environment().getProperty(IEnvironment.Keys.MODLIST.get()); + for (var itr = ServiceLoader.load(serviceLayer, ITransformationService.class).iterator(); itr.hasNext(); ) { + try { + var srvc = itr.next(); + transformers.put(srvc.name(), new TransformationServiceDecorator(srvc)); + @SuppressWarnings("removal") + var file = cpw.mods.modlauncher.util.ServiceLoaderUtils.fileNameFor(srvc.getClass()); + LOGGER.debug(MODLAUNCHER, "Found transformer service: {}: {}", srvc.name(), file); + if (modlist.isPresent()) { + modlist.get().add(Map.of( + "name", srvc.name(), + "type", "TRANSFORMATIONSERVICE", + "file", file + )); + } + } catch (ServiceConfigurationError sce) { + LOGGER.fatal(MODLAUNCHER, "Encountered serious error loading transformation service, expect problems", sce); + } + } + serviceLookup = transformers; } public List triggerScanCompletion(IModuleLayerManager moduleLayerManager) { diff --git a/src/main/java/cpw/mods/modlauncher/TransformingClassLoader.java b/src/main/java/cpw/mods/modlauncher/TransformingClassLoader.java index 6367c5a..3463f5a 100644 --- a/src/main/java/cpw/mods/modlauncher/TransformingClassLoader.java +++ b/src/main/java/cpw/mods/modlauncher/TransformingClassLoader.java @@ -6,7 +6,9 @@ package cpw.mods.modlauncher; import cpw.mods.cl.ModuleClassLoader; -import cpw.mods.modlauncher.api.*; +import cpw.mods.modlauncher.api.IEnvironment; +import cpw.mods.modlauncher.api.ITransformerActivity; +import cpw.mods.modlauncher.api.IModuleLayerManager.Layer; import java.lang.module.Configuration; import java.util.*; @@ -20,13 +22,18 @@ public class TransformingClassLoader extends ModuleClassLoader { } private final ClassTransformer classTransformer; - public TransformingClassLoader(TransformStore transformStore, LaunchPluginHandler pluginHandler, ModuleLayerHandler moduleLayerHandler) { - super("TRANSFORMER", moduleLayerHandler.getLayer(IModuleLayerManager.Layer.GAME).orElseThrow().configuration(), List.of(moduleLayerHandler.getLayer(IModuleLayerManager.Layer.SERVICE).orElseThrow())); + private static ModuleLayer get(ModuleLayerHandler layers, Layer layer) { + return layers.getLayer(layer).orElseThrow(() -> new NullPointerException("Failed to find " + layer.name() + " layer")); + } + + public TransformingClassLoader(TransformStore transformStore, LaunchPluginHandler pluginHandler, ModuleLayerHandler layers) { + super("TRANSFORMER", get(layers, Layer.GAME).configuration(), List.of(get(layers, Layer.SERVICE))); this.classTransformer = new ClassTransformer(transformStore, pluginHandler, this); } - TransformingClassLoader(TransformStore transformStore, LaunchPluginHandler pluginHandler, TransformingClassLoaderBuilder builder, final Environment environment, final Configuration configuration, List parentLayers) { - super("TRANSFORMER", configuration, parentLayers); + TransformingClassLoader(String name, ClassLoader parent, Configuration config, List parentLayers, List parentLoaders, + TransformStore transformStore, LaunchPluginHandler pluginHandler, Environment environment) { + super(name, parent, config, parentLayers, parentLoaders, true); TransformerAuditTrail tat = new TransformerAuditTrail(); environment.computePropertyIfAbsent(IEnvironment.Keys.AUDITTRAIL.get(), v->tat); this.classTransformer = new ClassTransformer(transformStore, pluginHandler, this, tat); diff --git a/src/main/java/cpw/mods/modlauncher/api/ILaunchHandlerService.java b/src/main/java/cpw/mods/modlauncher/api/ILaunchHandlerService.java index 806c711..489748e 100644 --- a/src/main/java/cpw/mods/modlauncher/api/ILaunchHandlerService.java +++ b/src/main/java/cpw/mods/modlauncher/api/ILaunchHandlerService.java @@ -5,9 +5,6 @@ package cpw.mods.modlauncher.api; -import java.nio.file.Path; -import java.util.concurrent.*; - /** * A singleton instance of this is loaded by the system to designate the launch target */ @@ -15,7 +12,7 @@ public interface ILaunchHandlerService { String name(); @Deprecated(forRemoval = true, since = "10.0") - void configureTransformationClassLoader(final ITransformingClassLoaderBuilder builder); + default void configureTransformationClassLoader(final ITransformingClassLoaderBuilder builder) {} ServiceRunner launchService(String[] arguments, ModuleLayer gameLayer); diff --git a/src/main/java/cpw/mods/modlauncher/api/IModuleLayerManager.java b/src/main/java/cpw/mods/modlauncher/api/IModuleLayerManager.java index b18c01d..75c91d4 100644 --- a/src/main/java/cpw/mods/modlauncher/api/IModuleLayerManager.java +++ b/src/main/java/cpw/mods/modlauncher/api/IModuleLayerManager.java @@ -5,6 +5,7 @@ package cpw.mods.modlauncher.api; +import java.util.List; import java.util.Optional; public interface IModuleLayerManager { @@ -16,14 +17,21 @@ enum Layer { PLUGIN(BOOT), GAME(PLUGIN, SERVICE); - private final Layer[] parent; + private final List parents; - Layer(final Layer... parent) { - this.parent = parent; + Layer(Layer... parent) { + this.parents = List.of(parent); } + /** Returning a raw array is unsafe, it used to return the backing array directly which would allow others to screw with it. */ + @Deprecated(forRemoval = true, since = "10.1") public Layer[] getParent() { - return parent; + return this.parents.toArray(Layer[]::new); + } + + /** Returns a potentially empty, immutable list of parent layers. */ + public List getParents() { + return this.parents; } } } diff --git a/src/main/java/cpw/mods/modlauncher/api/ITransformationService.java b/src/main/java/cpw/mods/modlauncher/api/ITransformationService.java index 8e1fab3..d4e53ce 100644 --- a/src/main/java/cpw/mods/modlauncher/api/ITransformationService.java +++ b/src/main/java/cpw/mods/modlauncher/api/ITransformationService.java @@ -90,34 +90,14 @@ default List completeScan(IModuleLayerManager layerManager) { @NotNull List transformers(); - /** - * Allow transformation services to provide additional classes when asked for. - * - * Rules: - * The Strings in the set must end with a dot. They must have at least one dot. They cannot include "net.minecraft." - * "net.minecraftforge.". Conflicts with other ITransformationServices will result in an immediate crash. - * - * @return a set of strings (tested with "startsWith" for classNames in "internal" format (my.package.Clazz)) - * with a function that receives the full classname and returns an Optional URL for loading that class. The null - * return value means no classlocator will be used for this transformation service. - * - */ + /** Hasn't been called in ages, will be removed in next breaking bump */ + @Deprecated(forRemoval = true, since = "10.1") default Map.Entry,Supplier>>> additionalClassesLocator() { return null; } - /** - * Allow transformation services to provide additional resource files when asked for. - * - * Rules: - * The Strings in the set must not end with ".class". Conflicts with other ITransformationServices will result - * in an immediate crash. - * - * @return a set of strings (tested with "equals" for classResources in "internal" format (my/package/Resource)) - * with a function that receives the full resource being searched and returns an Optional URL for loading that - * class. The null return value means no classlocator will be used for this transformation service. - * - */ + /** Hasn't been called in ages, will be removed in next breaking bump */ + @Deprecated(forRemoval = true, since = "10.1") default Map.Entry,Supplier>>> additionalResourcesLocator() { return null; } diff --git a/src/main/java/cpw/mods/modlauncher/log/MLClassLoaderContextSelector.java b/src/main/java/cpw/mods/modlauncher/log/MLClassLoaderContextSelector.java index b14a6a3..60fe832 100644 --- a/src/main/java/cpw/mods/modlauncher/log/MLClassLoaderContextSelector.java +++ b/src/main/java/cpw/mods/modlauncher/log/MLClassLoaderContextSelector.java @@ -5,9 +5,10 @@ package cpw.mods.modlauncher.log; -import cpw.mods.cl.ModuleClassLoader; import org.apache.logging.log4j.core.selector.ClassLoaderContextSelector; +import net.minecraftforge.securemodules.SecureModuleClassLoader; + /** * A custom context selector to avoid initializing multiple log4j contexts due to {@link ModuleClassLoader#getParent()} always returning null (as a {@link ModuleClassLoader} can have multiple parents). * As all {@link ModuleClassLoader}s should get the same log4j context, we just return a static string with "MCL", otherwise we use the default logic @@ -16,7 +17,7 @@ public class MLClassLoaderContextSelector extends ClassLoaderContextSelector { @Override protected String toContextMapKey(ClassLoader loader) { - if (loader instanceof ModuleClassLoader) { + if (loader instanceof SecureModuleClassLoader) { return "MCL"; } return super.toContextMapKey(loader); diff --git a/src/main/java/cpw/mods/modlauncher/serviceapi/ILaunchPluginService.java b/src/main/java/cpw/mods/modlauncher/serviceapi/ILaunchPluginService.java index 39542df..0616f7f 100644 --- a/src/main/java/cpw/mods/modlauncher/serviceapi/ILaunchPluginService.java +++ b/src/main/java/cpw/mods/modlauncher/serviceapi/ILaunchPluginService.java @@ -158,7 +158,17 @@ default void offerResource(Path resource, String name) {} */ default void addResources(List resources) {} - default void initializeLaunch(ITransformerLoader transformerLoader, NamedPath[] specialPaths) {} + default void initializeLaunch(ITransformerLoader transformerLoader) {} + + /** Nothing ever filled the special paths, there is no reason for it to exist. + * I *think* this method is just to give access to the transforming classloader + * in a round about way, I need to review this whole class + **/ + @Deprecated(forRemoval = true, since = "10.1") + default void initializeLaunch(ITransformerLoader transformerLoader, NamedPath[] specialPaths) { + this.initializeLaunch(transformerLoader); + } + /** * Get a plugin specific extension object from the plugin. This can be used to expose proprietary interfaces * to Launchers without ModLauncher needing to understand them. diff --git a/src/main/java/cpw/mods/modlauncher/util/ServiceLoaderUtils.java b/src/main/java/cpw/mods/modlauncher/util/ServiceLoaderUtils.java index 6f804e4..1f984aa 100644 --- a/src/main/java/cpw/mods/modlauncher/util/ServiceLoaderUtils.java +++ b/src/main/java/cpw/mods/modlauncher/util/ServiceLoaderUtils.java @@ -5,8 +5,6 @@ package cpw.mods.modlauncher.util; -import cpw.mods.niofs.union.UnionFileSystem; - import java.nio.file.Path; import java.util.Objects; import java.util.ServiceConfigurationError; @@ -15,6 +13,7 @@ import java.util.function.Supplier; import java.util.stream.Stream; +@Deprecated(forRemoval = true, since = "10.1") public final class ServiceLoaderUtils { public static Stream streamServiceLoader(Supplier> slSupplier, Consumer errorConsumer) { return streamWithErrorHandling(slSupplier.get(), errorConsumer); @@ -32,12 +31,15 @@ public static Stream streamWithErrorHandling(ServiceLoader sl, Consume } public static String fileNameFor(Class clazz) { - return clazz.getModule().getLayer().configuration() - .findModule(clazz.getModule().getName()) - .flatMap(rm->rm.reference().location()) - .map(Path::of) - .map(p -> p.getFileSystem() instanceof UnionFileSystem ufs ? ufs.getPrimaryPath() : p) - .map(p -> p.getFileName().toString()) - .orElse("MISSING FILE"); + var module = clazz.getModule(); + var ref = module.getLayer().configuration().findModule(module.getName()); + if (!ref.isPresent()) + return "MISSING FILE"; + var location = ref.get().reference().location(); + if (!location.isPresent()) + return "MISSING FILE"; + + var path = Path.of(location.get()); + return path.toString(); } } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 2159e30..7e6829f 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -6,23 +6,34 @@ module cpw.mods.modlauncher { requires java.base; requires org.apache.logging.log4j; - requires transitive org.objectweb.asm.tree; requires org.apache.logging.log4j.core; requires jopt.simple; requires transitive cpw.mods.securejarhandler; requires static org.jetbrains.annotations; + requires org.objectweb.asm; + requires transitive org.objectweb.asm.tree; + exports cpw.mods.modlauncher.log; exports cpw.mods.modlauncher.serviceapi; exports cpw.mods.modlauncher.api; exports cpw.mods.modlauncher.util; exports cpw.mods.modlauncher; - uses cpw.mods.modlauncher.api.ILaunchHandlerService; + uses cpw.mods.modlauncher.api.INameMappingService; uses cpw.mods.modlauncher.api.ITransformationService; uses cpw.mods.modlauncher.serviceapi.ILaunchPluginService; uses cpw.mods.modlauncher.serviceapi.ITransformerDiscoveryService; + + uses cpw.mods.modlauncher.api.ILaunchHandlerService; provides cpw.mods.modlauncher.api.ILaunchHandlerService with - cpw.mods.modlauncher.DefaultLaunchHandlerService, cpw.mods.modlauncher.TestingLaunchHandlerService; + cpw.mods.modlauncher.DefaultLaunchHandlerService, + cpw.mods.modlauncher.TestingLaunchHandlerService; + + requires net.minecraftforge.bootstrap.api; + provides net.minecraftforge.bootstrap.api.BootstrapEntryPoint with + cpw.mods.modlauncher.BootstrapEntry; + // for bootstrap launcher to find us without having to expose our Launcher main method - provides java.util.function.Consumer with cpw.mods.modlauncher.BootstrapLaunchConsumer; + provides java.util.function.Consumer with + cpw.mods.modlauncher.BootstrapLaunchConsumer; } \ No newline at end of file diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF deleted file mode 100644 index 5a898d3..0000000 --- a/src/main/resources/META-INF/MANIFEST.MF +++ /dev/null @@ -1,26 +0,0 @@ -Manifest-Version: 1.0 - -Name: cpw/mods/modlauncher/api/ -Specification-Title: modlauncher -Specification-Vendor: forge -Specification-Version: 7.0 -Implementation-Title: modlauncher -Implementation-Version: 9.0.1+0+master.09c65e8 -Implementation-Vendor: forge -Implementation-Timestamp: 2021-06-09T01:29:30.431684967Z -Git-Commit: 09c65e8 -Git-Branch: master -Build-Number: 0 - -Name: cpw/mods/modlauncher/cpw.mods.modlauncher.serviceapi/ -Specification-Title: modlauncherserviceapi -Specification-Vendor: forge -Specification-Version: 7.0 -Implementation-Title: modlauncher -Implementation-Version: 9.0.1+0+master.09c65e8 -Implementation-Vendor: forge -Implementation-Timestamp: 2021-06-09T01:29:30.433729643Z -Git-Commit: 09c65e8 -Git-Branch: master -Build-Number: 0 - diff --git a/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ILaunchHandlerService b/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ILaunchHandlerService deleted file mode 100644 index 376cedc..0000000 --- a/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ILaunchHandlerService +++ /dev/null @@ -1,2 +0,0 @@ -cpw.mods.modlauncher.DefaultLaunchHandlerService -cpw.mods.modlauncher.TestingLaunchHandlerService \ No newline at end of file diff --git a/test_results.html b/test_results.html deleted file mode 100644 index b9f5930..0000000 --- a/test_results.html +++ /dev/null @@ -1,200 +0,0 @@ - - - - -

net.minecraftforge.modlauncher.test.ClassLoaderAPITest

- - - - - - - - - - - -
TestAdoptium
testGetResources() - XAdoptium v16 -
-

net.minecraftforge.modlauncher.test.ClassTransformerTests

- - - - - - - - - - - -
TestAdoptium
testClassTransformer() - XAdoptium v16 -
-

net.minecraftforge.modlauncher.test.EnumerationHelperTest

- - - - - - - - - - - - - - - -
TestAdoptium
merge() - OAdoptium v16 -
fromOptional() - OAdoptium v16 -
-

net.minecraftforge.modlauncher.test.LauncherTests

- - - - - - - - - - - -
TestAdoptium
testLauncher() - XAdoptium v16 -
-

net.minecraftforge.modlauncher.test.PluginTests

- - - - - - - - - - - -
TestAdoptium
pluginTests() - OAdoptium v16 -
-

net.minecraftforge.modlauncher.test.TestingLHTests

- - - - - - - - - - - -
TestAdoptium
testTestingLaunchHandler() - XAdoptium v16 -
-

net.minecraftforge.modlauncher.test.TransformationServiceDecoratorTests

- - - - - - - - - - - -
TestAdoptium
testGatherTransformersNormally() - XAdoptium v16 -
-

net.minecraftforge.modlauncher.test.TransformingClassLoaderTests

- - - - - - - - - - - -
TestAdoptium
testClassLoader() - XAdoptium v16 -
-

net.minecraftforge.modlauncher.test.TypesafeMapTests

- - - - - - - - - - - - - - - -
TestAdoptium
testTypesafeMapKey() - OAdoptium v16 -
testTypesafeMap() - OAdoptium v16 -
- \ No newline at end of file