diff --git a/SpongeAPI b/SpongeAPI index 42dc372340a..d62dfef426e 160000 --- a/SpongeAPI +++ b/SpongeAPI @@ -1 +1 @@ -Subproject commit 42dc372340aa68a7ea28a5ffa90b68c25af09661 +Subproject commit d62dfef426e3e69c6e32fb6cb79b07636d7e9312 diff --git a/build-logic/src/main/java/org/spongepowered/gradle/impl/SpongeImplementationExtension.java b/build-logic/src/main/java/org/spongepowered/gradle/impl/SpongeImplementationExtension.java index f591b8500cf..ca43cbd7d60 100644 --- a/build-logic/src/main/java/org/spongepowered/gradle/impl/SpongeImplementationExtension.java +++ b/build-logic/src/main/java/org/spongepowered/gradle/impl/SpongeImplementationExtension.java @@ -81,7 +81,7 @@ public void copyModulesExcludingProvided(final Configuration source, final Confi final String providedVersion = providedModuleVersions.get(module); if (providedVersion == null) { - ModuleDependency dep = (ModuleDependency) deps.create(id.getDisplayName()); + ModuleDependency dep = (ModuleDependency) deps.create(module.getGroup() + ":" + module.getName() + ":" + version); dep.setTransitive(false); target.getDependencies().add(dep); } else if (!providedVersion.equals(version)) { diff --git a/build.gradle.kts b/build.gradle.kts index 8dfab8d5a90..0ab8e33a941 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,6 +19,8 @@ val apiVersion: String by project val apiJavaTarget: String by project val minecraftVersion: String by project val recommendedVersion: String by project +val organization: String by project +val projectUrl: String by project val commonManifest = java.manifest { attributes( @@ -34,48 +36,9 @@ val commonManifest = java.manifest { System.getenv()["GIT_BRANCH"]?.apply { attributes("Git-Branch" to this) } } -tasks { - jar { - manifest.from(commonManifest) - } - val mixinsJar by registering(Jar::class) { - archiveClassifier.set("mixins") - manifest.from(commonManifest) - from(mixins.map { it.output }) - } - val accessorsJar by registering(Jar::class) { - archiveClassifier.set("accessors") - manifest.from(commonManifest) - from(accessors.map { it.output }) - } - val launchJar by registering(Jar::class) { - archiveClassifier.set("launch") - manifest.from(commonManifest) - from(launch.map { it.output }) - } - val applaunchJar by registering(Jar::class) { - archiveClassifier.set("applaunch") - manifest.from(commonManifest) - from(applaunch.map { it.output }) - } - - test { - useJUnitPlatform() - } - - check { - dependsOn(gradle.includedBuild("SpongeAPI").task(":check")) - } - - prepareWorkspace { - dependsOn(gradle.includedBuild("SpongeAPI").task(":genEventImpl")) - } - -} - version = spongeImpl.generateImplementationVersionString(apiVersion, minecraftVersion, recommendedVersion) -// Configurations +// SpongeCommon configurations val applaunchConfig by configurations.register("applaunch") val launchConfig by configurations.register("launch") { @@ -90,35 +53,28 @@ val mixinsConfig by configurations.register("mixins") { extendsFrom(launchConfig) } -// create the sourcesets +// SpongeCommon source sets val main by sourceSets val applaunch by sourceSets.registering { spongeImpl.applyNamedDependencyOnOutput(project, this, main, project, main.implementationConfigurationName) - project.dependencies { - mixinsConfig(this@registering.output) - } + configurations.named(implementationConfigurationName) { extendsFrom(applaunchConfig) } } val launch by sourceSets.registering { spongeImpl.applyNamedDependencyOnOutput(project, applaunch.get(), this, project, this.implementationConfigurationName) - project.dependencies { - mixinsConfig(this@registering.output) - } - project.dependencies { - implementation(this@registering.output) - } + spongeImpl.applyNamedDependencyOnOutput(project, this, main, project, main.implementationConfigurationName) configurations.named(implementationConfigurationName) { extendsFrom(launchConfig) } } - val accessors by sourceSets.registering { spongeImpl.applyNamedDependencyOnOutput(project, launch.get(), this, project, this.implementationConfigurationName) spongeImpl.applyNamedDependencyOnOutput(project, this, main, project, main.implementationConfigurationName) + configurations.named(implementationConfigurationName) { extendsFrom(accessorsConfig) } @@ -128,6 +84,7 @@ val mixins by sourceSets.registering { spongeImpl.applyNamedDependencyOnOutput(project, applaunch.get(), this, project, this.implementationConfigurationName) spongeImpl.applyNamedDependencyOnOutput(project, accessors.get(), this, project, this.implementationConfigurationName) spongeImpl.applyNamedDependencyOnOutput(project, main, this, project, this.implementationConfigurationName) + configurations.named(implementationConfigurationName) { extendsFrom(mixinsConfig) } @@ -199,7 +156,6 @@ dependencies { applaunchConfig(libs.log4j.core) applaunchConfig(libs.log4j.jpl) - mixinsConfig(sourceSets.main.map { it.output }) add(mixins.get().implementationConfigurationName, "org.spongepowered:spongeapi:$apiVersion") // Tests @@ -215,8 +171,6 @@ dependencies { } } -val organization: String by project -val projectUrl: String by project indraSpotlessLicenser { licenseHeaderFile(rootProject.file("HEADER.txt")) @@ -229,7 +183,6 @@ idea { if (project != null) { (project as ExtensionAware).extensions["settings"].run { (this as ExtensionAware).extensions.getByType(org.jetbrains.gradle.ext.TaskTriggersConfig::class).run { - afterSync(":modlauncher-patcher:build") afterSync(":modlauncher-transformers:build") } } @@ -365,6 +318,38 @@ allprojects { } } + tasks.register("printConfigsHierarchy") { + group = "debug" + doLast { + configurations.forEach { conf: Configuration -> + val seen = mutableSetOf() + println("Parents of ${conf.name}:") + printParents(conf, "", seen) + } + } + } + + tasks.register("printConfigsResolution") { + group = "debug" + doLast { + configurations.forEach { conf: Configuration -> + println() + println("Artifacts of ${conf.name}:") + if (conf.isCanBeResolved) { + try { + conf.forEach { + println(it) + } + } catch (e: Exception) { + println("error") + } + } else { + println("not resolved") + } + } + } + } + afterEvaluate { publishing { repositories { @@ -395,34 +380,80 @@ allprojects { } } +fun printParents(conf: Configuration, indent: String, seen: MutableSet) { + for (parent in conf.extendsFrom) { + if (parent in seen) { + continue + } + seen.add(parent) + println("$indent - ${parent.name}") + printParents(parent, indent + " ", seen) + } +} + tasks { + jar { + manifest.from(commonManifest) + } + val mixinsJar by registering(Jar::class) { + archiveClassifier.set("mixins") + manifest.from(commonManifest) + from(mixins.map { it.output }) + } + val accessorsJar by registering(Jar::class) { + archiveClassifier.set("accessors") + manifest.from(commonManifest) + from(accessors.map { it.output }) + } + val launchJar by registering(Jar::class) { + archiveClassifier.set("launch") + manifest.from(commonManifest) + from(launch.map { it.output }) + } + val applaunchJar by registering(Jar::class) { + archiveClassifier.set("applaunch") + manifest.from(commonManifest) + from(applaunch.map { it.output }) + } + val jar by existing val sourcesJar by existing - val mixinsJar by existing - val accessorsJar by existing - val launchJar by existing - val applaunchJar by existing shadowJar { mergeServiceFiles() archiveClassifier.set("dev") + manifest { attributes(mapOf( - "Access-Widener" to "common.accesswidener", - "Multi-Release" to true + "Access-Widener" to "common.accesswidener", + "Multi-Release" to true )) from(commonManifest) } + from(jar) from(sourcesJar) from(mixinsJar) from(accessorsJar) from(launchJar) from(applaunchJar) + dependencies { include(project(":")) } } + + test { + useJUnitPlatform() + } + + check { + dependsOn(gradle.includedBuild("SpongeAPI").task(":check")) + } + + prepareWorkspace { + dependsOn(gradle.includedBuild("SpongeAPI").task(":genEventImpl")) + } } publishing { diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts index 6b1b816b98c..45e64e85bfb 100644 --- a/forge/build.gradle.kts +++ b/forge/build.gradle.kts @@ -19,6 +19,8 @@ plugins { val commonProject = parent!! val transformersProject = parent!!.project(":modlauncher-transformers") +val testPluginsProject: Project? = rootProject.subprojects.find { "testplugins" == it.name } + val apiVersion: String by project val minecraftVersion: String by project val forgeVersion: String by project @@ -26,8 +28,6 @@ val recommendedVersion: String by project val organization: String by project val projectUrl: String by project -val testPluginsProject: Project? = rootProject.subprojects.find { "testplugins" == it.name } - description = "The SpongeAPI implementation for MinecraftForge" version = spongeImpl.generatePlatformBuildVersionString(apiVersion, minecraftVersion, recommendedVersion, forgeVersion) @@ -38,13 +38,13 @@ repositories { } // SpongeForge libraries -val serviceLibrariesConfig: NamedDomainObjectProvider = configurations.register("spongeServiceLibraries") -val gameLibrariesConfig: NamedDomainObjectProvider = configurations.register("spongeGameLibraries") +val serviceLibrariesConfig: NamedDomainObjectProvider = configurations.register("serviceLibraries") +val gameLibrariesConfig: NamedDomainObjectProvider = configurations.register("gameLibraries") -val gameManagedLibrariesConfig: NamedDomainObjectProvider = configurations.register("spongeGameManagedLibraries") +val gameManagedLibrariesConfig: NamedDomainObjectProvider = configurations.register("gameManagedLibraries") -val serviceShadedLibrariesConfig: NamedDomainObjectProvider = configurations.register("spongeServiceShadedLibraries") -val gameShadedLibrariesConfig: NamedDomainObjectProvider = configurations.register("spongeGameShadedLibraries") +val serviceShadedLibrariesConfig: NamedDomainObjectProvider = configurations.register("serviceShadedLibraries") +val gameShadedLibrariesConfig: NamedDomainObjectProvider = configurations.register("gameShadedLibraries") val runTaskOnlyConfig: NamedDomainObjectProvider = configurations.register("runTaskOnly") @@ -70,7 +70,7 @@ val gameLayerConfig: NamedDomainObjectProvider = configurations.r } } -// Common source sets and configurations +// SpongeCommon source sets val launchConfig: NamedDomainObjectProvider = commonProject.configurations.named("launch") val accessors: NamedDomainObjectProvider = commonProject.sourceSets.named("accessors") val launch: NamedDomainObjectProvider = commonProject.sourceSets.named("launch") @@ -78,7 +78,7 @@ val applaunch: NamedDomainObjectProvider = commonProject.sourceSets.n val mixins: NamedDomainObjectProvider = commonProject.sourceSets.named("mixins") val main: NamedDomainObjectProvider = commonProject.sourceSets.named("main") -// Forge source sets +// SpongeForge source sets val forgeMain by sourceSets.named("main") { // implementation (compile) dependencies spongeImpl.applyNamedDependencyOnOutput(commonProject, accessors.get(), this, project, this.implementationConfigurationName) @@ -102,6 +102,7 @@ val forgeLaunch by sourceSets.register("launch") { } val forgeAccessors by sourceSets.register("accessors") { spongeImpl.applyNamedDependencyOnOutput(commonProject, mixins.get(), this, project, this.implementationConfigurationName) + spongeImpl.applyNamedDependencyOnOutput(commonProject, accessors.get(), this, project, this.implementationConfigurationName) spongeImpl.applyNamedDependencyOnOutput(project, this, forgeLaunch, project, forgeLaunch.implementationConfigurationName) configurations.named(implementationConfigurationName) { @@ -205,27 +206,27 @@ dependencies { forgeMixins.implementationConfigurationName(project(commonProject.path)) - val serviceLibraries = serviceLibrariesConfig.name - serviceLibraries(apiLibs.pluginSpi) - serviceLibraries(project(transformersProject.path)) - serviceLibraries(platform(apiLibs.configurate.bom)) - serviceLibraries(apiLibs.configurate.core) { + val service = serviceLibrariesConfig.name + service(apiLibs.pluginSpi) + service(project(transformersProject.path)) + service(platform(apiLibs.configurate.bom)) + service(apiLibs.configurate.core) { exclude(group = "org.checkerframework", module = "checker-qual") } - serviceLibraries(apiLibs.configurate.hocon) { + service(apiLibs.configurate.hocon) { exclude(group = "org.spongepowered", module = "configurate-core") exclude(group = "org.checkerframework", module = "checker-qual") } - serviceLibraries(libs.configurate.jackson) { + service(libs.configurate.jackson) { exclude(group = "org.spongepowered", module = "configurate-core") exclude(group = "org.checkerframework", module = "checker-qual") } - val gameLibraries = gameLibrariesConfig.name - gameLibraries("org.spongepowered:spongeapi:$apiVersion") - gameLibraries(libs.javaxInject) - gameLibraries(platform(apiLibs.adventure.bom)) - gameLibraries(libs.adventure.serializerConfigurate4) + val game = gameLibrariesConfig.name + game("org.spongepowered:spongeapi:$apiVersion") + game(libs.javaxInject) + game(platform(apiLibs.adventure.bom)) + game(libs.adventure.serializerConfigurate4) val serviceShadedLibraries = serviceShadedLibrariesConfig.name serviceShadedLibraries(project(transformersProject.path)) { isTransitive = false } @@ -292,7 +293,7 @@ tasks { from(forgeLang.output) } - val forgeServicesDevJar by registering(Jar::class) { + val forgeServicesJar by registering(Jar::class) { archiveClassifier.set("services") manifest.from(forgeManifest) @@ -307,7 +308,7 @@ tasks { // Default classpath is a mess, we better start a new one from scratch classpath = files( configurations.getByName("forgeRuntimeLibrary"), - forgeServicesDevJar, forgeLangJar, runTaskOnlyConfig + forgeServicesJar, forgeLangJar, runTaskOnlyConfig ) testPluginsProject?.also { @@ -354,7 +355,7 @@ tasks { mergeServiceFiles() configurations = listOf(serviceShadedLibrariesConfig.get()) - exclude("META-INF/INDEX.LIST", "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "module-info.class") + exclude("META-INF/INDEX.LIST", "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "**/module-info.class") manifest { attributes("Automatic-Module-Name" to "spongeforge.services") @@ -426,21 +427,6 @@ sourceSets { } } -afterEvaluate { - sourceSets.configureEach { - // Don't apply Mixin AP - configurations.named(annotationProcessorConfigurationName) { - exclude(group = "org.spongepowered", module = "mixin") - exclude(group = "net.fabricmc", module = "fabric-mixin-compile-extensions") - } - // And don't pass AP parameters - tasks.named(compileJavaTaskName, JavaCompile::class) { - val mixinApArgs = setOf("outRefMapFile", "defaultObfuscationEnv", "outMapFileNamedIntermediary", "inMapFileNamedIntermediary") - options.compilerArgs.removeIf { mixinApArgs.any { mixin -> it.contains(mixin)} } - } - } -} - indraSpotlessLicenser { licenseHeaderFile(rootProject.file("HEADER.txt")) @@ -457,6 +443,9 @@ publishing { artifact(tasks["jar"]) artifact(tasks["sourcesJar"]) + artifact(tasks["forgeLangJar"]) + artifact(tasks["langSourcesJar"]) + artifact(tasks["forgeMixinsJar"]) artifact(tasks["mixinsSourcesJar"]) @@ -490,46 +479,3 @@ publishing { } } } - -tasks.register("printConfigsHierarchy") { - group = "debug" - doLast { - configurations.forEach { conf: Configuration -> - val seen = mutableSetOf() - println("Parents of ${conf.name}:") - printParents(conf, "", seen) - } - } -} - -fun printParents(conf: Configuration, indent: String, seen: MutableSet) { - for (parent in conf.extendsFrom) { - if (parent in seen) { - continue - } - seen.add(parent) - println("$indent - ${parent.name}") - printParents(parent, indent + " ", seen) - } -} - -tasks.register("printConfigsResolution") { - group = "debug" - doLast { - configurations.forEach { conf: Configuration -> - println() - println("Artifacts of ${conf.name}:") - if (conf.isCanBeResolved) { - try { - conf.forEach { - println(it) - } - } catch (e: Exception) { - println("error") - } - } else { - println("not resolved") - } - } - } -} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginMetadataUtils.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginMetadataUtils.java index 1ffd3c8c854..c0372bcfae5 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginMetadataUtils.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginMetadataUtils.java @@ -26,7 +26,10 @@ import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import net.minecraftforge.forgespi.language.IModInfo; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.spongepowered.common.applaunch.AppLaunch; import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.metadata.builtin.MetadataContainer; import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; import org.spongepowered.plugin.metadata.builtin.model.StandardPluginContributor; import org.spongepowered.plugin.metadata.builtin.model.StandardPluginDependency; @@ -34,12 +37,52 @@ import org.spongepowered.plugin.metadata.model.PluginDependency; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; -/** - * ModInfo cannot be mixed into as it is on the app class loader so this somewhat hacky util class will do... - */ public final class PluginMetadataUtils { + private static final Set invalidPluginIds = new HashSet<>(); + + public static MetadataContainer fixPluginIds(final MetadataContainer container) { + boolean modified = false; + final List metadata = new ArrayList<>(); + + for (final PluginMetadata plugin : container.metadata()) { + final String id = plugin.id(); + if (id.indexOf('-') >= 0) { + final String newId = id.replace('-', '_'); + if (PluginMetadataUtils.invalidPluginIds.add(id)) { + AppLaunch.pluginPlatform().logger().warn("The dash character (-) is no longer supported in plugin ids.\n" + + "Plugin {} will be loaded as {}. If you are the developer of this plugin, please change the id.", id, newId); + } + + final StandardPluginMetadata.Builder pluginBuilder = StandardPluginMetadata.builder() + .from((StandardPluginMetadata) plugin).id(newId).entrypoint(plugin.entrypoint()); + plugin.name().ifPresent(pluginBuilder::name); + plugin.description().ifPresent(pluginBuilder::description); + + metadata.add(pluginBuilder.build()); + modified = true; + } else { + metadata.add((StandardPluginMetadata) plugin); + } + } + + if (!modified) { + return container; + } + + final MetadataContainer.Builder builder = container.toBuilder(); + builder.metadata(metadata); + + try { + return builder.build(); + } catch (InvalidVersionSpecificationException e) { + // This should not happen since we never modify the version. + throw new RuntimeException(e); + } + } public static PluginMetadata modToPlugin(final ModInfo info) { final StandardPluginMetadata.Builder builder = StandardPluginMetadata.builder(); diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/ModFileParsers.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/ModFileParsers.java index de41a11126b..ad1972b3226 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/ModFileParsers.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/ModFileParsers.java @@ -35,6 +35,7 @@ import org.spongepowered.common.applaunch.AppLaunch; import org.spongepowered.common.applaunch.plugin.PluginPlatformConstants; import org.spongepowered.forge.applaunch.loading.metadata.PluginFileConfigurable; +import org.spongepowered.forge.applaunch.loading.metadata.PluginMetadataUtils; import org.spongepowered.plugin.metadata.builtin.MetadataContainer; import org.spongepowered.plugin.metadata.builtin.MetadataParser; @@ -73,7 +74,7 @@ public static IModFileInfo parsePluginMetadata(final IModFile iModFile) { container = MetadataParser.read(reader); } - final PluginFileConfigurable config = new PluginFileConfigurable(container); + final PluginFileConfigurable config = new PluginFileConfigurable(PluginMetadataUtils.fixPluginIds(container)); return new ModFileInfo(modFile, config, (info) -> {}, List.of()); } catch (final Exception e) { AppLaunch.logger().warn("Could not read metadata for plugin file '{}'", modFile, e); diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/EnvironmentPluginLocator.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/EnvironmentPluginLocator.java index 4aa59a6cd64..a0c3b100247 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/EnvironmentPluginLocator.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/EnvironmentPluginLocator.java @@ -40,7 +40,7 @@ public final class EnvironmentPluginLocator extends AbstractModProvider implemen @Override public List scanMods() { final List modFiles = new ArrayList<>(); - for (final Path[] paths : getPluginsPaths()) { + for (final Path[] paths : EnvironmentPluginLocator.getPluginsPaths()) { modFiles.add(new ModFileOrException(ModFileParsers.newPluginInstance(this, paths), null)); } return modFiles; diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/ForgePluginPlatform.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/ForgePluginPlatform.java index f054ca99da1..e72dc791b8e 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/ForgePluginPlatform.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/ForgePluginPlatform.java @@ -49,11 +49,6 @@ public final class ForgePluginPlatform implements PluginPlatform { private final Logger logger; private final List pluginDirectories; - /** - * Bootstrap - * @param environment - * @return - */ public static synchronized boolean bootstrap(final Environment environment) { if (ForgePluginPlatform.bootstrapped) { return false; diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ListenerTransformer.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ListenerTransformer.java index 62daa6ab20e..4bbaab1e6f4 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ListenerTransformer.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ListenerTransformer.java @@ -24,10 +24,6 @@ */ package org.spongepowered.forge.applaunch.transformation; -import static org.objectweb.asm.Opcodes.ACC_STATIC; -import static org.objectweb.asm.Opcodes.INVOKESTATIC; -import static org.objectweb.asm.Opcodes.RETURN; - import cpw.mods.modlauncher.api.ITransformer; import cpw.mods.modlauncher.api.ITransformerActivity; import cpw.mods.modlauncher.api.ITransformerVotingContext; @@ -38,11 +34,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.InsnNode; -import org.objectweb.asm.tree.LdcInsnNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; +import org.spongepowered.transformers.modlauncher.ListenerTransformerHelper; import java.util.HashSet; import java.util.Set; @@ -52,28 +44,7 @@ public class ListenerTransformer implements ITransformer { @NonNull @Override public ClassNode transform(final ClassNode input, final ITransformerVotingContext context) { - MethodNode clinit = null; - for (final MethodNode method : input.methods) { - if (method.name.equals("") && method.desc.equals("()V")) { - clinit = method; - break; - } - } - - if (clinit == null) { - clinit = new MethodNode(ACC_STATIC, "", "()V", null, null); - clinit.instructions.add(new InsnNode(RETURN)); - input.methods.add(clinit); - } - - - final InsnList list = new InsnList(); - list.add(new LdcInsnNode(Type.getObjectType(input.name))); - list.add(new MethodInsnNode(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false)); - list.add(new MethodInsnNode(INVOKESTATIC, "org/spongepowered/forge/launch/event/ListenerLookups", "set", "(Ljava/lang/Class;Ljava/lang/invoke/MethodHandles$Lookup;)V")); - - clinit.instructions.insert(list); - + ListenerTransformerHelper.transform(input); return input; } @@ -86,7 +57,7 @@ public TransformerVoteResult castVote(final ITransformerVotingContext context) { @NonNull @Override public Set targets() { - final Type listenerType = Type.getType("Lorg/spongepowered/api/event/Listener;"); + final Type listenerType = Type.getType(ListenerTransformerHelper.LISTENER_DESC); final Set listenerClasses = new HashSet<>(); for (ModFileInfo fileInfo : LoadingModList.get().getModFiles()) { diff --git a/forge/src/lang/java/org/spongepowered/forge/lang/provider/JavaPluginLanguageProvider.java b/forge/src/lang/java/org/spongepowered/forge/lang/provider/JavaPluginLanguageProvider.java index 5bdcef45012..5b5b7af6e68 100644 --- a/forge/src/lang/java/org/spongepowered/forge/lang/provider/JavaPluginLanguageProvider.java +++ b/forge/src/lang/java/org/spongepowered/forge/lang/provider/JavaPluginLanguageProvider.java @@ -63,12 +63,16 @@ public Consumer getFileVisitor() { final Map modTargetMap = scanResult.getAnnotations().stream() .filter(ad -> ad.annotationType().equals(JavaPluginLanguageProvider.PLUGIN_ANNOTATION)) .peek(ad -> this.logger.debug(Logging.SCAN, "Found @Plugin class {} with id {}", ad.clazz().getClassName(), ad.annotationData().get("value"))) - .map(ad -> new PluginTarget(ad.clazz().getClassName(), (String)ad.annotationData().get("value"))) + .map(ad -> new PluginTarget(ad.clazz().getClassName(), JavaPluginLanguageProvider.fixPluginId((String) ad.annotationData().get("value")))) .collect(Collectors.toMap(PluginTarget::getPlugin, Function.identity(), (a,b)->a)); scanResult.addLanguageLoader(modTargetMap); }; } + private static String fixPluginId(final String id) { + return id.replace('-', '_'); + } + private static final class PluginTarget implements IModLanguageProvider.IModLanguageLoader { private final Logger logger; diff --git a/forge/src/launch/java/org/spongepowered/forge/launch/event/ForgeEventManager.java b/forge/src/launch/java/org/spongepowered/forge/launch/event/ForgeEventManager.java index 936129a0765..1a4fd2dd074 100644 --- a/forge/src/launch/java/org/spongepowered/forge/launch/event/ForgeEventManager.java +++ b/forge/src/launch/java/org/spongepowered/forge/launch/event/ForgeEventManager.java @@ -36,9 +36,7 @@ import org.spongepowered.common.event.manager.SpongeEventManager; import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; import org.spongepowered.forge.launch.bridge.event.SpongeEventBridge_Forge; -import org.spongepowered.plugin.PluginContainer; -import java.lang.invoke.MethodHandles; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -139,11 +137,6 @@ public void start() { // EventManager - @Override - protected MethodHandles.@Nullable Lookup getLookup(final PluginContainer plugin, final Class handle) { - return ListenerLookups.get(handle); - } - @Override public boolean post(final org.spongepowered.api.event.Event event) { final SpongeEventBridge_Forge eventBridge = ((SpongeEventBridge_Forge) event); diff --git a/forge/src/launch/java/org/spongepowered/forge/launch/plugin/ForgePluginContainer.java b/forge/src/launch/java/org/spongepowered/forge/launch/plugin/ForgePluginContainer.java index 6fceac8462a..c71ed3745b5 100644 --- a/forge/src/launch/java/org/spongepowered/forge/launch/plugin/ForgePluginContainer.java +++ b/forge/src/launch/java/org/spongepowered/forge/launch/plugin/ForgePluginContainer.java @@ -72,11 +72,11 @@ public Logger logger() { } @Override - public Optional locateResource(URI relative) { + public Optional locateResource(String relative) { Objects.requireNonNull(relative, "relative"); final ClassLoader classLoader = this.modContainer.getMod().getClass().getClassLoader(); - final URL resolved = classLoader.getResource(relative.getPath()); + final URL resolved = classLoader.getResource(relative); try { if (resolved == null) { return Optional.empty(); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 805458ce703..04cb5d08c52 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,10 @@ asm = "9.7" log4j = "2.22.1" forgeAutoRenamingTool = "1.0.6" mixin = "0.8.7" -modlauncher = "8.1.3" +bootstrap = "2.1.1" +modlauncher = "10.2.1" +securemodules = "2.2.20" +jarjar = "0.3.26" guava = "32.1.2-jre" mockito = "5.11.0" jline = "3.25.1" @@ -22,13 +25,14 @@ asm-analysis = { module = "org.ow2.asm:asm-analysis", version.ref = "asm" } asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" } asm-util = { module = "org.ow2.asm:asm-util", version.ref = "asm" } asm-tree = { module = "org.ow2.asm:asm-tree", version.ref = "asm" } -grossJava9Hacks = { module = "cpw.mods:grossjava9hacks", version = "1.3.3" } joptSimple = { module = "net.sf.jopt-simple:jopt-simple", version = "5.0.4" } log4j-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j" } log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" } log4j-jpl = { module = "org.apache.logging.log4j:log4j-jpl", version.ref = "log4j" } log4j-slf4j2 = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" } -modlauncher = { module = "cpw.mods:modlauncher", version.ref = "modlauncher" } +bootstrap = { module = "net.minecraftforge:bootstrap", version.ref = "bootstrap" } +modlauncher = { module = "net.minecraftforge:modlauncher", version.ref = "modlauncher" } +securemodules = { module = "net.minecraftforge:securemodules", version.ref = "securemodules" } tinylog-api = { module = "org.tinylog:tinylog-api", version.ref = "tinylog" } tinylog-impl = { module = "org.tinylog:tinylog-impl", version.ref = "tinylog" } tinylog-slf4j = { module = "org.tinylog:slf4j-tinylog", version.ref = "tinylog" } @@ -52,6 +56,7 @@ mockito-junitJupiter = { module = "org.mockito:mockito-junit-jupiter", version.r # vanilla forgeAutoRenamingTool = { module = "net.minecraftforge:ForgeAutoRenamingTool", version.ref = "forgeAutoRenamingTool" } +jarjar-fs = { module = "net.minecraftforge:JarJarFileSystems", version.ref = "jarjar" } jline-reader = { module = "org.jline:jline-reader", version.ref = "jline" } jline-terminal = { module = "org.jline:jline-terminal", version.ref = "jline" } jline-terminalJansi = { module = "org.jline:jline-terminal-jansi", version.ref = "jline" } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index e3db472c750..e14ec7e3961 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -22,6 +22,7 @@ + @@ -490,6 +491,14 @@ + + + + + + + + @@ -527,6 +536,19 @@ + + + + + + + + + + + + + @@ -547,6 +569,11 @@ + + + + + @@ -1692,6 +1719,9 @@ + + + @@ -2452,6 +2482,9 @@ + + + @@ -2516,6 +2549,9 @@ + + + @@ -2528,6 +2564,14 @@ + + + + + + + + @@ -2540,6 +2584,9 @@ + + + @@ -2564,6 +2611,9 @@ + + + @@ -3051,6 +3101,9 @@ + + + @@ -3063,6 +3116,14 @@ + + + + + + + + @@ -3071,6 +3132,14 @@ + + + + + + + + @@ -3115,6 +3184,9 @@ + + + @@ -3662,6 +3734,11 @@ + + + + + @@ -3697,6 +3774,14 @@ + + + + + + + + @@ -4079,6 +4164,14 @@ + + + + + + + + @@ -5837,6 +5930,17 @@ + + + + + + + + + + + @@ -5852,6 +5956,14 @@ + + + + + + + + @@ -5878,11 +5990,6 @@ - - - - - diff --git a/modlauncher-patcher/build.gradle b/modlauncher-patcher/build.gradle deleted file mode 100644 index 03e64a2a5fe..00000000000 --- a/modlauncher-patcher/build.gradle +++ /dev/null @@ -1,22 +0,0 @@ -dependencies { - implementation libs.asm -} - -// Make sure jar is present for other projects -eclipse { - synchronizationTasks 'jar' -} - -tasks.named('jar') { - manifest.attributes "Premain-Class": "org.spongepowered.mlpatcher.AsmFixerAgent", - "Agent-Class": "org.spongepowered.mlpatcher.AsmFixerAgent" - -} - -indraSpotlessLicenser { - licenseHeaderFile rootProject.file("HEADER.txt") - - property "name", "Sponge" - property "organization", organization - property "url", projectUrl -} diff --git a/modlauncher-patcher/src/main/java/org/spongepowered/mlpatcher/AsmFixerAgent.java b/modlauncher-patcher/src/main/java/org/spongepowered/mlpatcher/AsmFixerAgent.java deleted file mode 100644 index fa9e00da844..00000000000 --- a/modlauncher-patcher/src/main/java/org/spongepowered/mlpatcher/AsmFixerAgent.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.mlpatcher; - -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.IllegalClassFormatException; -import java.lang.instrument.Instrumentation; -import java.security.ProtectionDomain; -import java.util.Map; -import java.util.Set; - -public class AsmFixerAgent { - private static final int ASM_VERSION = Opcodes.ASM9; - - private static Instrumentation instrumentation; - - public static void premain(final String agentArgs, final Instrumentation instrumentation) { - AsmFixerAgent.instrumentation = instrumentation; - AsmFixerAgent.setup(); - } - - public static void agentmain(final String agentArgs, final Instrumentation instrumentation) { - AsmFixerAgent.instrumentation = instrumentation; - AsmFixerAgent.setup(); - } - - private static void setup() { - AsmFixerAgent.instrumentation.addTransformer(new MLFixer()); - } - - private AsmFixerAgent() { - } - - private static class MLFixer implements ClassFileTransformer { - - record PatchInfo(Set methodsToPatch) { - - public PatchInfo(final String... methods) { - this(Set.of(methods)); - } - } - - private final Map patch = Map.of( - "cpw/mods/modlauncher/TransformerClassWriter$SuperCollectingVisitor", new PatchInfo(""), - "cpw/mods/modlauncher/ClassTransformer", new PatchInfo("transform"), - "cpw/mods/modlauncher/PredicateVisitor", new PatchInfo("") - ); - - @Override - public byte[] transform( - final Module module, - final ClassLoader loader, - final String className, - final Class classBeingRedefined, - final ProtectionDomain protectionDomain, - final byte[] classfileBuffer - ) throws IllegalClassFormatException { - if (!this.patch.containsKey(className)) { - return classfileBuffer; - } - - final ClassReader reader = new ClassReader(classfileBuffer); - final ClassWriter writer = new ClassWriter(reader, 0); - - reader.accept(new Cls(writer, this.patch.get(className)), 0); - - return writer.toByteArray(); - } - - private static class Cls extends ClassVisitor { - private final PatchInfo data; - public Cls(final ClassVisitor parent, final PatchInfo data) { - super(AsmFixerAgent.ASM_VERSION, parent); - this.data = data; - } - - @Override - public MethodVisitor visitMethod( - final int access, - final String name, - final String descriptor, - final String signature, - final String[] exceptions - ) { - final MethodVisitor parent = super.visitMethod( - access, - name, - descriptor, - signature, - exceptions - ); - - if (this.data.methodsToPatch.contains(name)) { - return new Method(parent); - } else { - return parent; - } - - } - - } - - private static class Method extends MethodVisitor { - public Method(final MethodVisitor parent) { - super(AsmFixerAgent.ASM_VERSION, parent); - } - - @Override - public void visitLdcInsn(final Object value) { - if (value instanceof Integer && ((Integer) value).intValue() == Opcodes.ASM7) { - super.visitLdcInsn(AsmFixerAgent.ASM_VERSION); - } else { - super.visitLdcInsn(value); - } - } - } - - } - - -} diff --git a/modlauncher-transformers/build.gradle.kts b/modlauncher-transformers/build.gradle.kts index 13753910654..238ee465cc3 100644 --- a/modlauncher-transformers/build.gradle.kts +++ b/modlauncher-transformers/build.gradle.kts @@ -7,7 +7,6 @@ eclipse { synchronizationTasks(tasks.jar) } - val organization: String by project val projectUrl: String by project @@ -31,14 +30,10 @@ dependencies { compileOnly(libs.modlauncher) { exclude(group = "org.ow2.asm") exclude(group = "org.apache.logging.log4j") - exclude(group = "net.sf.jopt-simple") // uses a newer version than MC } compileOnly(libs.joptSimple) compileOnly(libs.asm.commons) - compileOnly(libs.grossJava9Hacks) { - exclude(group="org.apache.logging.log4j") - } // Configurate dependencies, also to be provided by the platform // making use of this project compileOnly(platform(apiLibs.configurate.bom)) diff --git a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/AccessWidenerTransformationService.java b/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/AccessWidenerTransformationService.java index 2570580d24c..9dec53fc0ce 100644 --- a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/AccessWidenerTransformationService.java +++ b/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/AccessWidenerTransformationService.java @@ -106,10 +106,6 @@ public void argumentValues(final OptionResult option) { public void initialize(final IEnvironment environment) { } - @Override - public void beginScanning(final IEnvironment environment) { - } - @Override @SuppressWarnings("rawtypes") // :( public @NonNull List transformers() { diff --git a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/ListenerTransformerHelper.java b/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/ListenerTransformerHelper.java new file mode 100644 index 00000000000..7b65503bfa1 --- /dev/null +++ b/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/ListenerTransformerHelper.java @@ -0,0 +1,79 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.transformers.modlauncher; + +import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; +import static org.objectweb.asm.Opcodes.RETURN; + +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +public class ListenerTransformerHelper { + public static final String LISTENER_DESC = "Lorg/spongepowered/api/event/Listener;"; + + public static boolean shouldTransform(ClassNode classNode) { + for (final MethodNode method : classNode.methods) { + if (method.visibleAnnotations != null) { + for (final AnnotationNode annotation : method.visibleAnnotations) { + if (annotation.desc.equals(LISTENER_DESC)) { + return true; + } + } + } + } + return false; + } + + public static void transform(ClassNode classNode) { + MethodNode clinit = null; + for (final MethodNode method : classNode.methods) { + if (method.name.equals("") && method.desc.equals("()V")) { + clinit = method; + break; + } + } + + if (clinit == null) { + clinit = new MethodNode(ACC_STATIC, "", "()V", null, null); + clinit.instructions.add(new InsnNode(RETURN)); + classNode.methods.add(clinit); + } + + + final InsnList list = new InsnList(); + list.add(new LdcInsnNode(Type.getObjectType(classNode.name))); + list.add(new MethodInsnNode(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false)); + list.add(new MethodInsnNode(INVOKESTATIC, "org/spongepowered/common/event/ListenerLookups", "set", "(Ljava/lang/Class;Ljava/lang/invoke/MethodHandles$Lookup;)V")); + + clinit.instructions.insert(list); + } +} diff --git a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/SuperclassChanger.java b/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/SuperclassChanger.java index e0e8aacbc74..1cce82fbbe5 100644 --- a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/SuperclassChanger.java +++ b/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/SuperclassChanger.java @@ -112,10 +112,6 @@ public void argumentValues(final OptionResult option) { public void initialize(final IEnvironment environment) { } - @Override - public void beginScanning(final IEnvironment environment) { - } - @Override @SuppressWarnings("rawtypes") // :( public @NonNull List transformers() { diff --git a/settings.gradle.kts b/settings.gradle.kts index 24cd27fbdf6..50bc2b07780 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -61,7 +61,6 @@ include(":SpongeVanilla") project(":SpongeVanilla").projectDir = file("vanilla") include("modlauncher-transformers") include("generator") -include("modlauncher-patcher") val testPlugins = file("testplugins.settings.gradle.kts") if (testPlugins.exists()) { diff --git a/forge/src/launch/java/org/spongepowered/forge/launch/event/ListenerLookups.java b/src/main/java/org/spongepowered/common/event/ListenerLookups.java similarity index 97% rename from forge/src/launch/java/org/spongepowered/forge/launch/event/ListenerLookups.java rename to src/main/java/org/spongepowered/common/event/ListenerLookups.java index 0ab48d288c8..eff452c0281 100644 --- a/forge/src/launch/java/org/spongepowered/forge/launch/event/ListenerLookups.java +++ b/src/main/java/org/spongepowered/common/event/ListenerLookups.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.launch.event; +package org.spongepowered.common.event; import org.checkerframework.checker.nullness.qual.Nullable; diff --git a/src/main/java/org/spongepowered/common/event/manager/SpongeEventManager.java b/src/main/java/org/spongepowered/common/event/manager/SpongeEventManager.java index a8c6cf83398..10607676dfe 100644 --- a/src/main/java/org/spongepowered/common/event/manager/SpongeEventManager.java +++ b/src/main/java/org/spongepowered/common/event/manager/SpongeEventManager.java @@ -45,6 +45,7 @@ import org.spongepowered.api.event.item.inventory.container.InteractContainerEvent; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.world.inventory.container.ContainerBridge; +import org.spongepowered.common.event.ListenerLookups; import org.spongepowered.common.event.ShouldFire; import org.spongepowered.common.event.filter.FilterGenerator; import org.spongepowered.common.event.tracking.PhaseContext; @@ -77,7 +78,6 @@ public abstract class SpongeEventManager implements EventManager { private static final NoExceptionClosable NULL_CLOSABLE = new NoExceptionClosable(); - private static final MethodHandles.Lookup OWN_LOOKUP = MethodHandles.lookup(); public final ListenerChecker checker; private final Object lock; @@ -223,8 +223,6 @@ private void register(final RegisteredListener handler) { } } - protected abstract MethodHandles.@Nullable Lookup getLookup(final PluginContainer plugin, final Class handle); - private void registerListener(final PluginContainer plugin, final Object listenerObject, final MethodHandles.@Nullable Lookup customLookup) { Objects.requireNonNull(plugin, "plugin"); @@ -244,10 +242,10 @@ private void registerListener(final PluginContainer plugin, final Object listene MethodHandles.@Nullable Lookup lookup = customLookup; if (lookup == null) { - lookup = this.getLookup(plugin, handle); + lookup = ListenerLookups.get(handle); if (lookup == null) { - SpongeCommon.logger().warn("No lookup found for listener {}. Using Sponge's lookup as fallback.", handle.getName()); - lookup = SpongeEventManager.OWN_LOOKUP; + SpongeCommon.logger().warn("No lookup found for listener {}.", handle.getName()); + return; } } diff --git a/src/test/java/org/spongepowered/common/test/TestEventManager.java b/src/test/java/org/spongepowered/common/test/TestEventManager.java index 2f363d4202d..94a37a48fae 100644 --- a/src/test/java/org/spongepowered/common/test/TestEventManager.java +++ b/src/test/java/org/spongepowered/common/test/TestEventManager.java @@ -27,9 +27,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.manager.SpongeEventManager; import org.spongepowered.common.util.DefinableClassLoader; -import org.spongepowered.plugin.PluginContainer; - -import java.lang.invoke.MethodHandles; public class TestEventManager extends SpongeEventManager { @@ -42,9 +39,4 @@ public TestEventManager(final DefinableClassLoader loader) { public TestEventManager() { this.loader = null; } - - @Override - protected MethodHandles.@Nullable Lookup getLookup(PluginContainer plugin, Class handle) { - return this.loader == null ? null : this.loader.lookup(); - } } diff --git a/vanilla/build.gradle.kts b/vanilla/build.gradle.kts index 5fd71ad4636..524906a28a6 100644 --- a/vanilla/build.gradle.kts +++ b/vanilla/build.gradle.kts @@ -1,3 +1,5 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + plugins { id("org.spongepowered.gradle.vanilla") alias(libs.plugins.shadow) @@ -7,6 +9,9 @@ plugins { } val commonProject = parent!! +val transformersProject = parent!!.project(":modlauncher-transformers") +val testPluginsProject: Project? = rootProject.subprojects.find { "testplugins" == it.name } + val apiVersion: String by project val apiJavaTarget: String by project val minecraftVersion: String by project @@ -14,45 +19,56 @@ val recommendedVersion: String by project val organization: String by project val projectUrl: String by project -val testplugins: Project? = rootProject.subprojects.find { "testplugins".equals(it.name) } - description = "The SpongeAPI implementation for Vanilla Minecraft" version = spongeImpl.generatePlatformBuildVersionString(apiVersion, minecraftVersion, recommendedVersion) -// Vanilla extra configurations -val vanillaBootstrapLibrariesConfig = configurations.register("bootstrapLibraries") -val vanillaLibrariesConfig = configurations.register("libraries") -val mlTransformersConfig = configurations.register("mlTransformers") -val vanillaAppLaunchConfig = configurations.register("applaunch") { - extendsFrom(vanillaBootstrapLibrariesConfig.get()) +// SpongeVanilla libraries +val installerLibrariesConfig: NamedDomainObjectProvider = configurations.register("installerLibraries") +val initLibrariesConfig: NamedDomainObjectProvider = configurations.register("initLibraries") // JVM initial classpath +val bootLibrariesConfig: NamedDomainObjectProvider = configurations.register("bootLibraries") +val gameLibrariesConfig: NamedDomainObjectProvider = configurations.register("gameLibraries") { extendsFrom(configurations.minecraft.get()) - extendsFrom(mlTransformersConfig.get()) } -val mlpatcherConfig = configurations.register("mlpatcher") -val vanillaInstallerConfig = configurations.register("installer") { - extendsFrom(mlpatcherConfig.get()) - extendsFrom(mlTransformersConfig.get()) + +val gameManagedLibrariesConfig: NamedDomainObjectProvider = configurations.register("gameManagedLibraries") + +val bootShadedLibrariesConfig: NamedDomainObjectProvider = configurations.register("bootShadedLibraries") +val gameShadedLibrariesConfig: NamedDomainObjectProvider = configurations.register("gameShadedLibraries") + +val runTaskOnlyConfig: NamedDomainObjectProvider = configurations.register("runTaskOnly") + +// ModLauncher layers +val bootLayerConfig: NamedDomainObjectProvider = configurations.register("bootLayer") { + extendsFrom(initLibrariesConfig.get()) + extendsFrom(bootLibrariesConfig.get()) +} +val gameLayerConfig: NamedDomainObjectProvider = configurations.register("gameLayer") { + extendsFrom(bootLayerConfig.get()) + extendsFrom(gameLibrariesConfig.get()) } -// Common source sets and configurations -val launchConfig = commonProject.configurations.named("launch") -val accessors = commonProject.sourceSets.named("accessors") -val launch = commonProject.sourceSets.named("launch") -val applaunch = commonProject.sourceSets.named("applaunch") -val mixins = commonProject.sourceSets.named("mixins") -val main = commonProject.sourceSets.named("main") +// SpongeCommon source sets +val accessors: NamedDomainObjectProvider = commonProject.sourceSets.named("accessors") +val launch: NamedDomainObjectProvider = commonProject.sourceSets.named("launch") +val applaunch: NamedDomainObjectProvider = commonProject.sourceSets.named("applaunch") +val mixins: NamedDomainObjectProvider = commonProject.sourceSets.named("mixins") +val main: NamedDomainObjectProvider = commonProject.sourceSets.named("main") -// Vanilla source sets -val vanillaInstaller by sourceSets.register("installer") +// SpongeVanilla source sets +val vanillaInstaller by sourceSets.register("installer") { + configurations.named(implementationConfigurationName) { + extendsFrom(installerLibrariesConfig.get()) + } +} val vanillaMain by sourceSets.named("main") { // implementation (compile) dependencies spongeImpl.applyNamedDependencyOnOutput(commonProject, accessors.get(), this, project, this.implementationConfigurationName) spongeImpl.applyNamedDependencyOnOutput(commonProject, launch.get(), this, project, this.implementationConfigurationName) spongeImpl.applyNamedDependencyOnOutput(commonProject, applaunch.get(), this, project, this.implementationConfigurationName) + configurations.named(implementationConfigurationName) { - extendsFrom(vanillaLibrariesConfig.get()) - extendsFrom(vanillaBootstrapLibrariesConfig.get()) + extendsFrom(gameLayerConfig.get()) } } val vanillaLaunch by sourceSets.register("launch") { @@ -63,18 +79,17 @@ val vanillaLaunch by sourceSets.register("launch") { spongeImpl.applyNamedDependencyOnOutput(project, this, vanillaMain, project, vanillaMain.implementationConfigurationName) configurations.named(implementationConfigurationName) { - extendsFrom(vanillaLibrariesConfig.get()) - extendsFrom(vanillaAppLaunchConfig.get()) + extendsFrom(gameLayerConfig.get()) } } val vanillaAccessors by sourceSets.register("accessors") { spongeImpl.applyNamedDependencyOnOutput(commonProject, mixins.get(), this, project, this.implementationConfigurationName) spongeImpl.applyNamedDependencyOnOutput(commonProject, accessors.get(), this, project, this.implementationConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(commonProject, launch.get(), this, project, this.implementationConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(commonProject, applaunch.get(), this, project, this.implementationConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(commonProject, main.get(), this, project, this.implementationConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(project, vanillaMain, this, project, this.implementationConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(project, vanillaLaunch, this, project, this.implementationConfigurationName) + spongeImpl.applyNamedDependencyOnOutput(project, this, vanillaLaunch, project, vanillaLaunch.implementationConfigurationName) + + configurations.named(implementationConfigurationName) { + extendsFrom(gameLayerConfig.get()) + } } val vanillaMixins by sourceSets.register("mixins") { // implementation (compile) dependencies @@ -84,50 +99,31 @@ val vanillaMixins by sourceSets.register("mixins") { spongeImpl.applyNamedDependencyOnOutput(commonProject, applaunch.get(), this, project, this.implementationConfigurationName) spongeImpl.applyNamedDependencyOnOutput(commonProject, main.get(), this, project, this.implementationConfigurationName) spongeImpl.applyNamedDependencyOnOutput(project, vanillaMain, this, project, this.implementationConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(project, vanillaLaunch, this, project, this.implementationConfigurationName) spongeImpl.applyNamedDependencyOnOutput(project, vanillaAccessors, this, project, this.implementationConfigurationName) + spongeImpl.applyNamedDependencyOnOutput(project, vanillaLaunch, this, project, this.implementationConfigurationName) + + configurations.named(implementationConfigurationName) { + extendsFrom(gameLayerConfig.get()) + } +} +val vanillaLang by sourceSets.register("lang") { + configurations.named(implementationConfigurationName) { + extendsFrom(bootLayerConfig.get()) + } } val vanillaAppLaunch by sourceSets.register("applaunch") { // implementation (compile) dependencies spongeImpl.applyNamedDependencyOnOutput(commonProject, applaunch.get(), this, project, this.implementationConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(commonProject, launch.get(), vanillaLaunch, project, this.implementationConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(project, vanillaInstaller, this, project, this.implementationConfigurationName) spongeImpl.applyNamedDependencyOnOutput(project, this, vanillaLaunch, project, vanillaLaunch.implementationConfigurationName) - // runtime dependencies - literally add the rest of the project, because we want to launch the game - spongeImpl.applyNamedDependencyOnOutput(project, vanillaMixins, this, project, this.runtimeOnlyConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(project, vanillaLaunch, this, project, this.runtimeOnlyConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(commonProject, mixins.get(), this, project, this.runtimeOnlyConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(commonProject, main.get(), this, project, this.runtimeOnlyConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(commonProject, accessors.get(), this, project, this.runtimeOnlyConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(project, vanillaMain, this, project, this.runtimeOnlyConfigurationName) - spongeImpl.applyNamedDependencyOnOutput(project, vanillaAccessors, this, project, this.runtimeOnlyConfigurationName) - - configurations.named(runtimeClasspathConfigurationName) { - extendsFrom(vanillaLibrariesConfig.get()) - extendsFrom(mlpatcherConfig.get()) - extendsFrom(mlTransformersConfig.get()) - } -} -val vanillaAccessorsImplementation by configurations.named(vanillaAccessors.implementationConfigurationName) { - extendsFrom(vanillaAppLaunchConfig.get()) -} -val vanillaMixinsImplementation by configurations.named(vanillaMixins.implementationConfigurationName) { - extendsFrom(vanillaAppLaunchConfig.get()) - extendsFrom(vanillaLibrariesConfig.get()) -} -configurations.named(vanillaInstaller.implementationConfigurationName) { - extendsFrom(vanillaInstallerConfig.get()) -} - -configurations.named(vanillaAppLaunch.implementationConfigurationName) { - extendsFrom(vanillaAppLaunchConfig.get()) - extendsFrom(launchConfig.get()) + configurations.named(implementationConfigurationName) { + extendsFrom(bootLayerConfig.get()) + } } -val vanillaAppLaunchRuntime by configurations.named(vanillaAppLaunch.runtimeOnlyConfigurationName) val superclassConfigs = spongeImpl.getNamedConfigurations("superClassChanges") val mixinConfigs = spongeImpl.mixinConfigurations + minecraft { runs { // Full development environment @@ -161,27 +157,11 @@ minecraft { "-Dmixin.debug.strict=true", "-Dmixin.debug.strict.unique=false" ) - allJvmArgumentProviders += CommandLineArgumentProvider { - // todo: Mixin agent does not currently work in 0.8.4 - /*// Resolve the Mixin artifact for use as a reload agent - val mixinJar = vanillaAppLaunchConfig.get().resolvedConfiguration - .getFiles { it.name == "mixin" && it.group == "org.spongepowered" } - .firstOrNull() - - // The mixin agent initializes logging too early, which prevents jansi from properly stripping escape codes in Eclipse. - val base = if (!org.spongepowered.gradle.vanilla.internal.util.IdeConfigurer.isEclipseImport() && mixinJar != null) { - listOf("-javaagent:$mixinJar") - } else { - emptyList() - }*/ - - // Then add necessary module cracks - listOf( - "--add-exports=java.base/sun.security.util=ALL-UNNAMED", // ModLauncher - "--add-opens=java.base/java.util.jar=ALL-UNNAMED", // ModLauncher - "-javaagent:${mlpatcherConfig.get().resolvedConfiguration.files.firstOrNull()}" - ) - } + + // ModLauncher + // jvmArgs("-Dbsl.debug=true") // Uncomment to debug bootstrap classpath + mainClass("net.minecraftforge.bootstrap.ForgeBootstrap") + allArgumentProviders += CommandLineArgumentProvider { mixinConfigs.asSequence() .flatMap { sequenceOf("--mixin.config", it) } @@ -192,146 +172,177 @@ minecraft { .flatMap { sequenceOf("--superclass_change.config", it) } .toList() } - mainClass("org.spongepowered.vanilla.applaunch.Main") - classpath.setFrom( - vanillaAppLaunch.output, - vanillaAppLaunch.runtimeClasspath, - ) - ideaRunSourceSet.set(vanillaAppLaunch) + } + + all { + tasks.named(this.name, JavaExec::class) { + // Put modules in boot layer + classpath = files( + vanillaAppLaunch.output, + vanillaAppLaunch.runtimeClasspath, + runTaskOnlyConfig + ) + + // Merge applaunch sourcesets in a single module + val applaunchOutputs = files(applaunch.get().output, vanillaAppLaunch.output) + dependsOn(applaunchOutputs) + environment("MOD_CLASSES", applaunchOutputs.joinToString(";") { "applaunch%%$it" }) + + // Configure resources + val gameResources = mutableListOf() + gameResources.addAll(gameManagedLibrariesConfig.get().files.map { files(it) }) + + gameResources.add(files( + main.get().output, vanillaMain.output, + mixins.get().output, vanillaMixins.output, + accessors.get().output, vanillaAccessors.output, + launch.get().output, vanillaLaunch.output, + gameShadedLibrariesConfig.get() + )) + + dependsOn(gameResources) + jvmArgs("-Dsponge.gameResources=" + gameResources.joinToString(";") { it.joinToString("&") }) + + testPluginsProject?.also { + val plugins: FileCollection = it.sourceSets.getByName("main").output + dependsOn(plugins) + environment("SPONGE_PLUGINS", plugins.joinToString("&")) + } + } } } - commonProject.sourceSets["main"].resources + + main.get().resources .filter { it.name.endsWith(".accesswidener") } .files - .forEach { - accessWideners(it) - } + .forEach { accessWideners(it) } - project.sourceSets["main"].resources + vanillaMain.resources .filter { it.name.endsWith(".accesswidener") } .files .forEach { accessWideners(it) } } +configurations.configureEach { + // Force jopt-simple to be exactly 5.0.4 because Mojang ships that version, but some transitive dependencies request 6.0+ + resolutionStrategy { + force("net.sf.jopt-simple:jopt-simple:5.0.4") + } +} + dependencies { api(project(":", configuration = "launch")) implementation(project(":", configuration = "accessors")) implementation(project(commonProject.path)) - mlpatcherConfig.name(project(":modlauncher-patcher")) - vanillaAccessorsImplementation(project(commonProject.path)) - vanillaMixinsImplementation(project(commonProject.path)) + vanillaMixins.implementationConfigurationName(project(commonProject.path)) - val installer = vanillaInstallerConfig.name + val installer = installerLibrariesConfig.name installer(apiLibs.gson) installer(platform(apiLibs.configurate.bom)) installer(apiLibs.configurate.hocon) installer(apiLibs.configurate.core) - installer(libs.configurate.jackson) installer(libs.joptSimple) installer(libs.tinylog.api) installer(libs.tinylog.impl) - // Override ASM versions, and explicitly declare dependencies so ASM is excluded from the manifest. - val asmExclusions = sequenceOf(libs.asm.asProvider(), libs.asm.commons, libs.asm.tree, libs.asm.analysis) - .onEach { - installer(it) - }.toSet() + + installer(libs.asm.commons) + installer(libs.asm.tree) installer(libs.forgeAutoRenamingTool) { exclude(group = "net.sf.jopt-simple") - asmExclusions.forEach { exclude(group = it.get().group, module = it.get().name) } // Use our own ASM version - } - mlTransformersConfig.name(rootProject.project(":modlauncher-transformers")) - - // Add the API as a runtime dependency, just so it gets shaded into the jar - add(vanillaInstaller.runtimeOnlyConfigurationName, "org.spongepowered:spongeapi:$apiVersion") { - isTransitive = false + exclude(group = "org.ow2.asm") } - val appLaunch = vanillaAppLaunchConfig.name + val init = initLibrariesConfig.name + init(libs.securemodules) + init(libs.asm.commons) + init(libs.asm.util) + init(libs.jarjar.fs) - val bootstrapLibraries = vanillaBootstrapLibrariesConfig.name - val libraries = vanillaLibrariesConfig.name + val boot = bootLibrariesConfig.name + boot(libs.securemodules) + boot(libs.asm.commons) + boot(libs.asm.util) + boot(libs.bootstrap) - // Libraries only needed on the TCL (during main game lifecycle) - - libraries("org.spongepowered:spongeapi:$apiVersion") - libraries(platform(apiLibs.adventure.bom)) { - exclude(group = "org.jetbrains", module = "annotations") - } - libraries(libs.adventure.serializerConfigurate4) { - exclude(group = "org.checkerframework", module = "checker-qual") - } - libraries(libs.javaxInject) - libraries(libs.configurate.jackson) { - exclude(group = "org.spongepowered", module = "configurate-core") - exclude(group = "org.checkerframework", module = "checker-qual") + boot(libs.modlauncher) { + exclude(group = "org.apache.logging.log4j") } - - libraries(libs.adventure.serializerAnsi) { - exclude(group = "org.jetbrains", module = "annotations") + boot(apiLibs.pluginSpi) { exclude(group = "org.checkerframework", module = "checker-qual") + exclude(group = "org.apache.logging.log4j", module = "log4j-api") } + boot(libs.lmaxDisruptor) + boot(apiLibs.checkerQual) - // Libraries needed during applaunch phase and runtime - bootstrapLibraries(libs.terminalConsoleAppender) { + boot(libs.terminalConsoleAppender) { exclude(group = "org.jline", module = "jline-reader") exclude(group = "org.apache.logging.log4j", module = "log4j-core") } - bootstrapLibraries(apiLibs.checkerQual) - bootstrapLibraries(libs.jline.terminal) - bootstrapLibraries(libs.jline.reader) - bootstrapLibraries(libs.jline.terminalJansi) - // Must be on the base ClassLoader since ModLauncher has a dependency on log4j - bootstrapLibraries(libs.log4j.jpl) + boot(libs.jline.terminal) + boot(libs.jline.reader) + boot(libs.jline.terminalJansi) + + boot(libs.log4j.jpl) + boot(libs.log4j.api) + boot(libs.log4j.core) + boot(libs.log4j.slf4j2) - bootstrapLibraries(platform(apiLibs.configurate.bom)) - bootstrapLibraries(apiLibs.configurate.core) { + boot(platform(apiLibs.configurate.bom)) + boot(apiLibs.configurate.core) { exclude(group = "org.checkerframework", module = "checker-qual") } - bootstrapLibraries(apiLibs.configurate.hocon) { + boot(apiLibs.configurate.hocon) { exclude(group = "org.spongepowered", module = "configurate-core") exclude(group = "org.checkerframework", module = "checker-qual") } - bootstrapLibraries(libs.log4j.api) - bootstrapLibraries(libs.log4j.core) - bootstrapLibraries(libs.log4j.slf4j2) { - exclude(group = "org.slf4j", module = "slf4j-api") + boot(libs.configurate.jackson) { + exclude(group = "org.spongepowered", module = "configurate-core") + exclude(group = "org.checkerframework", module = "checker-qual") } - // Mixin and dependencies - bootstrapLibraries(libs.mixin) - bootstrapLibraries(libs.asm.util) - bootstrapLibraries(libs.asm.tree) - bootstrapLibraries(libs.guava) { + boot(libs.mixin) + boot(libs.asm.tree) + boot(libs.guava) { exclude(group = "com.google.errorprone", module = "error_prone_annotations") exclude(group = "org.checkerframework", module = "checker-qual") } - // Launch Dependencies - Needed to bootstrap the engine(s) - // Not needing to be source-visible past the init phase - // The ModLauncher compatibility launch layer - appLaunch(libs.modlauncher) { - exclude(group = "org.ow2.asm") - exclude(group = "org.apache.logging.log4j") - exclude(group = "net.sf.jopt-simple") // uses a newer version than MC + // All minecraft deps except itself + configurations.minecraft.get().resolvedConfiguration.resolvedArtifacts + .map { it.id.componentIdentifier.toString() } + .filter { !it.startsWith("net.minecraft:joined") } + .forEach { boot(it) { isTransitive = false } } + + boot(project(transformersProject.path)) + + val game = gameLibrariesConfig.name + game("org.spongepowered:spongeapi:$apiVersion") + game(platform(apiLibs.adventure.bom)) { + exclude(group = "org.jetbrains", module = "annotations") } - appLaunch(libs.asm.commons) - appLaunch(libs.grossJava9Hacks) { - exclude(group = "org.apache.logging.log4j") + game(libs.adventure.serializerConfigurate4) { + exclude(group = "org.checkerframework", module = "checker-qual") } - appLaunch(apiLibs.pluginSpi) { + game(libs.javaxInject) + game(libs.adventure.serializerAnsi) { + exclude(group = "org.jetbrains", module = "annotations") exclude(group = "org.checkerframework", module = "checker-qual") - exclude(group = "com.google.code.gson", module = "gson") - exclude(group = "org.apache.logging.log4j", module = "log4j-api") - exclude(group = "org.apache.commons", module = "commons-lang3") } - appLaunch(libs.lmaxDisruptor) - testplugins?.also { - vanillaAppLaunchRuntime(project(it.path)) { - exclude(group = "org.spongepowered") - } + val bootShadedLibraries = bootShadedLibrariesConfig.name + bootShadedLibraries(project(transformersProject.path)) { isTransitive = false } + + val gameShadedLibraries = gameShadedLibrariesConfig.name + gameShadedLibraries("org.spongepowered:spongeapi:$apiVersion") { isTransitive = false } + + afterEvaluate { + spongeImpl.copyModulesExcludingProvided(gameLibrariesConfig.get(), bootLayerConfig.get(), gameManagedLibrariesConfig.get()) } + + val runTaskOnly = runTaskOnlyConfig.name + // Allow boot layer manipulation such as merging applaunch sourcesets + runTaskOnly("net.minecraftforge:bootstrap-dev:2.1.1") } val vanillaManifest = java.manifest { @@ -348,6 +359,11 @@ val vanillaManifest = java.manifest { System.getenv()["GIT_BRANCH"]?.apply { attributes("Git-Branch" to this) } } +vanillaAppLaunch.apply { + blossom.resources { + property("minecraftVersion", minecraftVersion) + } +} vanillaLaunch.apply { blossom.resources { property("apiVersion", apiVersion) @@ -371,15 +387,12 @@ tasks { manifest{ from(vanillaManifest) attributes( - "Premain-Class" to "org.spongepowered.vanilla.installer.Agent", - "Agent-Class" to "org.spongepowered.vanilla.installer.Agent", - "Launcher-Agent-Class" to "org.spongepowered.vanilla.installer.Agent", - "Multi-Release" to true + "Main-Class" to "org.spongepowered.vanilla.installer.InstallerMain", + "Multi-Release" to true ) } from(vanillaInstaller.output) } - val vanillaAppLaunchJar by registering(Jar::class) { archiveClassifier.set("applaunch") manifest.from(vanillaManifest) @@ -411,15 +424,13 @@ tasks { val downloadNotNeeded = configurations.register("downloadNotNeeded") { extendsFrom(configurations.minecraft.get()) - extendsFrom(vanillaInstallerConfig.get()) + extendsFrom(gameShadedLibrariesConfig.get()) } val emitDependencies by registering(org.spongepowered.gradle.impl.OutputDependenciesToJson::class) { group = "sponge" - // everything in applaunch goes -> bootstrap, everything in libraries goes -> main (MC is inserted here too) - this.dependencies("bootstrap", vanillaAppLaunchConfig) - this.dependencies("main", vanillaLibrariesConfig) - // except what we're providing through the installer + this.dependencies("bootstrap", bootLibrariesConfig) + this.dependencies("main", gameManagedLibrariesConfig) this.excludedDependencies(downloadNotNeeded) outputFile.set(installerResources.map { it.file("libraries.json") }) @@ -428,54 +439,92 @@ tasks { dependsOn(emitDependencies) } - shadowJar { + val vanillaBootShadowJar by register("bootShadowJar", ShadowJar::class) { + group = "shadow" + archiveClassifier.set("boot") + mergeServiceFiles() + configurations = listOf(bootShadedLibrariesConfig.get()) - configurations = listOf(project.configurations.getByName(vanillaInstaller.runtimeClasspathConfigurationName)) + manifest { + from(vanillaManifest) + attributes("Automatic-Module-Name" to "spongevanilla.boot") + } + + from(commonProject.sourceSets.named("applaunch").map { it.output }) + from(vanillaAppLaunch.output) + } + + val installerShadowJar by register("installerShadowJar", ShadowJar::class) { + group = "shadow" + archiveClassifier.set("installer-shadow") + + mergeServiceFiles() + configurations = listOf(installerLibrariesConfig.get(), initLibrariesConfig.get()) + exclude("META-INF/INDEX.LIST", "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "**/module-info.class") - archiveClassifier.set("universal") manifest { - attributes(mapOf( - "Superclass-Transformer" to "common.superclasschange,vanilla.superclasschange", - "Access-Widener" to "common.accesswidener", - "MixinConfigs" to mixinConfigs.joinToString(","), - "Main-Class" to "org.spongepowered.vanilla.installer.InstallerMain", - "Launch-Target" to "sponge_server_prod", - "Multi-Release" to true, - "Premain-Class" to "org.spongepowered.vanilla.installer.Agent", - "Agent-Class" to "org.spongepowered.vanilla.installer.Agent", - "Launcher-Agent-Class" to "org.spongepowered.vanilla.installer.Agent" - )) + from(vanillaManifest) attributes( - mapOf("Implementation-Version" to libs.versions.asm.get()), - "org/objectweb/asm/" + "Premain-Class" to "org.spongepowered.vanilla.installer.Agent", + "Main-Class" to "org.spongepowered.vanilla.installer.InstallerMain", + "Automatic-Module-Name" to "spongevanilla.installer", + "Launch-Target" to "sponge_server_prod", + "Multi-Release" to true ) + attributes(mapOf("Implementation-Version" to libs.versions.asm.get()), "org/objectweb/asm/") + } + + from(vanillaInstaller.output) + } + + shadowJar { + group = "shadow" + archiveClassifier.set("mod") + + mergeServiceFiles() + configurations = listOf(gameShadedLibrariesConfig.get()) + + manifest { from(vanillaManifest) + attributes( + "Superclass-Transformer" to "common.superclasschange,vanilla.superclasschange", + "Access-Widener" to "common.accesswidener", + "MixinConfigs" to mixinConfigs.joinToString(","), + "Multi-Release" to true + ) } + from(commonProject.sourceSets.main.map { it.output }) - from(commonProject.sourceSets.named("mixins").map {it.output }) - from(commonProject.sourceSets.named("accessors").map {it.output }) - from(commonProject.sourceSets.named("launch").map {it.output }) - from(commonProject.sourceSets.named("applaunch").map {it.output }) - from(sourceSets.main.map {it.output }) - from(vanillaInstaller.output) - from(vanillaAppLaunch.output) + from(commonProject.sourceSets.named("mixins").map { it.output }) + from(commonProject.sourceSets.named("accessors").map { it.output }) + from(commonProject.sourceSets.named("launch").map { it.output }) + from(vanillaLaunch.output) from(vanillaAccessors.output) from(vanillaMixins.output) - /*dependencies { - // include(project(":")) - include("org.spongepowered:spongeapi:$apiVersion") - } */ + } + + val universalJar = register("universalJar", Jar::class) { + group = "build" + archiveClassifier.set("universal") + + manifest.from(installerShadowJar.manifest) - // We cannot have modules in a shaded jar - exclude("META-INF/versions/*/module-info.class") - exclude("module-info.class") + from(installerShadowJar.archiveFile.map { zipTree(it) }) + + into("jars") { + from(shadowJar) + rename("spongevanilla-(.*)-mod.jar", "spongevanilla-mod.jar") + + from(vanillaBootShadowJar) + rename("spongevanilla-(.*)-boot.jar", "spongevanilla-boot.jar") + } } + assemble { - dependsOn(shadowJar) + dependsOn(universalJar) } - } indraSpotlessLicenser { @@ -486,7 +535,7 @@ indraSpotlessLicenser { property("url", projectUrl) } -val shadowJar by tasks.existing +val universalJar by tasks.existing val vanillaInstallerJar by tasks.existing val vanillaAppLaunchJar by tasks.existing val vanillaLaunchJar by tasks.existing @@ -496,17 +545,26 @@ val vanillaMixinsJar by tasks.existing publishing { publications { register("sponge", MavenPublication::class) { + artifact(tasks["universalJar"]) - artifact(shadowJar.get()) - artifact(vanillaInstallerJar.get()) - artifact(vanillaAppLaunchJar.get()) - artifact(vanillaLaunchJar.get()) - artifact(vanillaAccessorsJar.get()) - artifact(vanillaMixinsJar.get()) - artifact(tasks["applaunchSourcesJar"]) - artifact(tasks["launchSourcesJar"]) - artifact(tasks["accessorsSourcesJar"]) + artifact(tasks["jar"]) + artifact(tasks["sourcesJar"]) + + artifact(tasks["vanillaInstallerJar"]) + artifact(tasks["installerSourcesJar"]) + + artifact(tasks["vanillaMixinsJar"]) artifact(tasks["mixinsSourcesJar"]) + + artifact(tasks["vanillaAccessorsJar"]) + artifact(tasks["accessorsSourcesJar"]) + + artifact(tasks["vanillaLaunchJar"]) + artifact(tasks["launchSourcesJar"]) + + artifact(tasks["vanillaAppLaunchJar"]) + artifact(tasks["applaunchSourcesJar"]) + pom { artifactId = project.name.lowercase() this.name.set(project.name) diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/AppCommandLine.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/AppCommandLine.java deleted file mode 100644 index 22324628acc..00000000000 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/AppCommandLine.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.vanilla.applaunch; - -import joptsimple.ArgumentAcceptingOptionSpec; -import joptsimple.OptionParser; -import joptsimple.OptionSet; -import joptsimple.util.PathConverter; -import joptsimple.util.PathProperties; -import org.spongepowered.vanilla.installer.Constants; - -import java.io.InputStream; -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.jar.Manifest; - -public final class AppCommandLine { - - private static final OptionParser PARSER = new OptionParser(); - private static final ArgumentAcceptingOptionSpec LAUNCH_TARGET_ARG = AppCommandLine.PARSER.accepts("launchTarget", "Launch Target") - .withRequiredArg(); - private static final ArgumentAcceptingOptionSpec GAME_DIRECTORY_ARG = AppCommandLine.PARSER.accepts("gameDir", "Alternative game directory") - .withRequiredArg().withValuesConvertedBy(new PathConverter(PathProperties.DIRECTORY_EXISTING)).defaultsTo(Paths.get(".")); - - public static String[] RAW_ARGS; - public static AppLaunchTargets launchTarget; - public static Path gameDirectory; - - public static void configure(String[] args) throws Exception { - AppCommandLine.PARSER.allowsUnrecognizedOptions(); - - final OptionSet options = AppCommandLine.PARSER.parse(args); - - String launchTarget = options.valueOf(AppCommandLine.LAUNCH_TARGET_ARG); - boolean manifestTarget = false; - if (launchTarget == null) { - final Path executionJar = Paths.get(AppCommandLine.class.getProtectionDomain().getCodeSource().getLocation().toURI()); - try (final FileSystem system = FileSystems.newFileSystem(executionJar, (ClassLoader) null)) { - final Path manifestFile = system.getPath("META-INF", "MANIFEST.MF"); - if (Files.notExists(manifestFile)) { - throw new RuntimeException("The installer contains no manifest!"); - } - - // Try the manifest before we give up - try (final InputStream stream = Files.newInputStream(manifestFile)) { - final Manifest manifest = new Manifest(stream); - launchTarget = manifest.getMainAttributes().getValue(Constants.ManifestAttributes.LAUNCH_TARGET); - manifestTarget = true; - } - } - } - - if (launchTarget == null) { - throw new RuntimeException("No launch target has been specified! Check your run configs/manifest..."); - } - - AppCommandLine.launchTarget = AppLaunchTargets.from(launchTarget); - if (AppCommandLine.launchTarget == null) { - throw new RuntimeException("Invalid launch target specified!"); - } - - if (manifestTarget) { - String[] temp = new String[args.length + 2]; - System.arraycopy(args, 0, temp, 0, args.length); - temp[args.length] = "--launchTarget"; - temp[args.length + 1] = launchTarget; - args = temp; - } - - AppCommandLine.gameDirectory = options.valueOf(AppCommandLine.GAME_DIRECTORY_ARG); - - AppCommandLine.RAW_ARGS = args; - } - - private AppCommandLine() { - } -} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/AppLaunchTargets.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/AppLaunchTarget.java similarity index 68% rename from vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/AppLaunchTargets.java rename to vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/AppLaunchTarget.java index 8cf0b25ee40..22f22fd4f0d 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/AppLaunchTargets.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/AppLaunchTarget.java @@ -24,7 +24,7 @@ */ package org.spongepowered.vanilla.applaunch; -public enum AppLaunchTargets { +public enum AppLaunchTarget { CLIENT_DEVELOPMENT("sponge_client_dev"), CLIENT_PRODUCTION("sponge_client_prod"), SERVER_DEVELOPMENT("sponge_server_dev"), @@ -34,7 +34,7 @@ public enum AppLaunchTargets { private final String launchTarget; - AppLaunchTargets(final String launchTarget) { + AppLaunchTarget(final String launchTarget) { this.launchTarget = launchTarget; } @@ -42,23 +42,15 @@ public String getLaunchTarget() { return this.launchTarget; } - public static AppLaunchTargets from(final String launchTarget) { - - switch (launchTarget) { - case "sponge_client_dev": - return AppLaunchTargets.CLIENT_DEVELOPMENT; - case "sponge_client_prod": - return AppLaunchTargets.CLIENT_PRODUCTION; - case "sponge_server_dev": - return AppLaunchTargets.SERVER_DEVELOPMENT; - case "sponge_server_prod": - return AppLaunchTargets.SERVER_PRODUCTION; - case "sponge_client_it": - return AppLaunchTargets.CLIENT_INTEGRATION_TEST; - case "sponge_server_it": - return AppLaunchTargets.SERVER_INTEGRATION_TEST; - } - - return null; + public static AppLaunchTarget from(final String launchTarget) { + return switch (launchTarget) { + case "sponge_client_dev" -> AppLaunchTarget.CLIENT_DEVELOPMENT; + case "sponge_client_prod" -> AppLaunchTarget.CLIENT_PRODUCTION; + case "sponge_server_dev" -> AppLaunchTarget.SERVER_DEVELOPMENT; + case "sponge_server_prod" -> AppLaunchTarget.SERVER_PRODUCTION; + case "sponge_client_it" -> AppLaunchTarget.CLIENT_INTEGRATION_TEST; + case "sponge_server_it" -> AppLaunchTarget.SERVER_INTEGRATION_TEST; + default -> null; + }; } } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/Constants.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/Constants.java new file mode 100644 index 00000000000..969dea1eb7c --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/Constants.java @@ -0,0 +1,33 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch; + +public final class Constants { + + public static final class ManifestAttributes { + public static final String ACCESS_WIDENER = "Access-Widener"; + public static final String SUPERCLASS_CHANGE = "Superclass-Transformer"; + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/Main.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/Main.java deleted file mode 100644 index b3e96d20476..00000000000 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/Main.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.vanilla.applaunch; - -import cpw.mods.modlauncher.Launcher; -import org.fusesource.jansi.AnsiConsole; -import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.common.applaunch.plugin.PluginPlatformConstants; -import org.spongepowered.plugin.builtin.StandardEnvironment; -import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; -import org.spongepowered.vanilla.applaunch.util.ArgumentList; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -public final class Main { - - static { - AnsiConsole.systemInstall(); - } - - private final VanillaPluginPlatform pluginPlatform; - private final Path[] extraPaths; - - public Main(final Path[] extraPaths) { - this.pluginPlatform = AppLaunch.setPluginPlatform(new VanillaPluginPlatform(new StandardEnvironment())); - this.extraPaths = extraPaths; - } - - public static void main(final String[] args) throws Exception { - Main.main(args, new Path[0]); - } - - public static void main(final String[] args, final Path[] extraPaths) throws Exception { - AppCommandLine.configure(args); - new Main(extraPaths).run(); - } - - public void run() throws IOException { - final String implementationVersion = StandardEnvironment.class.getPackage().getImplementationVersion(); - - this.pluginPlatform.setVersion(implementationVersion == null ? "dev" : implementationVersion); - this.pluginPlatform.setBaseDirectory(AppCommandLine.gameDirectory); - - final Path modsDirectory = AppCommandLine.gameDirectory.resolve("mods"); - if (Files.notExists(modsDirectory)) { - Files.createDirectories(modsDirectory); - } - final Path pluginsDirectory = AppCommandLine.gameDirectory.resolve("plugins"); - final List pluginDirectories = new ArrayList<>(); - pluginDirectories.add(modsDirectory); - if (Files.exists(pluginsDirectory)) { - pluginDirectories.add(pluginsDirectory); - } - this.pluginPlatform.setPluginDirectories(pluginDirectories); - this.pluginPlatform.setMetadataFilePath(PluginPlatformConstants.METADATA_FILE_LOCATION); - - // Extra paths that are on the TCL but not the system loader - this.pluginPlatform.getStandardEnvironment().blackboard().getOrCreate( - VanillaPluginPlatform.EXTRA_TRANSFORMABLE_PATHS, - () -> Collections.unmodifiableList(Arrays.asList(this.extraPaths)) - ); - - AppLaunch.logger().info("Transitioning to ModLauncher, please wait..."); - final ArgumentList lst = ArgumentList.from(AppCommandLine.RAW_ARGS); - Launcher.main(lst.getArguments()); - } -} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/AbstractVanillaLaunchHandler.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/AbstractVanillaLaunchHandler.java index 05562bdd3de..269bf6c5ea9 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/AbstractVanillaLaunchHandler.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/AbstractVanillaLaunchHandler.java @@ -24,327 +24,46 @@ */ package org.spongepowered.vanilla.applaunch.handler; -import cpw.mods.gross.Java9ClassLoaderUtil; -import cpw.mods.modlauncher.TransformingClassLoader; import cpw.mods.modlauncher.api.ILaunchHandlerService; -import cpw.mods.modlauncher.api.ITransformingClassLoader; -import cpw.mods.modlauncher.api.ITransformingClassLoaderBuilder; +import cpw.mods.modlauncher.api.ServiceRunner; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.plugin.PluginResource; -import org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource; -import org.spongepowered.plugin.builtin.jvm.locator.ResourceType; -import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; +import org.spongepowered.vanilla.applaunch.AppLaunchTarget; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Field; -import java.net.JarURLConnection; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLConnection; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; import java.util.NoSuchElementException; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.jar.JarFile; -import java.util.jar.Manifest; /** * The common Sponge {@link ILaunchHandlerService launch handler} for development * and production environments. */ public abstract class AbstractVanillaLaunchHandler implements ILaunchHandlerService { - - private static final String JAVA_HOME_PATH = System.getProperty("java.home"); protected final Logger logger = LogManager.getLogger("launch"); - /** - * Classes or packages that mark jar files that should be excluded from the transformation path - */ - protected static final String[] NON_TRANSFORMABLE_PATHS = { - "org/spongepowered/asm/", // Mixin (for obvious reasons) - // because NIO Paths use different normalization than Instrumentation.appendToSystemClassLoaderSearch() - // (NIO uses uppercase URL encoding (ex. %2D), Instrumentation does not (ex. %2d)), this cannot appear in the transformer path at all - // This suppresses a warning from LoggerFactory.findPossibleStaticLoggerBinderPathSet - "org/slf4j/impl/", // slf4j - }; - - /** - * A list of packages to exclude from the {@link TransformingClassLoader transforming class loader}, - * to be registered with {@link ITransformingClassLoader#addTargetPackageFilter(Predicate)}. - *

- * Packages should be scoped as tightly as possible - for example {@code "com.google.common."} is - * preferred over {@code "com.google."}. - *

- * Packages should always include a trailing full stop - for example if {@code "org.neptune"} was - * excluded, classes in {@code "org.neptunepowered"} would also be excluded. The correct usage would - * be to exclude {@code "org.neptune."}. - */ - private static final String[] EXCLUDED_PACKAGES = { - "org.spongepowered.plugin.", - "org.spongepowered.common.applaunch.", - "org.spongepowered.vanilla.applaunch.", - // configurate 4 - "io.leangen.geantyref.", - "org.spongepowered.configurate.", - // terminal console bits - "org.jline.", - "org.fusesource.", - "net.minecrell.terminalconsole.", - "org.slf4j.", - // Maven artifacts -- specifically for versioning - "org.apache.maven.artifact.", - // HotswapAgent - "org.hotswap.agent." - }; - - private static final String[] EXCLUSION_EXCEPTIONS = { - "org.spongepowered.configurate.objectmapping.guice.", - "org.spongepowered.configurate.yaml.", - "org.spongepowered.configurate.gson.", - "org.spongepowered.configurate.jackson.", - "org.spongepowered.configurate.xml.", - }; - - protected boolean isDev() { - return false; - } - @Override - public void configureTransformationClassLoader(final ITransformingClassLoaderBuilder builder) { - // Specifically requested to be available on the launch loader - final VanillaPluginPlatform platform = AppLaunch.pluginPlatform(); - for (final Path path : platform.getStandardEnvironment().blackboard().getOrCreate(VanillaPluginPlatform.EXTRA_TRANSFORMABLE_PATHS, () -> Collections.emptyList())) { - builder.addTransformationPath(path); - } - - // Plus everything else on the system loader - // todo: we might be able to eliminate this at some point, but that causes complications - if (this.isDev()) { - for (final URL url : Java9ClassLoaderUtil.getSystemClassPathURLs()) { - try { - final URI uri = url.toURI(); - if (!this.isTransformable(uri)) { - this.logger.debug("Non-transformable system classpath entry: {}", uri); - continue; - } - - builder.addTransformationPath(Paths.get(uri)); - this.logger.debug("Transformable system classpath entry: {}", uri); - } catch (final URISyntaxException | IOException ex) { - this.logger.error("Failed to add {} to transformation path", url, ex); - } - } - } - - builder.setResourceEnumeratorLocator(this.getResourceLocator()); - builder.setManifestLocator(this.getManifestLocator()); - } - - protected boolean isTransformable(final URI uri) throws URISyntaxException, IOException { - final Path p = Path.of(uri); - - // in Java 8 ONLY, the system classpath contains JVM internals - // let's make sure those don't get transformed - if (p.toAbsolutePath().startsWith(AbstractVanillaLaunchHandler.JAVA_HOME_PATH)) { - return false; - } - - if (!Files.exists(p)) { - return false; - } - - final BasicFileAttributes basicFileAttributes = Files.readAttributes(p, BasicFileAttributes.class); - if (basicFileAttributes.isDirectory()) { - for (final String test : AbstractVanillaLaunchHandler.NON_TRANSFORMABLE_PATHS) { - if (Files.exists(p.resolve(test))) { - return false; - } - } - } else if (basicFileAttributes.isRegularFile()) { - try (final JarFile jf = new JarFile(new File(uri))) { - for (final String test : AbstractVanillaLaunchHandler.NON_TRANSFORMABLE_PATHS) { - if (jf.getEntry(test) != null) { - return false; - } - } - } - } - return true; + public String name() { + return this.target().getLaunchTarget(); } @Override - public Callable launchService(final String[] arguments, final ITransformingClassLoader launchClassLoader) { + public ServiceRunner launchService(final String[] arguments, final ModuleLayer gameLayer) { this.logger.info("Transitioning to Sponge launch, please wait..."); - - launchClassLoader.addTargetPackageFilter(klass -> { - outer: for (final String pkg : AbstractVanillaLaunchHandler.EXCLUDED_PACKAGES) { - if (klass.startsWith(pkg)) { - for (final String exception : AbstractVanillaLaunchHandler.EXCLUSION_EXCEPTIONS) { - if (klass.startsWith(exception)) { - break outer; - } - } - return false; - } - } - return true; - }); - AbstractVanillaLaunchHandler.fixPackageExclusions(launchClassLoader); - return () -> { - this.launchService0(arguments, launchClassLoader); - return null; + final Module module = gameLayer.findModule("spongevanilla").orElseThrow(() -> new NoSuchElementException("Module spongevanilla")); + this.launchSponge(module, arguments); }; } - @SuppressWarnings({"rawtypes", "unchecked"}) - private static void fixPackageExclusions(final ITransformingClassLoader tcl) { - try { - final Field prefixField = tcl.getClass().getDeclaredField("SKIP_PACKAGE_PREFIXES"); - prefixField.setAccessible(true); - ((List) prefixField.get(null)).set(1, "__javax__noplswhy."); - } catch (NoSuchFieldException | IllegalAccessException ex) { - throw new RuntimeException("Failed to fix strange transformer exclusions", ex); - } - } - - protected Function> getResourceLocator() { - return s -> { - // Save unnecessary searches of plugin classes for things that are definitely not plugins - // In this case: MC and fastutil - if (s.startsWith("net/minecraft") || s.startsWith("it/unimi")) { - return Collections.emptyEnumeration(); - } - - final URI asUri; - try { - asUri = new URI(null, null, s, null); - } catch (final URISyntaxException ex) { - this.logger.error("Failed to convert resource path {} to a URI", s, ex); - return Collections.emptyEnumeration(); - } - - return new Enumeration() { - final Iterator> serviceResources = ((VanillaPluginPlatform) AppLaunch.pluginPlatform()).getResources() - .values().iterator(); - Iterator resources; - URL next = this.computeNext(); - - @Override - public boolean hasMoreElements() { - return this.next != null; - } - - @Override - public URL nextElement() { - final URL next = this.next; - if (next == null) { - throw new NoSuchElementException(); - } - this.next = this.computeNext(); - return next; - } - - private URL computeNext() { - while (true) { - if (this.resources != null && !this.resources.hasNext()) { - this.resources = null; - } - if (this.resources == null) { - if (!this.serviceResources.hasNext()) { - return null; - } - this.resources = this.serviceResources.next().iterator(); - } - - if (this.resources.hasNext()) { - final PluginResource resource = this.resources.next(); - if (resource instanceof JVMPluginResource) { - if (((JVMPluginResource) resource).type() != ResourceType.JAR) { - continue; - } - } - - final Optional uri = resource.locateResource(asUri); - if (uri.isPresent()) { - try { - return uri.get().toURL(); - } catch (final MalformedURLException ex) { - throw new RuntimeException(ex); - } - } - } - } - } - }; - }; - } - - private final ConcurrentMap> manifestCache = new ConcurrentHashMap<>(); - private static final Optional UNKNOWN_MANIFEST = Optional.of(new Manifest()); - - private Function> getManifestLocator() { - return connection -> { - if (connection instanceof JarURLConnection) { - final URL jarFileUrl = ((JarURLConnection) connection).getJarFileURL(); - final Optional manifest = this.manifestCache.computeIfAbsent(jarFileUrl, key -> { - for (final Set resources : ((VanillaPluginPlatform) AppLaunch.pluginPlatform()).getResources().values()) { - for (final PluginResource resource : resources) { - if (resource instanceof JVMPluginResource) { - final JVMPluginResource jvmResource = (JVMPluginResource) resource; - try { - if (jvmResource.type() == ResourceType.JAR && ((JVMPluginResource) resource).path().toAbsolutePath().normalize().equals(Paths.get(key.toURI()).toAbsolutePath().normalize())) { - return jvmResource.manifest(); - } - } catch (final URISyntaxException ex) { - this.logger.error("Failed to load manifest from jar {}: ", key, ex); - } - } - } - } - return AbstractVanillaLaunchHandler.UNKNOWN_MANIFEST; - }); - - try { - if (manifest == AbstractVanillaLaunchHandler.UNKNOWN_MANIFEST) { - return Optional.ofNullable(((JarURLConnection) connection).getManifest()); - } else { - return manifest; - } - } catch (final IOException ex) { - this.logger.error("Failed to load manifest from jar {}: ", jarFileUrl, ex); - } - } - return Optional.empty(); - }; - } + public abstract AppLaunchTarget target(); /** * Launch the service (Minecraft). *

* Take care to ONLY load classes on the provided - * {@link ClassLoader class loader}, which can be retrieved with {@link ITransformingClassLoader#getInstance()}. + * {@link Module module}. * + * @param module The sponge module to load classes with * @param arguments The arguments to launch the service with - * @param launchClassLoader The transforming class loader to load classes with * @throws Exception This can be any exception that occurs during the launch process */ - protected abstract void launchService0(final String[] arguments, final ITransformingClassLoader launchClassLoader) throws Exception; + protected abstract void launchSponge(final Module module, final String[] arguments) throws Exception; } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/dev/ClientDevLaunchHandler.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/dev/ClientDevLaunchHandler.java index 69ee9f6288d..d8a8ceecd91 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/dev/ClientDevLaunchHandler.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/dev/ClientDevLaunchHandler.java @@ -24,27 +24,21 @@ */ package org.spongepowered.vanilla.applaunch.handler.dev; -import cpw.mods.modlauncher.api.ITransformingClassLoader; import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.vanilla.applaunch.AppLaunchTargets; +import org.spongepowered.vanilla.applaunch.AppLaunchTarget; import org.spongepowered.vanilla.applaunch.handler.AbstractVanillaLaunchHandler; import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; public final class ClientDevLaunchHandler extends AbstractVanillaLaunchHandler { @Override - protected boolean isDev() { - return true; + public AppLaunchTarget target() { + return AppLaunchTarget.CLIENT_DEVELOPMENT; } @Override - public String name() { - return AppLaunchTargets.CLIENT_DEVELOPMENT.getLaunchTarget(); - } - - @Override - protected void launchService0(final String[] arguments, final ITransformingClassLoader launchClassLoader) throws Exception { - Class.forName("org.spongepowered.vanilla.launch.ClientLaunch", true, launchClassLoader.getInstance()) + protected void launchSponge(final Module module, final String[] arguments) throws Exception { + Class.forName(module, "org.spongepowered.vanilla.launch.ClientLaunch") .getMethod("launch", VanillaPluginPlatform.class, Boolean.class, String[].class) .invoke(null, AppLaunch.pluginPlatform(), Boolean.TRUE, arguments); } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/dev/ServerDevLaunchHandler.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/dev/ServerDevLaunchHandler.java index 1b54be0b2a0..c2b5e72cd3b 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/dev/ServerDevLaunchHandler.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/dev/ServerDevLaunchHandler.java @@ -24,27 +24,21 @@ */ package org.spongepowered.vanilla.applaunch.handler.dev; -import cpw.mods.modlauncher.api.ITransformingClassLoader; import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.vanilla.applaunch.AppLaunchTargets; +import org.spongepowered.vanilla.applaunch.AppLaunchTarget; import org.spongepowered.vanilla.applaunch.handler.AbstractVanillaLaunchHandler; import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; public final class ServerDevLaunchHandler extends AbstractVanillaLaunchHandler { @Override - protected boolean isDev() { - return true; + public AppLaunchTarget target() { + return AppLaunchTarget.SERVER_DEVELOPMENT; } @Override - public String name() { - return AppLaunchTargets.SERVER_DEVELOPMENT.getLaunchTarget(); - } - - @Override - protected void launchService0(final String[] arguments, final ITransformingClassLoader launchClassLoader) throws Exception { - Class.forName("org.spongepowered.vanilla.launch.DedicatedServerLaunch", true, launchClassLoader.getInstance()) + protected void launchSponge(final Module module, final String[] arguments) throws Exception { + Class.forName(module, "org.spongepowered.vanilla.launch.DedicatedServerLaunch") .getMethod("launch", VanillaPluginPlatform.class, Boolean.class, String[].class) .invoke(null, AppLaunch.pluginPlatform(), Boolean.TRUE, arguments); } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/prod/ServerProdLaunchHandler.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/prod/ServerProdLaunchHandler.java index 8476ce4557d..f326f93b4d6 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/prod/ServerProdLaunchHandler.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/prod/ServerProdLaunchHandler.java @@ -24,27 +24,21 @@ */ package org.spongepowered.vanilla.applaunch.handler.prod; -import cpw.mods.modlauncher.api.ITransformingClassLoader; import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.vanilla.applaunch.AppLaunchTargets; +import org.spongepowered.vanilla.applaunch.AppLaunchTarget; import org.spongepowered.vanilla.applaunch.handler.AbstractVanillaLaunchHandler; import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; public final class ServerProdLaunchHandler extends AbstractVanillaLaunchHandler { @Override - protected boolean isDev() { - return false; + public AppLaunchTarget target() { + return AppLaunchTarget.SERVER_PRODUCTION; } @Override - public String name() { - return AppLaunchTargets.SERVER_PRODUCTION.getLaunchTarget(); - } - - @Override - protected void launchService0(final String[] arguments, final ITransformingClassLoader launchClassLoader) throws Exception { - Class.forName("org.spongepowered.vanilla.launch.DedicatedServerLaunch", true, launchClassLoader.getInstance()) + protected void launchSponge(final Module module, final String[] arguments) throws Exception { + Class.forName(module, "org.spongepowered.vanilla.launch.DedicatedServerLaunch") .getMethod("launch", VanillaPluginPlatform.class, Boolean.class, String[].class) .invoke(null, AppLaunch.pluginPlatform(), Boolean.FALSE, arguments); } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/test/ClientTestLaunchHandler.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/test/ClientTestLaunchHandler.java index fd4b9957d7b..c06d849bf3d 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/test/ClientTestLaunchHandler.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/test/ClientTestLaunchHandler.java @@ -24,23 +24,22 @@ */ package org.spongepowered.vanilla.applaunch.handler.test; -import cpw.mods.modlauncher.api.ITransformingClassLoader; import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.vanilla.applaunch.AppLaunchTargets; +import org.spongepowered.vanilla.applaunch.AppLaunchTarget; import org.spongepowered.vanilla.applaunch.handler.AbstractVanillaLaunchHandler; import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; public class ClientTestLaunchHandler extends AbstractVanillaLaunchHandler { @Override - public String name() { - return AppLaunchTargets.CLIENT_INTEGRATION_TEST.getLaunchTarget(); + public AppLaunchTarget target() { + return AppLaunchTarget.CLIENT_INTEGRATION_TEST; } @Override - protected void launchService0(final String[] arguments, final ITransformingClassLoader launchClassLoader) throws Exception { - Class.forName("org.spongepowered.vanilla.launch.IntegrationTestLaunch", true, launchClassLoader.getInstance()) - .getMethod("launch", VanillaPluginPlatform.class, Boolean.class, String[].class) - .invoke(null, AppLaunch.pluginPlatform(), /* isServer = */ Boolean.FALSE, arguments); + protected void launchSponge(final Module module, final String[] arguments) throws Exception { + Class.forName(module, "org.spongepowered.vanilla.launch.IntegrationTestLaunch") + .getMethod("launch", VanillaPluginPlatform.class, Boolean.class, String[].class) + .invoke(null, AppLaunch.pluginPlatform(), /* isServer = */ Boolean.FALSE, arguments); } } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/test/ServerTestLaunchHandler.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/test/ServerTestLaunchHandler.java index 0f5367bca37..3a1d4b9a4ef 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/test/ServerTestLaunchHandler.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/handler/test/ServerTestLaunchHandler.java @@ -24,23 +24,22 @@ */ package org.spongepowered.vanilla.applaunch.handler.test; -import cpw.mods.modlauncher.api.ITransformingClassLoader; import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.vanilla.applaunch.AppLaunchTargets; +import org.spongepowered.vanilla.applaunch.AppLaunchTarget; import org.spongepowered.vanilla.applaunch.handler.AbstractVanillaLaunchHandler; import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; public class ServerTestLaunchHandler extends AbstractVanillaLaunchHandler { @Override - public String name() { - return AppLaunchTargets.SERVER_INTEGRATION_TEST.getLaunchTarget(); + public AppLaunchTarget target() { + return AppLaunchTarget.SERVER_INTEGRATION_TEST; } @Override - protected void launchService0(final String[] arguments, final ITransformingClassLoader launchClassLoader) throws Exception { - Class.forName("org.spongepowered.vanilla.launch.IntegrationTestLaunch", true, launchClassLoader.getInstance()) - .getMethod("launch", VanillaPluginPlatform.class, Boolean.class, String[].class) - .invoke(null, AppLaunch.pluginPlatform(), /* isServer = */ Boolean.TRUE, arguments); + protected void launchSponge(final Module module, final String[] arguments) throws Exception { + Class.forName(module, "org.spongepowered.vanilla.launch.IntegrationTestLaunch") + .getMethod("launch", VanillaPluginPlatform.class, Boolean.class, String[].class) + .invoke(null, AppLaunch.pluginPlatform(), /* isServer = */ Boolean.TRUE, arguments); } } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/JavaPluginLanguageService.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/JavaPluginLanguageService.java new file mode 100644 index 00000000000..07b026699e3 --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/JavaPluginLanguageService.java @@ -0,0 +1,70 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.plugin; + +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.PluginCandidate; +import org.spongepowered.plugin.PluginResource; +import org.spongepowered.plugin.builtin.StandardPluginCandidate; +import org.spongepowered.plugin.builtin.StandardPluginLanguageService; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; + +import java.io.InputStream; +import java.nio.file.Files; +import java.util.LinkedList; +import java.util.List; + +public final class JavaPluginLanguageService extends StandardPluginLanguageService { + private final static String NAME = "java_plain"; + + @Override + public String name() { + return JavaPluginLanguageService.NAME; + } + + @Override + public String pluginLoader() { + return "org.spongepowered.vanilla.launch.plugin.JavaPluginLoader"; + } + + @Override + public List createPluginCandidates(final Environment environment, final PluginResource resource) throws Exception { + if (resource instanceof JVMPluginResource jvmResource && Files.exists(jvmResource.resourcesRoot().resolve("net/minecraft/server/MinecraftServer.class"))) { + this.logger.debug("Container in path '{}' has been detected as Minecraft.", resource.path()); + + final List candidates = new LinkedList<>(); + try (final InputStream stream = JavaPluginLanguageService.class.getClassLoader().getResourceAsStream("META-INF/minecraft_sponge_plugins.json")) { + for (final PluginMetadata metadata : loadMetadataContainer(environment, stream).metadata()) { + candidates.add(new StandardPluginCandidate(metadata, resource)); + } + } + + return candidates; + } + + return super.createPluginCandidates(environment, resource); + } +} diff --git a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/JavaPluginLanguageService.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ListenerPluginService.java similarity index 56% rename from vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/JavaPluginLanguageService.java rename to vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ListenerPluginService.java index 04a58be0df4..6953b18551c 100644 --- a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/JavaPluginLanguageService.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ListenerPluginService.java @@ -22,36 +22,35 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.launch.plugin; +package org.spongepowered.vanilla.applaunch.plugin; -import org.spongepowered.plugin.Environment; -import org.spongepowered.plugin.builtin.jvm.JVMPluginLanguageService; -import org.spongepowered.plugin.metadata.Container; -import org.spongepowered.plugin.metadata.builtin.MetadataParser; +import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.transformers.modlauncher.ListenerTransformerHelper; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; +import java.util.EnumSet; -public final class JavaPluginLanguageService extends JVMPluginLanguageService { - - private final static String NAME = "java_plain"; +public class ListenerPluginService implements ILaunchPluginService { + private static final EnumSet YAY = EnumSet.of(Phase.AFTER); + private static final EnumSet NAY = EnumSet.noneOf(Phase.class); @Override public String name() { - return JavaPluginLanguageService.NAME; + return "listener"; } @Override - public String pluginLoader() { - return "org.spongepowered.vanilla.launch.plugin.JavaPluginLoader"; + public EnumSet handlesClass(Type classType, boolean isEmpty) { + return isEmpty ? NAY : YAY; } @Override - public Container loadMetadataContainer(final Environment environment, final InputStream stream) throws Exception { - final BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); - return MetadataParser.read(reader, MetadataParser.gsonBuilder().create()); + public int processClassWithFlags(final Phase phase, final ClassNode classNode, final Type classType, final String reason) { + if (ListenerTransformerHelper.shouldTransform(classNode)) { + ListenerTransformerHelper.transform(classNode); + return ComputeFlags.COMPUTE_FRAMES; + } + return ComputeFlags.NO_REWRITE; } - } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ResourceType.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ResourceType.java new file mode 100644 index 00000000000..adeece5a25d --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ResourceType.java @@ -0,0 +1,50 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.plugin; + +import cpw.mods.jarhandling.SecureJar; +import org.spongepowered.plugin.PluginResource; + +import java.util.Locale; + +public enum ResourceType { + SERVICE, // service layer + LANGUAGE, // plugin layer + PLUGIN; // game layer + + public static final String PROPERTY_NAME = "Resource-Type"; + + public static ResourceType of(final PluginResource resource) { + return ResourceType.fromName(resource.property(PROPERTY_NAME).orElse(null)); + } + + public static ResourceType of(final SecureJar jar) { + return ResourceType.fromName(jar.moduleDataProvider().getManifest().getMainAttributes().getValue(PROPERTY_NAME)); + } + + public static ResourceType fromName(final String name) { + return name == null ? ResourceType.PLUGIN : ResourceType.valueOf(name.toUpperCase(Locale.ROOT)); + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaPlatformService.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaPlatformService.java deleted file mode 100644 index 14c233cea90..00000000000 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaPlatformService.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.vanilla.applaunch.plugin; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; -import cpw.mods.modlauncher.api.IEnvironment; -import cpw.mods.modlauncher.api.ITransformationService; -import cpw.mods.modlauncher.api.ITransformer; -import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.asm.launch.MixinLaunchPluginLegacy; -import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.plugin.PluginResource; -import org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource; -import org.spongepowered.transformers.modlauncher.AccessWidenerTransformationService; -import org.spongepowered.transformers.modlauncher.SuperclassChanger; -import org.spongepowered.vanilla.installer.Constants; - -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public final class VanillaPlatformService implements ITransformationService { - - private static final String NAME = "vanilla_platform"; - - private static final VanillaPluginPlatform pluginPlatform = AppLaunch.pluginPlatform(); - - @Override - public @NonNull String name() { - return VanillaPlatformService.NAME; - } - - @Override - public void initialize(final IEnvironment environment) { - VanillaPlatformService.pluginPlatform.initialize(); - } - - @Override - public void beginScanning(final IEnvironment environment) { - //NOOP - } - - @Override - public List> runScan(final IEnvironment environment) { - VanillaPlatformService.pluginPlatform.locatePluginResources(); - VanillaPlatformService.pluginPlatform.createPluginCandidates(); - final AccessWidenerTransformationService accessWidener = environment.getProperty(AccessWidenerTransformationService.INSTANCE.get()).orElse(null); - final SuperclassChanger superclassChanger = environment.getProperty(SuperclassChanger.INSTANCE.get()).orElse(null); - final ILaunchPluginService mixin = environment.findLaunchPlugin(MixinLaunchPluginLegacy.NAME).orElse(null); - - final List> launchResources = new ArrayList<>(); - - for (final Map.Entry> resourcesEntry : VanillaPlatformService.pluginPlatform.getResources().entrySet()) { - final Set resources = resourcesEntry.getValue(); - for (final PluginResource resource : resources) { - - // Handle Access Transformers - if ((accessWidener != null || mixin != null || superclassChanger != null) && resource instanceof JVMPluginResource) { - if (mixin != null) { - // Offer jar to the Mixin service - mixin.offerResource(((JVMPluginResource) resource).path(), ((JVMPluginResource) resource).path().getFileName().toString()); - } - - // Offer jar to the AW service - ((JVMPluginResource) resource).manifest().ifPresent(manifest -> { - if (accessWidener != null) { - final String atFiles = manifest.getMainAttributes().getValue(Constants.ManifestAttributes.ACCESS_WIDENER); - if (atFiles != null) { - for (final String atFile : atFiles.split(",")) { - if (!atFile.endsWith(AccessWidenerTransformationService.ACCESS_WIDENER_EXTENSION)) { - continue; - } - try { - accessWidener.offerResource( - ((JVMPluginResource) resource).fileSystem().getPath(atFile).toUri().toURL(), - atFile - ); - } catch (final MalformedURLException ex) { - VanillaPlatformService.pluginPlatform.logger().warn( - "Failed to read declared access widener {}, from {}:", - atFile, - resource.locator() - ); - } - } - } - } - if (mixin != null && manifest.getMainAttributes().getValue(org.spongepowered.asm.util.Constants.ManifestAttributes.MIXINCONFIGS) != null) { - if (!VanillaPlatformService.isSponge((JVMPluginResource) resource)) { - VanillaPlatformService.pluginPlatform.logger().warn( - "Plugin from {} uses Mixins to modify the Minecraft Server. If something breaks, remove it before reporting the " - + "problem to Sponge!", ((JVMPluginResource) resource).path() - ); - } - } - if (superclassChanger != null) { - final String superclassChangeFiles = manifest.getMainAttributes().getValue(Constants.ManifestAttributes.SUPERCLASS_CHANGE); - if (superclassChangeFiles != null) { - for (final String superclassChangeFile : superclassChangeFiles.split(",")) { - if (!superclassChangeFile.endsWith(SuperclassChanger.SUPER_CLASS_EXTENSION)) { - continue; - } - try { - superclassChanger.offerResource( - ((JVMPluginResource) resource).fileSystem().getPath(superclassChangeFile).toUri().toURL(), - superclassChangeFile - ); - } catch (final MalformedURLException ex) { - VanillaPlatformService.pluginPlatform.logger().warn( - "Failed to read declared superclass changer {}, from {}:", - superclassChangeFile, - resource.locator() - ); - } - } - } - } - }); - - final Map.Entry entry = Maps.immutableEntry(((JVMPluginResource) resource).path().getFileName().toString(), - ((JVMPluginResource) resource).path()); - launchResources.add(entry); - } - } - } - - return launchResources; - } - - private static boolean isSponge(final JVMPluginResource resource) { - try { - return resource.path().toUri().equals(VanillaPlatformService.class.getProtectionDomain().getCodeSource().getLocation().toURI()); - } catch (final URISyntaxException ex) { - return false; - } - } - - @Override - public void onLoad(final IEnvironment env, final Set otherServices) { - final VanillaPluginPlatform pluginPlatform = VanillaPlatformService.pluginPlatform; - pluginPlatform.logger().info("SpongePowered PLUGIN Subsystem Version={} Source={}", - pluginPlatform.version(), this.getCodeSource()); - - pluginPlatform.discoverLocatorServices(); - pluginPlatform.getLocatorServices().forEach((k, v) -> pluginPlatform.logger().info("Plugin resource locator '{}' found.", k)); - pluginPlatform.discoverLanguageServices(); - pluginPlatform.getLanguageServices().forEach((k, v) -> pluginPlatform.logger().info("Plugin language loader '{}' found.", k)); - } - - @Override - public @NonNull List transformers() { - return ImmutableList.of(); - } - - private String getCodeSource() { - try { - return this.getClass().getProtectionDomain().getCodeSource().getLocation().toString(); - } catch (final Throwable th) { - return "Unknown"; - } - } -} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaPluginPlatform.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaPluginPlatform.java index d35fa3778ab..1bd3bc80bc8 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaPluginPlatform.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaPluginPlatform.java @@ -24,22 +24,24 @@ */ package org.spongepowered.vanilla.applaunch.plugin; +import cpw.mods.modlauncher.Launcher; +import cpw.mods.modlauncher.api.IModuleLayerManager; import org.apache.logging.log4j.Logger; import org.spongepowered.common.applaunch.plugin.PluginPlatform; import org.spongepowered.plugin.PluginCandidate; import org.spongepowered.plugin.PluginLanguageService; import org.spongepowered.plugin.PluginResource; import org.spongepowered.plugin.PluginResourceLocatorService; -import org.spongepowered.plugin.blackboard.Key; +import org.spongepowered.plugin.blackboard.Blackboard; import org.spongepowered.plugin.blackboard.Keys; import org.spongepowered.plugin.builtin.StandardEnvironment; import org.spongepowered.plugin.builtin.jvm.JVMKeys; +import org.spongepowered.vanilla.applaunch.plugin.resource.SecureJarPluginResource; import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -49,14 +51,12 @@ public final class VanillaPluginPlatform implements PluginPlatform { - public static final Key> EXTRA_TRANSFORMABLE_PATHS = Key.of("spongevanilla:transformable_paths", List.class); - private final StandardEnvironment standardEnvironment; - private final Map> locatorServices; - private final Map> languageServices; + private final Map> locatorServices; + private final Map languageServices; - private final Map> locatorResources; - private final Map, List>> pluginCandidates; + private final Map> locatorResources; + private final Map> pluginCandidates; public VanillaPluginPlatform(final StandardEnvironment standardEnvironment) { this.standardEnvironment = standardEnvironment; @@ -73,7 +73,7 @@ public String version() { @Override public void setVersion(final String version) { - this.standardEnvironment.blackboard().getOrCreate(Keys.VERSION, () -> version); + this.standardEnvironment.blackboard().set(Keys.VERSION, version); } @Override @@ -88,7 +88,7 @@ public Path baseDirectory() { @Override public void setBaseDirectory(final Path baseDirectory) { - this.standardEnvironment.blackboard().getOrCreate(Keys.BASE_DIRECTORY, () -> baseDirectory); + this.standardEnvironment.blackboard().set(Keys.BASE_DIRECTORY, baseDirectory); } @Override @@ -98,51 +98,55 @@ public List pluginDirectories() { @Override public void setPluginDirectories(final List pluginDirectories) { - this.standardEnvironment.blackboard().getOrCreate(Keys.PLUGIN_DIRECTORIES, () -> pluginDirectories); + this.standardEnvironment.blackboard().set(Keys.PLUGIN_DIRECTORIES, pluginDirectories); } @Override public String metadataFilePath() { - return this.standardEnvironment.blackboard().get(JVMKeys.METADATA_FILE_PATH); + return this.standardEnvironment.blackboard().get(Keys.METADATA_FILE_PATH); } @Override public void setMetadataFilePath(final String metadataFilePath) { - this.standardEnvironment.blackboard().getOrCreate(JVMKeys.METADATA_FILE_PATH, () -> metadataFilePath); + this.standardEnvironment.blackboard().set(Keys.METADATA_FILE_PATH, metadataFilePath); } public StandardEnvironment getStandardEnvironment() { return this.standardEnvironment; } - public Map> getLocatorServices() { + public Map> getLocatorServices() { return Collections.unmodifiableMap(this.locatorServices); } - public Map> getLanguageServices() { + public Map getLanguageServices() { return Collections.unmodifiableMap(this.languageServices); } - public Map> getResources() { + public Map> getResources() { return Collections.unmodifiableMap(this.locatorResources); } - public Map, List>> getCandidates() { + public Map> getCandidates() { return Collections.unmodifiableMap(this.pluginCandidates); } - public void initialize() { - for (final Map.Entry> entry : this.languageServices.entrySet()) { + public void initializeLanguageServices() { + for (final Map.Entry entry : this.languageServices.entrySet()) { entry.getValue().initialize(this.standardEnvironment); } } public void discoverLocatorServices() { - final ServiceLoader> serviceLoader = (ServiceLoader>) (Object) ServiceLoader.load( - PluginResourceLocatorService.class, null); + final Blackboard blackboard = this.standardEnvironment.blackboard(); + blackboard.set(JVMKeys.ENVIRONMENT_LOCATOR_VARIABLE_NAME, "SPONGE_PLUGINS"); + blackboard.set(JVMKeys.JVM_PLUGIN_RESOURCE_FACTORY, SecureJarPluginResource::new); + + final ModuleLayer serviceLayer = Launcher.INSTANCE.environment().findModuleLayerManager().flatMap(lm -> lm.getLayer(IModuleLayerManager.Layer.SERVICE)).orElseThrow(); + final var serviceLoader = ServiceLoader.load(serviceLayer, PluginResourceLocatorService.class); - for (final Iterator> iter = serviceLoader.iterator(); iter.hasNext(); ) { - final PluginResourceLocatorService next; + for (final var iter = serviceLoader.iterator(); iter.hasNext(); ) { + final PluginResourceLocatorService next; try { next = iter.next(); @@ -156,11 +160,11 @@ public void discoverLocatorServices() { } public void discoverLanguageServices() { - final ServiceLoader> serviceLoader = (ServiceLoader>) (Object) ServiceLoader.load( - PluginLanguageService.class, VanillaPluginPlatform.class.getClassLoader()); + final ModuleLayer pluginLayer = Launcher.INSTANCE.environment().findModuleLayerManager().flatMap(lm -> lm.getLayer(IModuleLayerManager.Layer.PLUGIN)).orElseThrow(); + final var serviceLoader = ServiceLoader.load(pluginLayer, PluginLanguageService.class); - for (final Iterator> iter = serviceLoader.iterator(); iter.hasNext(); ) { - final PluginLanguageService next; + for (final var iter = serviceLoader.iterator(); iter.hasNext(); ) { + final PluginLanguageService next; try { next = iter.next(); @@ -174,9 +178,9 @@ public void discoverLanguageServices() { } public void locatePluginResources() { - for (final Map.Entry> locatorEntry : this.locatorServices.entrySet()) { - final PluginResourceLocatorService locatorService = locatorEntry.getValue(); - final Set resources = locatorService.locatePluginResources(this.standardEnvironment); + for (final Map.Entry> locatorEntry : this.locatorServices.entrySet()) { + final PluginResourceLocatorService locatorService = locatorEntry.getValue(); + final Set resources = locatorService.locatePluginResources(this.standardEnvironment); if (!resources.isEmpty()) { this.locatorResources.put(locatorEntry.getKey(), resources); } @@ -184,18 +188,23 @@ public void locatePluginResources() { } public void createPluginCandidates() { - for (final Map.Entry> languageEntry : this.languageServices.entrySet()) { - final PluginLanguageService languageService = languageEntry.getValue(); - for (final Map.Entry> resourcesEntry : this.locatorResources.entrySet()) { + for (final PluginLanguageService languageService : this.languageServices.values()) { + for (final Set resources : this.locatorResources.values()) { + for (final PluginResource pluginResource : resources) { + if (ResourceType.of(pluginResource) != ResourceType.PLUGIN) { + continue; + } - for (final PluginResource pluginResource : resourcesEntry.getValue()) { try { - final List> candidates = languageService.createPluginCandidates(this.standardEnvironment, - pluginResource); + final List candidates = languageService.createPluginCandidates(this.standardEnvironment, pluginResource); if (candidates.isEmpty()) { continue; } this.pluginCandidates.computeIfAbsent(languageService, k -> new LinkedList<>()).addAll(candidates); + + if (pluginResource instanceof SecureJarPluginResource jarResource) { + jarResource.addCandidates(candidates); + } } catch (final Exception ex) { this.standardEnvironment.logger().error("Failed to create plugin candidates", ex); } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaTransformationService.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaTransformationService.java new file mode 100644 index 00000000000..385ca924d71 --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaTransformationService.java @@ -0,0 +1,218 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.plugin; + +import com.google.common.collect.ImmutableList; +import cpw.mods.jarhandling.SecureJar; +import cpw.mods.modlauncher.Launcher; +import cpw.mods.modlauncher.api.IEnvironment; +import cpw.mods.modlauncher.api.IModuleLayerManager; +import cpw.mods.modlauncher.api.ITransformationService; +import cpw.mods.modlauncher.api.ITransformer; +import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.fusesource.jansi.AnsiConsole; +import org.spongepowered.asm.launch.MixinLaunchPlugin; +import org.spongepowered.common.applaunch.AppLaunch; +import org.spongepowered.common.applaunch.plugin.PluginPlatformConstants; +import org.spongepowered.plugin.PluginResource; +import org.spongepowered.plugin.builtin.StandardEnvironment; +import org.spongepowered.transformers.modlauncher.AccessWidenerTransformationService; +import org.spongepowered.transformers.modlauncher.SuperclassChanger; +import org.spongepowered.vanilla.applaunch.Constants; +import org.spongepowered.vanilla.applaunch.plugin.resource.SecureJarPluginResource; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public final class VanillaTransformationService implements ITransformationService { + private @MonotonicNonNull VanillaPluginPlatform pluginPlatform; + + @Override + public @NonNull String name() { + return "spongevanilla"; + } + + @Override + public void onLoad(final IEnvironment env, final Set otherServices) { + AnsiConsole.systemInstall(); + + this.pluginPlatform = new VanillaPluginPlatform(new StandardEnvironment()); + AppLaunch.setPluginPlatform(this.pluginPlatform); + + final String implementationVersion = StandardEnvironment.class.getPackage().getImplementationVersion(); + final Path baseDirectory = env.getProperty(IEnvironment.Keys.GAMEDIR.get()).orElse(Paths.get(".")); + + this.pluginPlatform.setVersion(implementationVersion == null ? "dev" : implementationVersion); + this.pluginPlatform.setBaseDirectory(baseDirectory); + + this.pluginPlatform.logger().info("SpongePowered PLUGIN Subsystem Version={} Source={}", this.pluginPlatform.version(), this.getCodeSource()); + + final Path modsDirectory = baseDirectory.resolve("mods"); + if (Files.notExists(modsDirectory)) { + try { + Files.createDirectories(modsDirectory); + } catch (IOException ignored) {} + } + + final Path pluginsDirectory = baseDirectory.resolve("plugins"); + final List pluginDirectories = new ArrayList<>(); + pluginDirectories.add(modsDirectory); + if (Files.exists(pluginsDirectory)) { + pluginDirectories.add(pluginsDirectory); + } + + this.pluginPlatform.setPluginDirectories(pluginDirectories); + this.pluginPlatform.setMetadataFilePath(PluginPlatformConstants.METADATA_FILE_LOCATION); + } + + @Override + public void initialize(final IEnvironment environment) { + this.pluginPlatform.initializeLanguageServices(); + } + + @Override + public List beginScanning(final IEnvironment environment) { + this.pluginPlatform.discoverLocatorServices(); + this.pluginPlatform.getLocatorServices().forEach((k, v) -> this.pluginPlatform.logger().info("Plugin resource locator '{}' found.", k)); + + this.pluginPlatform.locatePluginResources(); + + final List languageResources = new ArrayList<>(); + + for (final Set resources : this.pluginPlatform.getResources().values()) { + for (final PluginResource resource : resources) { + if (resource instanceof SecureJarPluginResource secureJarResource) { + if (ResourceType.of(resource) == ResourceType.LANGUAGE) { + languageResources.add(secureJarResource.jar()); + } + } + } + } + + return List.of(new Resource(IModuleLayerManager.Layer.PLUGIN, languageResources)); + } + + @Override + public List completeScan(IModuleLayerManager layerManager) { + this.pluginPlatform.discoverLanguageServices(); + this.pluginPlatform.getLanguageServices().forEach((k, v) -> this.pluginPlatform.logger().info("Plugin language loader '{}' found.", k)); + + this.pluginPlatform.createPluginCandidates(); + + IEnvironment env = Launcher.INSTANCE.environment(); + final AccessWidenerTransformationService accessWidener = env.getProperty(AccessWidenerTransformationService.INSTANCE.get()).orElse(null); + final SuperclassChanger superclassChanger = env.getProperty(SuperclassChanger.INSTANCE.get()).orElse(null); + final ILaunchPluginService mixin = env.findLaunchPlugin(MixinLaunchPlugin.NAME).orElse(null); + + final List gameResources = new ArrayList<>(); + + for (final Set resources : this.pluginPlatform.getResources().values()) { + for (final PluginResource resource : resources) { + if (resource instanceof SecureJarPluginResource secureJarResource) { + // Build jar metadata from first candidate, or fallback to standard + secureJarResource.init(); + + if (ResourceType.of(resource) == ResourceType.PLUGIN) { + gameResources.add(secureJarResource.jar()); + } + } + + // Offer jar to the Mixin service + if (mixin != null) { + mixin.offerResource(resource.path(), resource.path().getFileName().toString()); + } + + // Offer jar to the AW service + if (accessWidener != null) { + final Optional awFiles = resource.property(Constants.ManifestAttributes.ACCESS_WIDENER); + if (awFiles.isPresent()) { + for (final String awFile : awFiles.get().split(",")) { + if (!awFile.endsWith(AccessWidenerTransformationService.ACCESS_WIDENER_EXTENSION)) { + continue; + } + try { + accessWidener.offerResource(resource.locateResource(awFile).get().toURL(), awFile); + } catch (final Exception ex) { + this.pluginPlatform.logger().warn("Failed to read declared access widener {}, from {}:", awFile, resource.locator()); + } + } + } + } + + // Offer jar to the SuperclassChanger service + if (superclassChanger != null) { + final Optional superclassChangeFiles = resource.property(Constants.ManifestAttributes.SUPERCLASS_CHANGE); + if (superclassChangeFiles.isPresent()) { + for (final String superclassChangeFile : superclassChangeFiles.get().split(",")) { + if (!superclassChangeFile.endsWith(SuperclassChanger.SUPER_CLASS_EXTENSION)) { + continue; + } + try { + superclassChanger.offerResource(resource.locateResource(superclassChangeFile).get().toURL(), superclassChangeFile); + } catch (final Exception ex) { + this.pluginPlatform.logger().warn("Failed to read declared superclass changer {}, from {}:", superclassChangeFile, resource.locator()); + } + } + } + } + + // Log warning about plugin using Mixin + if (mixin != null && resource.property(org.spongepowered.asm.util.Constants.ManifestAttributes.MIXINCONFIGS).isPresent()) { + if (!VanillaTransformationService.isSponge(resource)) { + this.pluginPlatform.logger().warn("Plugin from {} uses Mixins to modify the Minecraft Server. If something breaks, remove it before reporting the problem to Sponge!", resource.path()); + } + } + } + } + + return List.of(new Resource(IModuleLayerManager.Layer.GAME, gameResources)); + } + + private static boolean isSponge(final PluginResource resource) { + return resource instanceof SecureJarPluginResource secureJarResource && secureJarResource.jar().name().equals("spongevanilla"); + } + + @Override + @SuppressWarnings("rawtypes") + public @NonNull List transformers() { + return ImmutableList.of(); + } + + private String getCodeSource() { + try { + return this.getClass().getProtectionDomain().getCodeSource().getLocation().toString(); + } catch (final Throwable th) { + return "Unknown"; + } + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/locator/GameResourceLocatorService.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/locator/GameResourceLocatorService.java new file mode 100644 index 00000000000..0a1791fb459 --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/locator/GameResourceLocatorService.java @@ -0,0 +1,90 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.plugin.locator; + +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResourceLocatorService; + +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +public final class GameResourceLocatorService implements JVMPluginResourceLocatorService { + + @Override + public String name() { + return "game"; + } + + @Override + public Set locatePluginResources(final Environment environment) { + environment.logger().info("Locating '{}' resources...", this.name()); + + final Set resources = new HashSet<>(); + final String resourcesProp = System.getProperty("sponge.gameResources"); + if (resourcesProp != null) { + for (final String entry : resourcesProp.split(";")) { + if (entry.isBlank()) { + continue; + } + + final Path[] paths = Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new); + resources.add(JVMPluginResource.create(environment, this.name(), paths)); + } + } + + final String fsProp = System.getProperty("sponge.rootJarFS"); + if (fsProp != null) { + try { + final FileSystem fs = FileSystems.getFileSystem(new URI(fsProp)); + final Path spongeMod = newJarInJar(fs.getPath("jars", "spongevanilla-mod.jar")); + resources.add(JVMPluginResource.create(environment, this.name(), spongeMod)); + } catch (final Exception e) { + environment.logger().error("Failed to locate spongevanilla-mod jar."); + } + } + + environment.logger().info("Located [{}] resource(s) for '{}'...", resources.size(), this.name()); + + return resources; + } + + private static Path newJarInJar(final Path jar) { + try { + URI jij = new URI("jij:" + jar.toAbsolutePath().toUri().getRawSchemeSpecificPart()).normalize(); + final Map env = Map.of("packagePath", jar); + FileSystem jijFS = FileSystems.newFileSystem(jij, env); + return jijFS.getPath("/"); // root of the archive to load + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/PluginJarMetadata.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/PluginJarMetadata.java new file mode 100644 index 00000000000..31a5850c88b --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/PluginJarMetadata.java @@ -0,0 +1,102 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.plugin.resource; + +import cpw.mods.jarhandling.JarMetadata; +import cpw.mods.jarhandling.SecureJar; +import org.spongepowered.plugin.metadata.PluginMetadata; + +import java.lang.module.ModuleDescriptor; +import java.nio.file.Path; + +public class PluginJarMetadata implements JarMetadata { + private final SecureJar jar; + private final Path[] paths; + + private boolean initialized = false; + private PluginMetadata plugin; + private JarMetadata fallback; + + private String name; + private ModuleDescriptor descriptor; + + public PluginJarMetadata(final SecureJar jar, final Path[] paths) { + this.jar = jar; + this.paths = paths; + } + + public void init(final PluginMetadata plugin) { + if (this.initialized) { + throw new IllegalStateException("Metadata already initialized"); + } + this.initialized = true; + if (plugin == null) { + this.fallback = JarMetadata.from(this.jar, this.paths); + this.name = this.fallback.name(); + } else { + this.plugin = plugin; + this.name = this.plugin.id().replace('-', '_'); + } + } + + private void checkInitialized() { + if (!this.initialized) { + throw new IllegalStateException("Metadata not initialized"); + } + } + + @Override + public String name() { + checkInitialized(); + return this.name; + } + + @Override + public String version() { + checkInitialized(); + if (this.plugin == null) { + return this.fallback.version(); + } + return this.plugin.version().toString(); + } + + @Override + public ModuleDescriptor descriptor() { + checkInitialized(); + if (this.plugin == null) { + return this.fallback.descriptor(); + } + if (this.descriptor == null) { + ModuleDescriptor.Builder builder = ModuleDescriptor.newAutomaticModule(name()) + .version(version()) + .packages(this.jar.getPackages()); + this.jar.getProviders().stream() + .filter(p -> !p.providers().isEmpty()) + .forEach(p -> builder.provides(p.serviceName(), p.providers())); + this.descriptor = builder.build(); + } + return this.descriptor; + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/SecureJarPluginResource.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/SecureJarPluginResource.java new file mode 100644 index 00000000000..62f42cc0dff --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/SecureJarPluginResource.java @@ -0,0 +1,125 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.plugin.resource; + +import cpw.mods.jarhandling.JarMetadata; +import cpw.mods.jarhandling.SecureJar; +import org.spongepowered.plugin.PluginCandidate; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import org.spongepowered.vanilla.applaunch.plugin.ResourceType; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.jar.Manifest; + +public final class SecureJarPluginResource implements JVMPluginResource { + private final String locator; + private final SecureJar jar; + + private PluginJarMetadata pluginJarMetadata; + private List candidates; + + public SecureJarPluginResource(final String locator, final Path[] paths) { + Objects.requireNonNull(locator, "locator"); + Objects.requireNonNull(paths, "paths"); + + if (paths.length == 0) { + throw new IllegalArgumentException("Need at least one path"); + } + + this.locator = locator; + this.jar = SecureJar.from(jar -> { + if (ResourceType.of(jar) == ResourceType.PLUGIN) { + this.pluginJarMetadata = new PluginJarMetadata(jar, paths); + this.candidates = new ArrayList<>(); + return this.pluginJarMetadata; + } + return JarMetadata.from(jar, paths); + }, paths); + } + + @Override + public String locator() { + return this.locator; + } + + public SecureJar jar() { + return this.jar; + } + + @Override + public Path path() { + return this.jar.getPrimaryPath(); + } + + @Override + public Manifest manifest() { + return this.jar.moduleDataProvider().getManifest(); + } + + @Override + public Path resourcesRoot() { + return this.jar.getRootPath(); + } + + public void addCandidates(Collection candidates) { + if (this.candidates != null) { + this.candidates.addAll(candidates); + } + } + + public void init() { + if (this.pluginJarMetadata != null) { + this.pluginJarMetadata.init(this.candidates.isEmpty() ? null : this.candidates.get(0).metadata()); + } + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof SecureJarPluginResource that)) { + return false; + } + return this.locator.equals(that.locator) && this.jar.equals(that.jar); + } + + @Override + public int hashCode() { + return Objects.hash(this.locator, this.jar); + } + + @Override + public String toString() { + return "SecureJarPluginResource[" + + "locator=" + this.locator + ", " + + "jar=" + this.jar + ']'; + } + +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/util/MixinLoggerInjector.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/util/MixinLoggerInjector.java index 9ce261dbf17..fcaa7c28c71 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/util/MixinLoggerInjector.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/util/MixinLoggerInjector.java @@ -30,7 +30,6 @@ import org.apache.logging.log4j.core.filter.CompositeFilter; import org.apache.logging.log4j.core.filter.DenyAllFilter; import org.apache.logging.log4j.core.filter.RegexFilter; -import org.intellij.lang.annotations.Language; import org.spongepowered.asm.mixin.MixinEnvironment; import java.util.Queue; @@ -45,7 +44,7 @@ public final class MixinLoggerInjector { private MixinLoggerInjector() { } - private static RegexFilter pattern(@Language("RegExp") final String pattern) { + private static RegexFilter pattern(final String pattern) { try { return RegexFilter.createFilter(pattern, new String[0], false, Filter.Result.ACCEPT, Filter.Result.NEUTRAL); } catch (final IllegalAccessException ex) { diff --git a/vanilla/src/applaunch/resource-templates/META-INF/minecraft_sponge_plugins.json b/vanilla/src/applaunch/resource-templates/META-INF/minecraft_sponge_plugins.json new file mode 100644 index 00000000000..a4d61effc80 --- /dev/null +++ b/vanilla/src/applaunch/resource-templates/META-INF/minecraft_sponge_plugins.json @@ -0,0 +1,24 @@ +{ + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "Mojang Studios, All Rights Reserved", + "plugins": [ + { + "id": "minecraft", + "name": "Minecraft", + "version": "{{ minecraftVersion }}", + "entrypoint": "org.spongepowered.common.launcher.Launcher", + "links": { + "homepage": "https://www.minecraft.net" + }, + "contributors": [ + { + "name": "Mojang Studios", + "description": "Lead Developer" + } + ] + } + ] +} \ No newline at end of file diff --git a/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService b/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService index 5760567b8c2..fcea6e880a6 100644 --- a/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService +++ b/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService @@ -1 +1 @@ -org.spongepowered.vanilla.applaunch.plugin.VanillaPlatformService \ No newline at end of file +org.spongepowered.vanilla.applaunch.plugin.VanillaTransformationService \ No newline at end of file diff --git a/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService b/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService index e69de29bb2d..819258d57e1 100644 --- a/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService +++ b/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService @@ -0,0 +1 @@ +org.spongepowered.vanilla.applaunch.plugin.ListenerPluginService \ No newline at end of file diff --git a/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginLanguageService b/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginLanguageService index e12ee48b985..3e6983f38f8 100644 --- a/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginLanguageService +++ b/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginLanguageService @@ -1 +1 @@ -org.spongepowered.vanilla.launch.plugin.JavaPluginLanguageService \ No newline at end of file +org.spongepowered.vanilla.applaunch.plugin.JavaPluginLanguageService \ No newline at end of file diff --git a/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginResourceLocatorService b/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginResourceLocatorService index 93928337b63..330fcb92be8 100644 --- a/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginResourceLocatorService +++ b/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginResourceLocatorService @@ -1,2 +1 @@ -org.spongepowered.plugin.builtin.jvm.locator.DirectoryPluginResourceLocatorService -org.spongepowered.plugin.builtin.jvm.locator.ClasspathPluginResourceLocatorService \ No newline at end of file +org.spongepowered.vanilla.applaunch.plugin.locator.GameResourceLocatorService \ No newline at end of file diff --git a/vanilla/src/installer/java-templates/org/spongepowered/vanilla/installer/Constants.java b/vanilla/src/installer/java-templates/org/spongepowered/vanilla/installer/Constants.java index 2da830df3cd..c7a1027467f 100644 --- a/vanilla/src/installer/java-templates/org/spongepowered/vanilla/installer/Constants.java +++ b/vanilla/src/installer/java-templates/org/spongepowered/vanilla/installer/Constants.java @@ -24,12 +24,8 @@ */ package org.spongepowered.vanilla.installer; -import org.objectweb.asm.Opcodes; - public final class Constants { - public static final int ASM_VERSION = Opcodes.ASM9; - public static final class Libraries { public static final String MINECRAFT_VERSION_TARGET = "{{ minecraftVersion }}"; @@ -44,9 +40,6 @@ public static final class Libraries { } public static final class ManifestAttributes { - - public static final String ACCESS_WIDENER = "Access-Widener"; public static final String LAUNCH_TARGET = "Launch-Target"; - public static final String SUPERCLASS_CHANGE = "Superclass-Transformer"; } } diff --git a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/Agent.java b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/Agent.java index 7fe229109e1..9e180133bf2 100644 --- a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/Agent.java +++ b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/Agent.java @@ -24,152 +24,14 @@ */ package org.spongepowered.vanilla.installer; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.mlpatcher.AsmFixerAgent; -import org.tinylog.Logger; - -import java.io.File; -import java.io.IOException; import java.lang.instrument.Instrumentation; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Map; -import java.util.Set; -import java.util.jar.JarFile; -import java.util.jar.Manifest; /** - * Agent, used to add downloaded jars to the system classpath and open modules - * for deep reflection. - * - *

This needs to be compiled for exactly java 9, since it runs before we have - * an opportunity to provide a friendly warning message.

+ * We used to have an agent but we no longer need it. + * Some versions of SpongeGradle attempt to run this agent, so this class exists otherwise it fails. */ public class Agent { - - private static Instrumentation instrumentation; - private static boolean usingFallback; - public static void premain(final String agentArgs, final Instrumentation instrumentation) { - Agent.instrumentation = instrumentation; - AsmFixerAgent.premain("", instrumentation); + // ignored } - - public static void agentmain(final String agentArgs, final Instrumentation instrumentation) { - Agent.instrumentation = instrumentation; - AsmFixerAgent.agentmain("", instrumentation); - } - - static void addJarToClasspath(final Path jar) { - if (Agent.instrumentation == null) { - throw new IllegalStateException("The SpongeVanilla jar must be run as a java agent in order to add downloaded libraries to the classpath!"); - } - try { - final Path normalized = Paths.get(jar.toRealPath().toUri().toURL().toURI()); - - if (Agent.usingFallback) { - Fallback.addToSystemClasspath(jar); - return; - } - - try { - // x.X The URL escaping done by appendToSystemClassLoaderSearch differs from the - try (final JarFile jf = new JarFile(new File(normalized.toUri()))) { - Agent.instrumentation.appendToSystemClassLoaderSearch(jf); - } - } catch (final IllegalArgumentException ex) { - // For some reason, the Agent method on Windows can't handle some non-ASCII characters - // This is fairly awful, but it makes things work (and hopefully won't be reached often) - Logger.debug(ex, "Failed to add library {} to classpath, transitioning to fallback (more unsafe!) method", jar); - Agent.usingFallback = true; - Fallback.addToSystemClasspath(jar); - } - } catch (final IOException | URISyntaxException ex) { - Logger.error(ex, "Failed to create jar file for archive '{}'!", jar); - } - } - - static void crackModules() { - final Set systemUnnamed = Set.of(ClassLoader.getSystemClassLoader().getUnnamedModule()); - Agent.instrumentation.redefineModule( - Manifest.class.getModule(), - Set.of(), - Map.of("sun.security.util", systemUnnamed), // ModLauncher - Map.of( - // ModLauncher -- needs Manifest.jv, and various JarVerifier methods - "java.util.jar", systemUnnamed - ), - Set.of(), - Map.of() - ); - } - - static final class Fallback { - - private static final Object SYSTEM_CLASS_PATH; /* a URLClassPath */ - private static final Method ADD_URL; /* URLClassPath.addURL(java.net.URL) */ - - static { - Logger.debug("Initializing fallback classpath modification. This is only expected when using non-ASCII characters in file paths on Windows"); - // Crack the java.base module to allow us to use reflection - final Set systemUnnamed = Set.of(ClassLoader.getSystemClassLoader().getUnnamedModule()); - Agent.instrumentation.redefineModule( - ClassLoader.class.getModule(), /* java.base */ - Set.of(), - Map.of("jdk.internal.loader", systemUnnamed), - Map.of("jdk.internal.loader", systemUnnamed), - Set.of(), - Map.of() - ); - - final ClassLoader loader = ClassLoader.getSystemClassLoader(); - - Field ucp = Fallback.fieldOrNull(loader.getClass(), "ucp"); - if (ucp == null) { - ucp = Fallback.fieldOrNull(loader.getClass().getSuperclass(), "ucp"); - } - - if (ucp == null) { - // Did they change something? - throw new ExceptionInInitializerError("Unable to initialize fallback classpath handling on your system. Perhaps try a different Java version?"); - } - - try { - SYSTEM_CLASS_PATH = ucp.get(loader); - ADD_URL = Fallback.SYSTEM_CLASS_PATH.getClass().getDeclaredMethod("addURL", URL.class); - } catch (final NoSuchMethodException | IllegalAccessException ex) { - throw new ExceptionInInitializerError(ex); - } - } - - private static @Nullable Field fieldOrNull(final @Nullable Class clazz, final String name) { - if (clazz == null) { - return null; - } - - try { - final Field f = clazz.getDeclaredField(name); - f.setAccessible(true); - return f; - } catch (final NoSuchFieldException ex) { - return null; - } - } - - static void addToSystemClasspath(final Path file) { - try { - Fallback.ADD_URL.invoke(Fallback.SYSTEM_CLASS_PATH, file.toUri().toURL()); - } catch (final IllegalAccessException | InvocationTargetException | IOException ex) { - Logger.error(ex, "Failed to add file {} to the system classpath", file); - throw new RuntimeException(ex); - } - } - - } - } diff --git a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java index 3e6f96caa00..992785c98d9 100644 --- a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java +++ b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java @@ -43,9 +43,14 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URI; import java.net.URL; +import java.net.URLClassLoader; import java.net.URLConnection; import java.nio.file.AccessDeniedException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @@ -63,12 +68,13 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.jar.JarFile; +import java.util.jar.Manifest; import java.util.zip.ZipEntry; public final class InstallerMain { - private static final String COLLECTION_BOOTSTRAP = "bootstrap"; // goes on app - private static final String COLLECTION_MAIN = "main"; // goes on TCL + private static final String COLLECTION_BOOTSTRAP = "bootstrap"; // boot layer + private static final String COLLECTION_MAIN = "main"; // game layer private static final int MAX_TRIES = 2; private final Installer installer; @@ -126,41 +132,72 @@ public void downloadAndRun() throws Exception { } assert remappedMinecraftJar != null; // always assigned or thrown - // MC itself and mojang dependencies are on the main layer, other libs are only on the bootstrap layer + // Minecraft itself is on the main layer libraryManager.addLibrary(InstallerMain.COLLECTION_MAIN, new LibraryManager.Library("minecraft", remappedMinecraftJar.server())); - for (final Map.Entry library : remappedMinecraftJar.libraries().entrySet()) { - if (library.getKey().group().equals("com.mojang") || library.getKey().group().equals("net.minecraft")) { - libraryManager.addLibrary(InstallerMain.COLLECTION_MAIN, new LibraryManager.Library(library.getKey().toString(), library.getValue())); - } else { - libraryManager.addLibrary(InstallerMain.COLLECTION_BOOTSTRAP, new LibraryManager.Library(library.getKey().toString(), library.getValue())); - } + + // Other libs are on the bootstrap layer + for (final Map.Entry entry : remappedMinecraftJar.libraries().entrySet()) { + final GroupArtifactVersion artifact = entry.getKey(); + final Path path = entry.getValue(); + + libraryManager.addLibrary(InstallerMain.COLLECTION_BOOTSTRAP, new LibraryManager.Library(artifact.toString(), path)); } + this.installer.getLibraryManager().finishedProcessing(); Logger.info("Environment has been verified."); final Set seenLibs = new HashSet<>(); - this.installer.getLibraryManager().getAll(InstallerMain.COLLECTION_BOOTSTRAP).stream() - .peek(lib -> seenLibs.add(lib.getName())) - .map(LibraryManager.Library::getFile) - .forEach(path -> { - Logger.debug("Adding jar {} to bootstrap classpath", path); - Agent.addJarToClasspath(path); - }); - - final Path[] transformableLibs = this.installer.getLibraryManager().getAll(InstallerMain.COLLECTION_MAIN).stream() - .filter(lib -> !seenLibs.contains(lib.getName())) - .map(LibraryManager.Library::getFile) + final Path[] bootLibs = this.installer.getLibraryManager().getAll(InstallerMain.COLLECTION_BOOTSTRAP).stream() + .peek(lib -> seenLibs.add(lib.name())) + .map(LibraryManager.Library::file) .toArray(Path[]::new); + final Path[] gameLibs = this.installer.getLibraryManager().getAll(InstallerMain.COLLECTION_MAIN).stream() + .filter(lib -> !seenLibs.contains(lib.name())) + .map(LibraryManager.Library::file) + .toArray(Path[]::new); + + final URL rootJar = InstallerMain.class.getProtectionDomain().getCodeSource().getLocation(); + final URI fsURI = new URI("jar", rootJar.toString(), null); + System.setProperty("sponge.rootJarFS", fsURI.toString()); + + final FileSystem fs = FileSystems.newFileSystem(fsURI, Map.of()); + final Path spongeBoot = newJarInJar(fs.getPath("jars", "spongevanilla-boot.jar")); + + String launchTarget = LauncherCommandLine.launchTarget; + if (launchTarget == null) { + final Path manifestFile = fs.getPath("META-INF", "MANIFEST.MF"); + try (final InputStream stream = Files.newInputStream(manifestFile)) { + final Manifest manifest = new Manifest(stream); + launchTarget = manifest.getMainAttributes().getValue(Constants.ManifestAttributes.LAUNCH_TARGET); + } + } + + final StringBuilder gameLibsEnv = new StringBuilder(); + for (final Path lib : gameLibs) { + gameLibsEnv.append(lib.toAbsolutePath()).append(';'); + } + gameLibsEnv.setLength(gameLibsEnv.length() - 1); + System.setProperty("sponge.gameResources", gameLibsEnv.toString()); + final List gameArgs = new ArrayList<>(LauncherCommandLine.remainingArgs); + gameArgs.add("--launchTarget"); + gameArgs.add(launchTarget); Collections.addAll(gameArgs, this.installer.getLauncherConfig().args.split(" ")); - // Suppress illegal reflection warnings on newer java - Agent.crackModules(); + InstallerMain.bootstrap(bootLibs, spongeBoot, gameArgs.toArray(new String[0])); + } - final String className = "org.spongepowered.vanilla.applaunch.Main"; - InstallerMain.invokeMain(className, gameArgs.toArray(new String[0]), transformableLibs); + private static Path newJarInJar(final Path jar) { + try { + URI jij = new URI("jij:" + jar.toAbsolutePath().toUri().getRawSchemeSpecificPart()).normalize(); + final Map env = Map.of("packagePath", jar); + FileSystem jijFS = FileSystems.newFileSystem(jij, env); + return jijFS.getPath("/"); // root of the archive to load + } catch (Exception e) { + throw new RuntimeException(e); + } } private ServerAndLibraries recoverFromMinecraftDownloadError(final T ex) throws T { @@ -175,17 +212,33 @@ private ServerAndLibraries recoverFromMinecraftDownloadErr } } - private static void invokeMain(final String className, final String[] args, final Path[] extraCpEntries) { + private static void bootstrap(final Path[] bootLibs, final Path spongeBoot, final String[] args) throws Exception { + final URL[] urls = new URL[bootLibs.length]; + for (int i = 0; i < bootLibs.length; i++) { + urls[i] = bootLibs[i].toAbsolutePath().toUri().toURL(); + } + + final List classpath = new ArrayList<>(); + for (final Path lib : bootLibs) { + classpath.add(new Path[] { lib }); + } + classpath.add(new Path[] { spongeBoot }); + + URLClassLoader loader = new URLClassLoader(urls, ClassLoader.getPlatformClassLoader()); + ClassLoader previousLoader = Thread.currentThread().getContextClassLoader(); try { - Class.forName(className) - .getMethod("main", String[].class, Path[].class) - .invoke(null, args, extraCpEntries); - } catch (final InvocationTargetException ex) { - Logger.error(ex.getCause(), "Failed to invoke main class {} due to an error", className); - System.exit(1); - } catch (final ClassNotFoundException | NoSuchMethodException | IllegalAccessException ex) { - Logger.error(ex, "Failed to invoke main class {} due to an error", className); + Thread.currentThread().setContextClassLoader(loader); + final Class cl = Class.forName("net.minecraftforge.bootstrap.Bootstrap", false, loader); + final Object instance = cl.getDeclaredConstructor().newInstance(); + final Method m = cl.getDeclaredMethod("bootstrapMain", String[].class, List.class); + m.setAccessible(true); + m.invoke(instance, args, classpath); + } catch (final Exception ex) { + final Throwable cause = ex instanceof InvocationTargetException ? ex.getCause() : ex; + Logger.error(cause, "Failed to invoke bootstrap main due to an error"); System.exit(1); + } finally { + Thread.currentThread().setContextClassLoader(previousLoader); } } @@ -391,47 +444,46 @@ private ServerAndLibraries remapMinecraft(final ServerAndLibraries minecraft, fi final Renamer.Builder renamerBuilder = Renamer.builder() .add(Transformer.parameterAnnotationFixerFactory()) .add(ctx -> { - final Transformer backing = Transformer.renamerFactory(mappings, false).create(ctx); - return new Transformer() { - - @Override - public ClassEntry process(final ClassEntry entry) { - final String name = entry.getName(); - if (name.startsWith("it/unimi") - || name.startsWith("com/google") - || name.startsWith("com/mojang/datafixers") - || name.startsWith("com/mojang/brigadier") - || name.startsWith("org/apache")) { - return entry; + final Transformer backing = Transformer.renamerFactory(mappings, false).create(ctx); + return new Transformer() { + @Override + public ClassEntry process(final ClassEntry entry) { + final String name = entry.getName(); + if (name.startsWith("it/unimi") + || name.startsWith("com/google") + || name.startsWith("com/mojang/datafixers") + || name.startsWith("com/mojang/brigadier") + || name.startsWith("org/apache")) { + return entry; + } + return backing.process(entry); } - return backing.process(entry); - } - - @Override - public ManifestEntry process(final ManifestEntry entry) { - return backing.process(entry); - } - @Override - public ResourceEntry process(final ResourceEntry entry) { - return backing.process(entry); - } + @Override + public ManifestEntry process(final ManifestEntry entry) { + return backing.process(entry); + } - @Override - public Collection getExtras() { - return backing.getExtras(); - } + @Override + public ResourceEntry process(final ResourceEntry entry) { + return backing.process(entry); + } - }; + @Override + public Collection getExtras() { + return backing.getExtras(); + } + }; }) .add(Transformer.recordFixerFactory()) .add(Transformer.parameterAnnotationFixerFactory()) .add(Transformer.sourceFixerFactory(SourceFixerConfig.JAVA)) .add(Transformer.signatureStripperFactory(SignatureStripperConfig.ALL)) .logger(Logger::debug); // quiet - try (final Renamer ren = renamerBuilder.build()) { - ren.run(minecraft.server.toFile(), tempOutput.toFile()); - } + + try (final Renamer ren = renamerBuilder.build()) { + ren.run(minecraft.server.toFile(), tempOutput.toFile()); + } // Restore file try { diff --git a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/LauncherCommandLine.java b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/LauncherCommandLine.java index f52c34a3169..89cc81e3075 100644 --- a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/LauncherCommandLine.java +++ b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/LauncherCommandLine.java @@ -39,12 +39,16 @@ public final class LauncherCommandLine { private static final OptionParser PARSER = new OptionParser(); - private static final ArgumentAcceptingOptionSpec INSTALLER_DIRECTORY_ARG = LauncherCommandLine.PARSER.accepts("installerDir", - "Alternative installer directory").withRequiredArg().withValuesConvertedBy(new PathConverter(PathProperties.DIRECTORY_EXISTING)) + private static final ArgumentAcceptingOptionSpec INSTALLER_DIRECTORY_ARG = LauncherCommandLine.PARSER + .accepts("installerDir", "Alternative installer directory").withRequiredArg() + .withValuesConvertedBy(new PathConverter(PathProperties.DIRECTORY_EXISTING)) .defaultsTo(Paths.get(".")); - private static final ArgumentAcceptingOptionSpec LIBRARIES_DIRECTORY_ARG = LauncherCommandLine.PARSER.accepts("librariesDir", - "Alternative libraries directory").withRequiredArg().withValuesConvertedBy(new PathConverter(PathProperties.DIRECTORY_EXISTING)) + private static final ArgumentAcceptingOptionSpec LIBRARIES_DIRECTORY_ARG = LauncherCommandLine.PARSER + .accepts("librariesDir", "Alternative libraries directory").withRequiredArg() + .withValuesConvertedBy(new PathConverter(PathProperties.DIRECTORY_EXISTING)) .defaultsTo(Paths.get("libraries")); + private static final ArgumentAcceptingOptionSpec LAUNCH_TARGET_ARG = LauncherCommandLine.PARSER + .accepts("launchTarget", "Launch target").withRequiredArg(); private static final NonOptionArgumentSpec REMAINDER = LauncherCommandLine.PARSER.nonOptions().ofType(String.class); static { @@ -52,6 +56,7 @@ public final class LauncherCommandLine { } public static Path installerDirectory, librariesDirectory; + public static String launchTarget; public static List remainingArgs; private LauncherCommandLine() { @@ -61,6 +66,7 @@ public static void configure(final String[] args) { final OptionSet options = LauncherCommandLine.PARSER.parse(args); LauncherCommandLine.installerDirectory = options.valueOf(LauncherCommandLine.INSTALLER_DIRECTORY_ARG); LauncherCommandLine.librariesDirectory = options.valueOf(LauncherCommandLine.LIBRARIES_DIRECTORY_ARG); + LauncherCommandLine.launchTarget = options.valueOf(LauncherCommandLine.LAUNCH_TARGET_ARG); LauncherCommandLine.remainingArgs = UnmodifiableCollections.copyOf(options.valuesOf(LauncherCommandLine.REMAINDER)); } } diff --git a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/LibraryManager.java b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/LibraryManager.java index c7147a310fd..8c939f4c4e9 100644 --- a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/LibraryManager.java +++ b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/LibraryManager.java @@ -87,7 +87,7 @@ public Set getAll(final String collection) { return Collections.unmodifiableSet(this.libraries.getOrDefault(collection, Collections.emptySet())); } - protected void addLibrary(final String set, final Library library) { + void addLibrary(final String set, final Library library) { this.libraries.computeIfAbsent(set, $ -> Collections.synchronizedSet(new LinkedHashSet<>())).add(library); } @@ -244,24 +244,7 @@ public void finishedProcessing() { } } - public static class Library { - - private final String name; - private final Path file; - - public Library(final String name, final Path file) { - this.name = name; - this.file = file; - } - - public String getName() { - return this.name; - } - - public Path getFile() { - return this.file; - } - } + public record Library(String name, Path file) {} private static String asId(final Dependency dep) { return dep.group + ':' + dep.module + ':' + dep.version; diff --git a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/VanillaLaunch.java b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/VanillaLaunch.java index 7203b0a7328..174351373f5 100644 --- a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/VanillaLaunch.java +++ b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/VanillaLaunch.java @@ -34,28 +34,18 @@ import org.spongepowered.common.launch.Launch; import org.spongepowered.common.launch.mapping.SpongeMappingManager; import org.spongepowered.plugin.PluginContainer; -import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.MetadataContainer; -import org.spongepowered.plugin.metadata.builtin.MetadataParser; import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; import org.spongepowered.vanilla.launch.inject.SpongeVanillaModule; import org.spongepowered.vanilla.launch.mapping.VanillaMappingManager; import org.spongepowered.vanilla.launch.plugin.VanillaDummyPluginContainer; import org.spongepowered.vanilla.launch.plugin.VanillaPluginManager; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Enumeration; +import java.util.Collection; import java.util.List; +import java.util.Set; public abstract class VanillaLaunch extends Launch { + private static final Set PLATFORM_IDS = Set.of("spongevanilla", "sponge", "spongeapi", "minecraft"); private final Stage injectionStage; private final VanillaPluginManager pluginManager; @@ -121,58 +111,9 @@ protected final void launchPlatform(final String[] args) { protected abstract void performBootstrap(final String[] args); protected final void createPlatformPlugins() { - final String metadataFileLocation = this.pluginPlatform.metadataFilePath(); - try { - // This is a bit nasty, but allows Sponge to detect builtin platform plugins when it's not the first entry on the classpath. - final URL classUrl = VanillaLaunch.class.getResource("/" + VanillaLaunch.class.getName().replace('.', '/') + ".class"); - - MetadataContainer read = null; - - // In production, let's try to ensure we can find our descriptor even if we're not first on the classpath - if (classUrl.getProtocol().equals("jar")) { - // Extract the path of the underlying jar file, and parse it as a path to normalize it - final String[] classUrlSplit = classUrl.getPath().split("!"); - final Path expectedFile = Paths.get(new URL(classUrlSplit[0]).toURI()); - - // Then go through every possible resource - final Enumeration manifests = - VanillaLaunch.class.getClassLoader().getResources("/" + metadataFileLocation); - while (manifests.hasMoreElements()) { - final URL next = manifests.nextElement(); - if (!next.getProtocol().equals("jar")) { - continue; - } - - // And stop when the normalized jar in that resource matches the URL of the jar that loaded VanillaLaunch? - final String[] pathSplit = next.getPath().split("!"); - if (pathSplit.length == 2) { - final Path vanillaPath = Paths.get(new URL(pathSplit[0]).toURI()); - if (vanillaPath.equals(expectedFile)) { - try (final Reader reader = new InputStreamReader(next.openStream(), StandardCharsets.UTF_8)) { - read = MetadataParser.read(reader); - } - break; - } - } - } - } - - if (read == null) { // other measures failed, fall back to directly querying the classpath - final Path vanillaPath = - Paths.get(VanillaLaunch.class.getResource("/" + metadataFileLocation).toURI()); - try (final Reader reader = Files.newBufferedReader(vanillaPath, StandardCharsets.UTF_8)) { - read = MetadataParser.read(reader); - } - } - if (read == null) { - throw new RuntimeException("Could not determine location for implementation metadata!"); - } - - for (final PluginMetadata metadata : read.metadata()) { - this.pluginManager().addPlugin(new VanillaDummyPluginContainer(metadata, this.logger(), this)); - } - } catch (final IOException | URISyntaxException e) { - throw new RuntimeException("Could not load metadata information for the implementation! This should be impossible!"); - } + this.pluginPlatform().getCandidates().values().stream().flatMap(Collection::stream) + .filter(plugin -> PLATFORM_IDS.contains(plugin.metadata().id())) + .map(plugin -> new VanillaDummyPluginContainer(plugin, this.logger(), this)) + .forEach(this.pluginManager()::addPlugin); } } diff --git a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/event/VanillaEventManager.java b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/event/VanillaEventManager.java index 1333eac8ece..7746158359d 100644 --- a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/event/VanillaEventManager.java +++ b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/event/VanillaEventManager.java @@ -25,22 +25,9 @@ package org.spongepowered.vanilla.launch.event; import com.google.inject.Singleton; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.manager.SpongeEventManager; -import org.spongepowered.plugin.PluginContainer; -import org.spongepowered.vanilla.launch.plugin.VanillaJavaPluginContainer; - -import java.lang.invoke.MethodHandles; @Singleton public final class VanillaEventManager extends SpongeEventManager { - @Override - protected MethodHandles.@Nullable Lookup getLookup(final PluginContainer plugin, final Class handle) { - if (plugin instanceof VanillaJavaPluginContainer vanilla) { - return vanilla.lookup(); - } - return null; - } - } diff --git a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/JavaPluginLoader.java b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/JavaPluginLoader.java index a672c47da58..410635b1304 100644 --- a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/JavaPluginLoader.java +++ b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/JavaPluginLoader.java @@ -32,30 +32,24 @@ import org.spongepowered.plugin.Environment; import org.spongepowered.plugin.InvalidPluginException; import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.builtin.jvm.JVMPluginLoader; -import org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource; +import org.spongepowered.plugin.PluginLoader; -import java.lang.invoke.MethodHandles; - -public final class JavaPluginLoader extends JVMPluginLoader { +public final class JavaPluginLoader implements PluginLoader { private final ArtifactVersion version = new DefaultArtifactVersion("1.0"); - private static final MethodHandles.Lookup SPONGE_LOOKUP = MethodHandles.lookup(); - @Override public ArtifactVersion version() { return this.version; } @Override - public VanillaJavaPluginContainer loadPlugin(final Environment environment, final PluginCandidate candidate, final ClassLoader targetClassLoader) + public VanillaJavaPluginContainer loadPlugin(final Environment environment, final PluginCandidate candidate, final ClassLoader targetClassLoader) throws InvalidPluginException { final VanillaJavaPluginContainer container = new VanillaJavaPluginContainer(candidate); try { final String mainClass = container.metadata().entrypoint(); final Class pluginClass = Class.forName(mainClass, true, targetClassLoader); - container.initializeLookup(MethodHandles.privateLookupIn(pluginClass, JavaPluginLoader.SPONGE_LOOKUP)); final Injector parentInjector = Launch.instance().lifecycle().platformInjector(); final Object plugin; diff --git a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaDummyPluginContainer.java b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaDummyPluginContainer.java index de1c07ccd66..f2053bc5117 100644 --- a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaDummyPluginContainer.java +++ b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaDummyPluginContainer.java @@ -26,79 +26,13 @@ import org.apache.logging.log4j.Logger; import org.spongepowered.common.applaunch.plugin.DummyPluginContainer; -import org.spongepowered.plugin.PluginContainer; -import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.PluginCandidate; +import org.spongepowered.plugin.builtin.StandardPluginContainer; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.Objects; -import java.util.Optional; -import java.util.StringJoiner; +public final class VanillaDummyPluginContainer extends StandardPluginContainer implements DummyPluginContainer { -public final class VanillaDummyPluginContainer implements PluginContainer, DummyPluginContainer { - - private final PluginMetadata metadata; - private final Logger logger; - private final Object instance; - - public VanillaDummyPluginContainer(final PluginMetadata metadata, final Logger logger, final Object instance) { - this.metadata = metadata; - this.logger = logger; - this.instance = instance; - } - - @Override - public PluginMetadata metadata() { - return this.metadata; - } - - @Override - public Logger logger() { - return this.logger; - } - - @Override - public Object instance() { - return this.instance; - } - - @Override - public Optional locateResource(final URI relative) { - final ClassLoader classLoader = this.getClass().getClassLoader(); - final URL resolved = classLoader.getResource(relative.getPath()); - try { - if (resolved == null) { - return Optional.empty(); - } - return Optional.of(resolved.toURI()); - } catch (final URISyntaxException ignored) { - return Optional.empty(); - } - } - - @Override - public int hashCode() { - return Objects.hash(this.metadata().id()); - } - - @Override - public boolean equals(final Object that) { - if (that == this) { - return true; - } - - if (!(that instanceof PluginContainer)) { - return false; - } - - return this.metadata().id().equals(((PluginContainer) that).metadata().id()); - } - - @Override - public String toString() { - return new StringJoiner(", ", VanillaDummyPluginContainer.class.getSimpleName() + "[", "]") - .add("metadata= " + this.metadata) - .toString(); + public VanillaDummyPluginContainer(final PluginCandidate candidate, final Logger logger, final Object instance) { + super(candidate, logger); + this.initializeInstance(instance); } } diff --git a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaJavaPluginContainer.java b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaJavaPluginContainer.java index eda180bfa20..7e1471bdfa0 100644 --- a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaJavaPluginContainer.java +++ b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaJavaPluginContainer.java @@ -26,39 +26,17 @@ import org.spongepowered.api.Sponge; import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.builtin.jvm.JVMPluginContainer; -import org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource; +import org.spongepowered.plugin.builtin.StandardPluginContainer; -import java.lang.invoke.MethodHandles; -import java.util.Objects; +public final class VanillaJavaPluginContainer extends StandardPluginContainer { -public final class VanillaJavaPluginContainer extends JVMPluginContainer { - - private MethodHandles.Lookup lookup; - - public VanillaJavaPluginContainer(final PluginCandidate candidate) { + public VanillaJavaPluginContainer(final PluginCandidate candidate) { super(candidate); } @Override protected void initializeInstance(final Object instance) { super.initializeInstance(instance); - Sponge.eventManager().registerListeners(this, instance); } - - /** - * {@return A lookup with access to internals of the module containing this plugin} - */ - public MethodHandles.Lookup lookup() { - return this.lookup; - } - - public void initializeLookup(final MethodHandles.Lookup lookup) { - if (this.lookup != null) { - throw new RuntimeException(String.format("Attempt made to set the lookup for plugin container '%s' twice!", - this.metadata().id())); - } - this.lookup = Objects.requireNonNull(lookup); - } } diff --git a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaPluginManager.java b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaPluginManager.java index f2251dffbf4..c1aa223a8bc 100644 --- a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaPluginManager.java +++ b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/VanillaPluginManager.java @@ -58,18 +58,15 @@ @Singleton public final class VanillaPluginManager implements SpongePluginManager { - private final Map plugins; private final Map instancesToPlugins; private final List sortedPlugins; - private final Map> locatedResources; private final Map containerToResource; public VanillaPluginManager() { this.plugins = new Object2ObjectOpenHashMap<>(); this.instancesToPlugins = new IdentityHashMap<>(); this.sortedPlugins = new ArrayList<>(); - this.locatedResources = new Object2ObjectOpenHashMap<>(); this.containerToResource = new Object2ObjectOpenHashMap<>(); } @@ -88,20 +85,16 @@ public Collection plugins() { return Collections.unmodifiableCollection(this.sortedPlugins); } - @SuppressWarnings("unchecked") public void loadPlugins(final VanillaPluginPlatform platform) { - this.locatedResources.putAll(platform.getResources()); - - final Map, PluginLanguageService> pluginLanguageLookup = new HashMap<>(); - final Map, PluginLoader> pluginLoaders = new HashMap<>(); + final Map pluginLanguageLookup = new HashMap<>(); + final Map> pluginLoaders = new HashMap<>(); // Initialise the plugin language loaders. - for (final Map.Entry, List>> candidate : platform.getCandidates().entrySet()) { - final PluginLanguageService languageService = candidate.getKey(); + for (final Map.Entry> candidate : platform.getCandidates().entrySet()) { + final PluginLanguageService languageService = candidate.getKey(); final String loaderClass = languageService.pluginLoader(); try { - pluginLoaders.put(languageService, - (PluginLoader) Class.forName(loaderClass).getConstructor().newInstance()); + pluginLoaders.put(languageService, (PluginLoader) Class.forName(loaderClass).getConstructor().newInstance()); } catch (final InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e); } @@ -111,23 +104,29 @@ public void loadPlugins(final VanillaPluginPlatform platform) { // Priority to platform plugins that will already exist here -- meaning the resolver will act upon them first // and if someone decides to give a plugin an ID that is the same as a platform plugin, the resolver will effectively // reject it. - final Set> resources = new LinkedHashSet<>(); + final Set resources = new LinkedHashSet<>(); pluginLanguageLookup.keySet().stream().filter(x -> this.plugins.containsKey(x.metadata().id())).forEach(resources::add); resources.addAll(pluginLanguageLookup.keySet()); - final ResolutionResult resolutionResult = DependencyResolver.resolveAndSortCandidates(resources, platform.logger()); - final Map, String> failedInstances = new HashMap<>(); - final Map, String> consequentialFailedInstances = new HashMap<>(); + final ResolutionResult resolutionResult = DependencyResolver.resolveAndSortCandidates(resources, platform.logger()); + final Map failedInstances = new HashMap<>(); + final Map consequentialFailedInstances = new HashMap<>(); final ClassLoader launchClassloader = VanillaLaunch.instance().getClass().getClassLoader(); - for (final PluginCandidate candidate : resolutionResult.sortedSuccesses()) { - final PluginContainer plugin = this.plugins.get(candidate.metadata().id()); + for (final PluginCandidate candidate : resolutionResult.sortedSuccesses()) { + final String id = candidate.metadata().id(); + if (id.indexOf('-') >= 0) { + platform.logger().warn("The dash character (-) is no longer supported in plugin ids.\n" + + "Plugin {} is still using it. If you are the developer of this plugin, please change the id.", id); + } + + final PluginContainer plugin = this.plugins.get(id); if (plugin != null) { if (plugin instanceof VanillaDummyPluginContainer) { continue; } // If we get here, we screwed up - duplicate IDs should have been detected earlier. // Place it in the resolution result... it'll then get picked up in the big error message - resolutionResult.duplicateIds().add(candidate.metadata().id()); + resolutionResult.duplicateIds().add(id); // but this is our screw up, let's also make a big point of it final PrettyPrinter prettyPrinter = new PrettyPrinter(120) @@ -135,8 +134,7 @@ public void loadPlugins(final VanillaPluginPlatform platform) { .hr() .addWrapped("Sponge attempted to create a second plugin with ID '%s'. This is not allowed - all plugins must have a unique " + "ID. Usually, Sponge will catch this earlier -- but in this case Sponge has validated two plugins with " - + "the same ID. Please report this error to Sponge.", - candidate.metadata().id()) + + "the same ID. Please report this error to Sponge.", id) .add() .add("Technical Details:") .add("Plugins to load:", 4); @@ -150,15 +148,15 @@ public void loadPlugins(final VanillaPluginPlatform platform) { // If a dependency failed to load, then we should bail on required dependencies too. // This should work fine, we're sorted so all deps should be in place at this stage. if (this.stillValid(candidate, consequentialFailedInstances)) { - final PluginLanguageService languageService = pluginLanguageLookup.get(candidate); - final PluginLoader pluginLoader = pluginLoaders.get(languageService); + final PluginLanguageService languageService = pluginLanguageLookup.get(candidate); + final PluginLoader pluginLoader = pluginLoaders.get(languageService); try { final PluginContainer container = pluginLoader.loadPlugin(platform.getStandardEnvironment(), candidate, launchClassloader); this.addPlugin(container); this.containerToResource.put(container, candidate.resource()); } catch (final InvalidPluginException e) { failedInstances.put(candidate, "Failed to construct: see stacktrace(s) above this message for details."); - platform.logger().error("Failed to construct plugin {}", candidate.metadata().id(), e); + platform.logger().error("Failed to construct plugin {}", id, e); } } } @@ -176,16 +174,12 @@ public void addPlugin(final PluginContainer plugin) { } } - public Map> locatedResources() { - return Collections.unmodifiableMap(this.locatedResources); - } - @Nullable public PluginResource resource(final PluginContainer container) { return this.containerToResource.get(container); } - private boolean stillValid(final PluginCandidate candidate, final Map, String> consequential) { + private boolean stillValid(final PluginCandidate candidate, final Map consequential) { final Optional failedId = candidate.metadata().dependencies().stream().filter(x -> !x.optional() && !this.plugins.containsKey(x.id())).findFirst(); if (failedId.isPresent()) { diff --git a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/resolver/DependencyResolver.java b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/resolver/DependencyResolver.java index aee9d1a5632..3d53223bac9 100644 --- a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/resolver/DependencyResolver.java +++ b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/resolver/DependencyResolver.java @@ -30,7 +30,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.util.Tuple; import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.PluginResource; import org.spongepowered.plugin.metadata.model.PluginDependency; import java.util.ArrayList; @@ -47,26 +46,26 @@ public final class DependencyResolver { - public static ResolutionResult resolveAndSortCandidates(final Collection> candidates, + public static ResolutionResult resolveAndSortCandidates(final Collection candidates, final Logger logger) { - final Map> nodes = new HashMap<>(); - final ResolutionResult resolutionResult = new ResolutionResult<>(); - for (final PluginCandidate candidate : candidates) { + final Map nodes = new HashMap<>(); + final ResolutionResult resolutionResult = new ResolutionResult(); + for (final PluginCandidate candidate : candidates) { final String id = candidate.metadata().id(); // If we already have an entry, this is now a duplicate ID situation. if (nodes.containsKey(id)) { resolutionResult.duplicateIds().add(id); } else { - nodes.put(id, new Node<>(candidate)); + nodes.put(id, new Node(candidate)); } } - for (final Map.Entry> entry : nodes.entrySet()) { + for (final Map.Entry entry : nodes.entrySet()) { // Attach deps, invalid deps will appear at this point. - final Node node = entry.getValue(); + final Node node = entry.getValue(); for (final PluginDependency pd : node.candidate.metadata().dependencies()) { final boolean isOptional = pd.optional(); - final Node dep = nodes.get(pd.id()); + final Node dep = nodes.get(pd.id()); if (dep == null) { if (isOptional) { @@ -113,21 +112,21 @@ public static ResolutionResult resolveAndSortCandi // Check for invalid deps DependencyResolver.checkCyclic(nodes.values(), resolutionResult); - for (final Node node : nodes.values()) { + for (final Node node : nodes.values()) { DependencyResolver.calculateSecondaryFailures(node, resolutionResult); } // Now to sort them. - final List> original = nodes.values().stream().filter(x -> !x.invalid).collect(Collectors.toCollection(ArrayList::new)); - final List> toLoad = new ArrayList<>(original); - final LinkedHashSet> sorted = new LinkedHashSet<>(); + final List original = nodes.values().stream().filter(x -> !x.invalid).collect(Collectors.toCollection(ArrayList::new)); + final List toLoad = new ArrayList<>(original); + final LinkedHashSet sorted = new LinkedHashSet<>(); toLoad.stream().filter(x -> x.dependencies.isEmpty() && x.optionalDependencies.isEmpty()).forEach(sorted::add); toLoad.removeIf(sorted::contains); int size = toLoad.size(); boolean excludeOptionals = false; while (!toLoad.isEmpty()) { boolean containsOptionalDeps = false; - for (final Node node : toLoad) { + for (final Node node : toLoad) { if (sorted.containsAll(node.dependencies) && DependencyResolver.checkOptionalDependencies(excludeOptionals, sorted, node)) { final boolean hasOptionalDeps = !node.optionalDependencies.isEmpty(); containsOptionalDeps |= hasOptionalDeps; @@ -162,15 +161,15 @@ public static ResolutionResult resolveAndSortCandi } } - final Collection> sortedSuccesses = resolutionResult.sortedSuccesses(); - for (final Node x : sorted) { + final Collection sortedSuccesses = resolutionResult.sortedSuccesses(); + for (final Node x : sorted) { sortedSuccesses.add(x.candidate); } return resolutionResult; } - private static boolean checkOptionalDependencies( - final boolean excludeOptionals, final Collection> sorted, final Node node) { + private static boolean checkOptionalDependencies( + final boolean excludeOptionals, final Collection sorted, final Node node) { if (excludeOptionals) { // We need to make sure we filter out any deps that have "before" requirements - so we load those with all required deps met. return node.optionalDependencies.stream().flatMap(x -> x.beforeRequiredDependency.stream()).distinct().allMatch(sorted::contains); @@ -178,7 +177,7 @@ private static boolean checkOptionalDependencies( return sorted.containsAll(node.optionalDependencies); } - private static void setDependency(final Node before, final Node after, final boolean optional) { + private static void setDependency(final Node before, final Node after, final boolean optional) { if (optional) { before.optionalDependencies.add(after); } else { @@ -203,24 +202,24 @@ private static boolean checkVersion(final @Nullable VersionRange requestedVersio return Objects.equals(requestedVersion.getRecommendedVersion(), dependencyVersion) || requestedVersion.containsVersion(dependencyVersion); } - private static void checkCyclic(final Collection> nodes, final ResolutionResult resolutionResult) { - for (final Node node : nodes) { + private static void checkCyclic(final Collection nodes, final ResolutionResult resolutionResult) { + for (final Node node : nodes) { if (!node.checked) { - final LinkedHashSet> nodeSet = new LinkedHashSet<>(); + final LinkedHashSet nodeSet = new LinkedHashSet<>(); nodeSet.add(node); DependencyResolver.checkCyclic(node, resolutionResult, nodeSet); } } } - private static void checkCyclic(final Node node, final ResolutionResult resolutionResult, - final LinkedHashSet> dependencyPath) { + private static void checkCyclic(final Node node, final ResolutionResult resolutionResult, + final LinkedHashSet dependencyPath) { if (node.invalid) { return; } // We're doing depth first. - for (final Node dependency : node.dependencies) { + for (final Node dependency : node.dependencies) { // We've already done this. Consequential failures will be handled later. if (dependency.checked) { continue; @@ -232,8 +231,8 @@ private static void checkCyclic(final Node node, f node.invalid = true; // We create the dependency path for printing later. boolean append = false; - final List> candidatePath = new LinkedList<>(); - for (final Node depInCycle : dependencyPath) { + final List candidatePath = new LinkedList<>(); + for (final Node depInCycle : dependencyPath) { append |= depInCycle == dependency; // all candidates from here are in the loop. if (append) { @@ -243,7 +242,7 @@ private static void checkCyclic(final Node node, f } // We'll only care about the one. - for (final PluginCandidate dep : candidatePath) { + for (final PluginCandidate dep : candidatePath) { resolutionResult.cyclicDependency().put(dep, candidatePath); } } else { @@ -254,7 +253,7 @@ private static void checkCyclic(final Node node, f } } - private static boolean calculateSecondaryFailures(final Node node, final ResolutionResult resolutionResult) { + private static boolean calculateSecondaryFailures(final Node node, final ResolutionResult resolutionResult) { if (node.secondaryChecked) { return node.invalid; } @@ -266,14 +265,14 @@ private static boolean calculateSecondaryFailures(fin return false; } - for (final Node depNode : node.dependencies) { + for (final Node depNode : node.dependencies) { if (DependencyResolver.calculateSecondaryFailures(depNode, resolutionResult)) { node.invalid = true; resolutionResult.cascadedFailure().computeIfAbsent(node.candidate, k -> new HashSet<>()).add(depNode.candidate); } } - for (final Node depNode : node.beforeRequiredDependency) { + for (final Node depNode : node.beforeRequiredDependency) { if (DependencyResolver.calculateSecondaryFailures(depNode, resolutionResult)) { node.invalid = true; resolutionResult.cascadedFailure().computeIfAbsent(node.candidate, k -> new HashSet<>()).add(depNode.candidate); @@ -282,17 +281,17 @@ private static boolean calculateSecondaryFailures(fin return node.invalid; } - static class Node { + static class Node { - final PluginCandidate candidate; - final Set> beforeRequiredDependency = new HashSet<>(); - final Set> dependencies = new HashSet<>(); - final Set> optionalDependencies = new HashSet<>(); + final PluginCandidate candidate; + final Set beforeRequiredDependency = new HashSet<>(); + final Set dependencies = new HashSet<>(); + final Set optionalDependencies = new HashSet<>(); boolean invalid = false; boolean checked = false; boolean secondaryChecked = false; - public Node(final PluginCandidate candidate) { + public Node(final PluginCandidate candidate) { this.candidate = candidate; } diff --git a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/resolver/ResolutionResult.java b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/resolver/ResolutionResult.java index 84c1966c702..fb1124b893b 100644 --- a/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/resolver/ResolutionResult.java +++ b/vanilla/src/launch/java/org/spongepowered/vanilla/launch/plugin/resolver/ResolutionResult.java @@ -29,7 +29,6 @@ import org.spongepowered.api.util.Tuple; import org.spongepowered.common.util.PrettyPrinter; import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.PluginResource; import java.util.Collection; import java.util.HashMap; @@ -38,14 +37,14 @@ import java.util.Map; import java.util.stream.Collectors; -public final class ResolutionResult { +public final class ResolutionResult { - private final LinkedHashSet> sortedSuccesses; + private final LinkedHashSet sortedSuccesses; private final Collection duplicateIds; - private final Map, Collection> missingDependencies; - private final Map, Collection>>> versionMismatch; - private final Map, Collection>> cyclicDependency; - private final Map, Collection>> cascadedFailure; + private final Map> missingDependencies; + private final Map>> versionMismatch; + private final Map> cyclicDependency; + private final Map> cascadedFailure; public ResolutionResult() { this.sortedSuccesses = new LinkedHashSet<>(); @@ -56,7 +55,7 @@ public ResolutionResult() { this.cascadedFailure = new HashMap<>(); } - public Collection> sortedSuccesses() { + public Collection sortedSuccesses() { return this.sortedSuccesses; } @@ -64,25 +63,25 @@ public Collection duplicateIds() { return this.duplicateIds; } - public Map, Collection> missingDependencies() { + public Map> missingDependencies() { return this.missingDependencies; } - public Map, Collection>>> versionMismatch() { + public Map>> versionMismatch() { return this.versionMismatch; } - public Map, Collection>> cyclicDependency() { + public Map> cyclicDependency() { return this.cyclicDependency; } - public Map, Collection>> cascadedFailure() { + public Map> cascadedFailure() { return this.cascadedFailure; } public void printErrorsIfAny( - final Map, String > failedInstance, - final Map, String> consequentialFailedInstance, + final Map failedInstance, + final Map consequentialFailedInstance, final Logger logger) { final int noOfFailures = this.numberOfFailures() + failedInstance.size() + consequentialFailedInstance.size(); if (noOfFailures == 0) { @@ -106,7 +105,7 @@ public void printErrorsIfAny( if (!this.missingDependencies.isEmpty()) { errorPrinter.add(); errorPrinter.add("The following plugins are missing dependencies:"); - for (final Map.Entry, Collection> entry : this.missingDependencies.entrySet()) { + for (final Map.Entry> entry : this.missingDependencies.entrySet()) { errorPrinter.add(" * %s requires [ %s ]", entry.getKey().metadata().id(), String.join(", ", entry.getValue())); @@ -116,9 +115,9 @@ public void printErrorsIfAny( if (!this.versionMismatch.isEmpty()) { errorPrinter.add(); errorPrinter.add("The following plugins require different version(s) of dependencies you have installed:"); - for (final Map.Entry, Collection>>> entry : this.versionMismatch.entrySet()) { - final PluginCandidate candidate = entry.getKey(); - final Collection>> mismatchedDeps = entry.getValue(); + for (final Map.Entry>> entry : this.versionMismatch.entrySet()) { + final PluginCandidate candidate = entry.getKey(); + final Collection> mismatchedDeps = entry.getValue(); final String errorString = mismatchedDeps.stream() .map(x -> String.format("%s version %s (currently version %s)", x.second().metadata().id(), x.first(), x.second().metadata().version())) @@ -132,7 +131,7 @@ public void printErrorsIfAny( if (!this.cyclicDependency.isEmpty()) { errorPrinter.add(); errorPrinter.add("The following plugins were found to have cyclic dependencies:"); - for (final Map.Entry, Collection>> node : this.cyclicDependency.entrySet()) { + for (final Map.Entry> node : this.cyclicDependency.entrySet()) { errorPrinter.add(" * %s has dependency cycle [ ... -> %s -> ... ]", node.getKey().metadata().id(), node.getValue().stream().map(x -> x.metadata().id()).collect(Collectors.joining(" -> "))); @@ -142,7 +141,7 @@ public void printErrorsIfAny( if (!failedInstance.isEmpty()) { errorPrinter.add(); errorPrinter.add("The following plugins threw exceptions when being created (report these to the plugin authors):"); - for (final Map.Entry, String> node : failedInstance.entrySet()) { + for (final Map.Entry node : failedInstance.entrySet()) { errorPrinter.add(" * %s with the error message \"%s\"", node.getKey().metadata().id(), node.getValue()); @@ -150,15 +149,15 @@ public void printErrorsIfAny( } if (!this.cascadedFailure.isEmpty() || !consequentialFailedInstance.isEmpty()) { - final Map, String> mergedFailures = new HashMap<>(consequentialFailedInstance); - for (final Map.Entry, Collection>> entry : this.cascadedFailure.entrySet()) { + final Map mergedFailures = new HashMap<>(consequentialFailedInstance); + for (final Map.Entry> entry : this.cascadedFailure.entrySet()) { final String error = entry.getValue().stream().map(x -> x.metadata().id()).collect(Collectors.joining(", ")); mergedFailures.merge(entry.getKey(), error, (old, incoming) -> old + ", " + incoming); } errorPrinter.add(); errorPrinter.add("The following plugins are not loading because they depend on plugins that will not load:"); - for (final Map.Entry, String> node : mergedFailures.entrySet()) { + for (final Map.Entry node : mergedFailures.entrySet()) { errorPrinter.add(" * %s depends on [ %s ]", node.getKey().metadata().id(), // nothing wrong with this plugin other than the other plugins, diff --git a/vanilla/src/launch/resource-templates/META-INF/sponge_plugins.json b/vanilla/src/launch/resource-templates/META-INF/sponge_plugins.json index de066322672..5a9c035285f 100644 --- a/vanilla/src/launch/resource-templates/META-INF/sponge_plugins.json +++ b/vanilla/src/launch/resource-templates/META-INF/sponge_plugins.json @@ -6,27 +6,11 @@ "license": "MIT", "plugins": [ { - "id": "minecraft", - "name": "Minecraft", - "version": "{{ minecraftVersion }}", - "entrypoint": "org.spongepowered.common.launcher.Launcher", - "links": { - "homepage": "https://www.minecraft.net" - }, - "contributors": [ - { - "name": "Mojang AB", - "description": "Lead Developer" - } - ] - }, - { - "loader": "java_plain", - "id": "spongeapi", - "name": "SpongeAPI", - "version": "{{ apiVersion }}", - "entrypoint": "org.spongepowered.api.Sponge", - "description": "The Minecraft API specification", + "id": "spongevanilla", + "name": "SpongeVanilla", + "version": "{{ version }}", + "entrypoint": "org.spongepowered.vanilla.SpongeVanilla", + "description": "Vanilla Minecraft with Sponge", "links": { "homepage": "https://www.spongepowered.org", "source": "https://www.spongepowered.org/source", @@ -37,6 +21,12 @@ "name": "SpongePowered", "description": "Lead Developer" } + ], + "dependencies": [ + { + "id": "sponge", + "version": "{{ minecraftVersion }}-{{ apiVersion }}" + } ] }, { @@ -68,11 +58,11 @@ ] }, { - "id": "spongevanilla", - "name": "SpongeVanilla", - "version": "{{ version }}", - "entrypoint": "org.spongepowered.vanilla.SpongeVanilla", - "description": "Vanilla Minecraft with Sponge", + "id": "spongeapi", + "name": "SpongeAPI", + "version": "{{ apiVersion }}", + "entrypoint": "org.spongepowered.api.Sponge", + "description": "The Minecraft API specification", "links": { "homepage": "https://www.spongepowered.org", "source": "https://www.spongepowered.org/source", @@ -83,12 +73,6 @@ "name": "SpongePowered", "description": "Lead Developer" } - ], - "dependencies": [ - { - "id": "sponge", - "version": "{{ minecraftVersion }}-{{ apiVersion }}" - } ] } ] diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginPackResources.java b/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginPackResources.java index 8558de7459b..067a29faa37 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginPackResources.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginPackResources.java @@ -38,15 +38,12 @@ import org.spongepowered.common.SpongeCommon; import org.spongepowered.plugin.PluginContainer; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; -import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -55,15 +52,13 @@ public final class PluginPackResources extends AbstractPackResources { private final PluginContainer container; private final PackMetadataSection metadata; - private final @Nullable Supplier fileSystem; - private final File file; + private final @Nullable Path pluginRoot; - public PluginPackResources(final PackLocationInfo info, final PluginContainer container, final @Nullable Supplier fileSystem) { + public PluginPackResources(final PackLocationInfo info, final PluginContainer container, final @Nullable Path pluginRoot) { super(info); - this.file = new File("sponge_plugin_pack"); this.container = container; this.metadata = new PackMetadataSection(Component.literal("Plugin Resources"), 6, Optional.empty()); - this.fileSystem = fileSystem; + this.pluginRoot = pluginRoot; } @Override @@ -73,7 +68,7 @@ public IoSupplier getRootResource(final String... var1) { } private IoSupplier getResource(final String rawPath) { - final Optional uri = this.container.locateResource(URI.create(rawPath)); + final Optional uri = this.container.locateResource(rawPath); if (uri.isEmpty()) { return null; } @@ -153,10 +148,10 @@ public void close() { } private @Nullable Path typeRoot(final PackType type) throws IOException { - if (this.fileSystem == null) { + if (this.pluginRoot == null) { return null; } - return this.fileSystem.get().getPath(type.getDirectory()); + return this.pluginRoot.resolve(type.getDirectory()); } } diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginRepositorySource.java b/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginRepositorySource.java index 84011e55e49..7d2b6972662 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginRepositorySource.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginRepositorySource.java @@ -33,19 +33,17 @@ import net.minecraft.server.packs.repository.PackRepository; import net.minecraft.server.packs.repository.PackSource; import net.minecraft.server.packs.repository.RepositorySource; -import org.apache.commons.io.FilenameUtils; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.launch.Launch; import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.PluginResource; -import org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; import org.spongepowered.vanilla.bridge.server.packs.repository.PackRepositoryBridge_Vanilla; import org.spongepowered.vanilla.launch.plugin.VanillaPluginManager; -import java.nio.file.FileSystem; +import java.nio.file.Path; import java.util.Optional; import java.util.function.Consumer; -import java.util.function.Supplier; public final class PluginRepositorySource implements RepositorySource { @@ -67,17 +65,13 @@ public void loadPacks(final Consumer callback) { final String id = "plugin-" + pluginContainer.metadata().id(); final PluginResource resource = pluginManager.resource(pluginContainer); // TODO: provide hook in the resource to return the file system for all resource types? - // Also -- can we fake a FileSystem for a non-Jar (needs thinking about).... - @Nullable Supplier fileSystemSupplier = null; - if (resource instanceof JVMPluginResource) { - final String extension = FilenameUtils.getExtension(resource.path().getFileName().toString()); - if ("jar".equals(extension)) { - fileSystemSupplier = ((JVMPluginResource) resource)::fileSystem; - } + @Nullable Path pluginRoot = null; + if (resource instanceof JVMPluginResource jvmResource) { + pluginRoot = jvmResource.resourcesRoot(); } final PackLocationInfo info = new PackLocationInfo(id, Component.literal(id), PackSource.DEFAULT, Optional.empty()); - final PluginPackResources packResources = new PluginPackResources(info, pluginContainer, fileSystemSupplier); + final PluginPackResources packResources = new PluginPackResources(info, pluginContainer, pluginRoot); final Pack.ResourcesSupplier packSupplier = new Pack.ResourcesSupplier() { @Override