diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 5b6b154fd91..dc10e45473e 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -29,13 +29,13 @@ jobs: matrix: project: - vanilla -# - forge + - forge - neoforge include: - project: vanilla gradleProject: SpongeVanilla -# - project: forge -# gradleProject: SpongeForge + - project: forge + gradleProject: SpongeForge - project: neoforge gradleProject: SpongeNeo steps: diff --git a/bootstrap-dev/build.gradle.kts b/bootstrap-dev/build.gradle.kts new file mode 100644 index 00000000000..408470bf53e --- /dev/null +++ b/bootstrap-dev/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + api(libs.bootstrap.api) +} diff --git a/bootstrap-dev/src/main/java/module-info.java b/bootstrap-dev/src/main/java/module-info.java new file mode 100644 index 00000000000..c46b80848e7 --- /dev/null +++ b/bootstrap-dev/src/main/java/module-info.java @@ -0,0 +1,6 @@ +module org.spongepowered.boostrap.dev { + requires net.minecraftforge.bootstrap.api; + exports org.spongepowered.bootstrap.dev; + + provides net.minecraftforge.bootstrap.api.BootstrapClasspathModifier with org.spongepowered.bootstrap.dev.SpongeDevClasspathFixer; +} diff --git a/bootstrap-dev/src/main/java/org/spongepowered/bootstrap/dev/Multimap.java b/bootstrap-dev/src/main/java/org/spongepowered/bootstrap/dev/Multimap.java new file mode 100644 index 00000000000..e56fa029fdd --- /dev/null +++ b/bootstrap-dev/src/main/java/org/spongepowered/bootstrap/dev/Multimap.java @@ -0,0 +1,36 @@ +/* + * 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.bootstrap.dev; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +public class Multimap extends HashMap> { + + public void add(K key, V value) { + this.computeIfAbsent(key, k -> new LinkedList<>()).add(value); + } +} diff --git a/vanilla/src/devlaunch/java/org/spongepowered/vanilla/devlaunch/SourceSet.java b/bootstrap-dev/src/main/java/org/spongepowered/bootstrap/dev/SourceSet.java similarity index 98% rename from vanilla/src/devlaunch/java/org/spongepowered/vanilla/devlaunch/SourceSet.java rename to bootstrap-dev/src/main/java/org/spongepowered/bootstrap/dev/SourceSet.java index 6bc2ef07a88..badeb5407dc 100644 --- a/vanilla/src/devlaunch/java/org/spongepowered/vanilla/devlaunch/SourceSet.java +++ b/bootstrap-dev/src/main/java/org/spongepowered/bootstrap/dev/SourceSet.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.vanilla.devlaunch; +package org.spongepowered.bootstrap.dev; import java.nio.file.Files; import java.nio.file.Path; diff --git a/vanilla/src/devlaunch/java/org/spongepowered/vanilla/devlaunch/SpongeDevClasspathFixer.java b/bootstrap-dev/src/main/java/org/spongepowered/bootstrap/dev/SpongeDevClasspathFixer.java similarity index 71% rename from vanilla/src/devlaunch/java/org/spongepowered/vanilla/devlaunch/SpongeDevClasspathFixer.java rename to bootstrap-dev/src/main/java/org/spongepowered/bootstrap/dev/SpongeDevClasspathFixer.java index ba527b23e6b..ab566c5f915 100644 --- a/vanilla/src/devlaunch/java/org/spongepowered/vanilla/devlaunch/SpongeDevClasspathFixer.java +++ b/bootstrap-dev/src/main/java/org/spongepowered/bootstrap/dev/SpongeDevClasspathFixer.java @@ -22,18 +22,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.devlaunch; +package org.spongepowered.bootstrap.dev; import net.minecraftforge.bootstrap.api.BootstrapClasspathModifier; +import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -53,14 +51,16 @@ public String name() { @Override public boolean process(final List classpath) { final Path spongeRoot = Paths.get(System.getProperty("sponge.dev.root")).toAbsolutePath(); - final Set bootLibs = Set.of(System.getProperty("sponge.dev.boot").split(";")); - final Set gameShadedLibs = Set.of(System.getProperty("sponge.dev.gameShaded").split(";")); + final Set ignoredLibs = Set.of("bootstrap-dev.jar"); + final Set bootLibs = Set.of(System.getProperty("sponge.dev.boot").split(File.pathSeparator)); + final Set gameShadedLibs = Set.of(System.getProperty("sponge.dev.gameShaded").split(File.pathSeparator)); - final Map> bootSourceSets = new HashMap<>(); - final Map> unknownProjects = new HashMap<>(); + // boot layer + final Multimap bootUnions = new Multimap<>(); - final List spongeImplUnion = new ArrayList<>(); - final List gameLibs = new ArrayList<>(); + // game or plugin layer + final Multimap unions = new Multimap<>(); + final List libs = new ArrayList<>(); final AtomicBoolean hasAPISourceSet = new AtomicBoolean(false); @@ -69,7 +69,7 @@ public boolean process(final List classpath) { return false; } - final Path path = paths[0].toAbsolutePath(); + final Path path = paths[0]; final SourceSet sourceSet = SourceSet.identify(path); if (sourceSet != null) { if (sourceSet.project().startsWith(spongeRoot)) { @@ -79,8 +79,11 @@ public boolean process(final List classpath) { final String projectName = spongeRoot.relativize(sourceSet.project()).toString(); switch (projectName) { - case "modlauncher-transformers": - bootSourceSets.computeIfAbsent("transformers", k -> new LinkedList<>()).add(path); + case "bootstrap-dev": + // ignore + break; + case "modlauncher-transformers", "library-manager": + bootUnions.add(projectName, path); break; case "SpongeAPI": switch (sourceSet.name()) { @@ -91,25 +94,25 @@ public boolean process(final List classpath) { hasAPISourceSet.set(true); // no break default: - spongeImplUnion.add(path); + unions.add("sponge", path); break; } break; - case "", "vanilla": + case "", "vanilla", "forge": switch (sourceSet.name()) { - case "devlaunch": - // ignore - break; case "applaunch": - bootSourceSets.computeIfAbsent("applaunch", k -> new LinkedList<>()).add(path); + bootUnions.add("applaunch", path); + break; + case "lang": + unions.add("lang", path); break; default: - spongeImplUnion.add(path); + unions.add("sponge", path); break; } break; default: - unknownProjects.computeIfAbsent(projectName, k -> new LinkedList<>()).add(path); + unions.add(projectName, path); break; } } else { @@ -117,13 +120,20 @@ public boolean process(final List classpath) { System.out.println("External SourceSet (" + sourceSet + "): " + path); } - unknownProjects.computeIfAbsent(sourceSet.project().toString(), k -> new LinkedList<>()).add(path); + unions.add(sourceSet.project().toString(), path); } return true; } final String fileName = path.getFileName().toString(); + if (ignoredLibs.contains(fileName)) { + if (DEBUG) { + System.out.println("Ignored: " + path); + } + return true; + } + if (bootLibs.contains(fileName)) { if (DEBUG) { System.out.println("Boot: " + path); @@ -135,14 +145,14 @@ public boolean process(final List classpath) { if (DEBUG) { System.out.println("Sponge: " + path); } - spongeImplUnion.add(path); + unions.add("sponge", path); return true; } if (DEBUG) { System.out.println("Game: " + path); } - gameLibs.add(path); + libs.add(path); return true; }); @@ -150,12 +160,12 @@ public boolean process(final List classpath) { return false; } - for (final List sourceSets : bootSourceSets.values()) { + for (final List sourceSets : bootUnions.values()) { classpath.add(sourceSets.toArray(Path[]::new)); } if (hasAPISourceSet.get()) { - spongeImplUnion.removeIf((path) -> { + unions.get("sponge").removeIf((path) -> { if (Files.isRegularFile(path)) { final String fileName = path.getFileName().toString(); if (fileName.startsWith("spongeapi") && fileName.endsWith(".jar")) { @@ -169,26 +179,23 @@ public boolean process(final List classpath) { }); } - final StringBuilder gameResourcesEnvBuilder = new StringBuilder(); - for (final Path resource : gameLibs) { - gameResourcesEnvBuilder.append(resource).append(';'); + final StringBuilder resourcesEnvBuilder = new StringBuilder(); + for (final Path resource : libs) { + resourcesEnvBuilder.append(resource).append(File.pathSeparator); } - for (final List project : unknownProjects.values()) { + for (final List project : unions.values()) { for (final Path resource : project) { - gameResourcesEnvBuilder.append(resource).append('&'); + resourcesEnvBuilder.append(resource).append('&'); } - gameResourcesEnvBuilder.setCharAt(gameResourcesEnvBuilder.length() - 1, ';'); - } - for (final Path resource : spongeImplUnion) { - gameResourcesEnvBuilder.append(resource).append('&'); + resourcesEnvBuilder.setCharAt(resourcesEnvBuilder.length() - 1, File.pathSeparatorChar); } - gameResourcesEnvBuilder.setLength(gameResourcesEnvBuilder.length() - 1); - final String gameResourcesEnv = gameResourcesEnvBuilder.toString(); + resourcesEnvBuilder.setLength(resourcesEnvBuilder.length() - 1); + final String resourcesEnv = resourcesEnvBuilder.toString(); if (DEBUG) { - System.out.println("Game resources env: " + gameResourcesEnv); + System.out.println("Resources env: " + resourcesEnv); } - System.setProperty("sponge.gameResources", gameResourcesEnv); + System.setProperty("sponge.resources", resourcesEnv); return true; } } diff --git a/bootstrap-dev/src/main/resources/META-INF/services/net.minecraftforge.bootstrap.api.BootstrapClasspathModifier b/bootstrap-dev/src/main/resources/META-INF/services/net.minecraftforge.bootstrap.api.BootstrapClasspathModifier new file mode 100644 index 00000000000..32a491ef8a6 --- /dev/null +++ b/bootstrap-dev/src/main/resources/META-INF/services/net.minecraftforge.bootstrap.api.BootstrapClasspathModifier @@ -0,0 +1 @@ +org.spongepowered.bootstrap.dev.SpongeDevClasspathFixer diff --git a/build-logic/build.gradle b/build-logic/build.gradle index 5cabd817724..1539cbfb1a6 100644 --- a/build-logic/build.gradle +++ b/build-logic/build.gradle @@ -13,6 +13,11 @@ indra { dependencies { api(apiLibs.gson) implementation("gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:${apiLibs.versions.ideaExt.get()}") + implementation(libs.at) + implementation(libs.accessWidener) + + // arch-loom is used by SpongeNeo but must be declared here to avoid a classloader conflict with its transitive dependencies + api("dev.architectury:architectury-loom:1.6.411") } indraSpotlessLicenser { diff --git a/build-logic/settings.gradle b/build-logic/settings.gradle index 9e7a64e236c..3e53ae9cde4 100644 --- a/build-logic/settings.gradle +++ b/build-logic/settings.gradle @@ -9,7 +9,14 @@ pluginManagement { dependencyResolutionManagement { repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS - repositories { pluginManagement.repositories.each { add(it) } } + repositories { + pluginManagement.repositories.each { add(it) } + maven { + url = "https://maven.architectury.dev/" + name = "architectury" + } + } + versionCatalogs { libs { from files("../gradle/libs.versions.toml") diff --git a/build-logic/src/main/java/org/spongepowered/gradle/impl/AWToAT.java b/build-logic/src/main/java/org/spongepowered/gradle/impl/AWToAT.java new file mode 100644 index 00000000000..da782c13dc6 --- /dev/null +++ b/build-logic/src/main/java/org/spongepowered/gradle/impl/AWToAT.java @@ -0,0 +1,95 @@ +/* + * 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.gradle.impl; + +import dev.architectury.at.AccessChange; +import dev.architectury.at.AccessTransform; +import dev.architectury.at.AccessTransformSet; +import dev.architectury.at.ModifierChange; +import dev.architectury.at.io.AccessTransformFormats; +import net.fabricmc.accesswidener.AccessWidenerReader; +import net.fabricmc.accesswidener.AccessWidenerVisitor; +import org.cadixdev.bombe.type.signature.MethodSignature; +import org.gradle.api.GradleException; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +public class AWToAT { + private static final Logger logger = Logging.getLogger(AWToAT.class); + + public static void convert(final Iterable awFiles, final File atFile) { + AWToAT.logger.lifecycle("Converting AWs {} to AT {} ...", awFiles, atFile); + + final AccessTransformSet at = AccessTransformSet.create(); + + for (final File awFile : awFiles) { + try (final BufferedReader reader = Files.newBufferedReader(awFile.toPath())) { + AWToAT.convert(reader, at); + } catch (final IOException e) { + throw new GradleException("Failed to read access widener: " + awFile, e); + } + } + + try (final BufferedWriter writer = Files.newBufferedWriter(atFile.toPath())) { + AccessTransformFormats.FML.write(writer, at); + } catch (IOException e) { + throw new GradleException("Failed to write access transformer: " + atFile, e); + } + + AWToAT.logger.lifecycle("Converted AWs to AT."); + } + + private static void convert(final BufferedReader reader, final AccessTransformSet at) throws IOException { + new AccessWidenerReader(new AccessWidenerVisitor() { + @Override + public void visitClass(final String name, final AccessWidenerReader.AccessType access, final boolean transitive) { + at.getOrCreateClass(name).merge(AWToAT.convertEntry(access)); + } + + @Override + public void visitMethod(final String owner, final String name, final String descriptor, final AccessWidenerReader.AccessType access, final boolean transitive) { + at.getOrCreateClass(owner).mergeMethod(MethodSignature.of(name, descriptor), AWToAT.convertEntry(access)); + } + + @Override + public void visitField(final String owner, final String name, final String descriptor, final AccessWidenerReader.AccessType access, final boolean transitive) { + at.getOrCreateClass(owner).mergeField(name, AWToAT.convertEntry(access)); + } + }).read(reader); + } + + private static AccessTransform convertEntry(final AccessWidenerReader.AccessType access) { + return switch (access) { + case ACCESSIBLE -> AccessTransform.of(AccessChange.PUBLIC); + case EXTENDABLE, MUTABLE -> AccessTransform.of(AccessChange.PUBLIC, ModifierChange.REMOVE); + }; + } +} 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 09dc131237a..7881f2a2d2b 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 @@ -37,6 +37,7 @@ import javax.annotation.Nullable; import javax.inject.Inject; +import java.io.File; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -44,6 +45,8 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.StringJoiner; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -60,33 +63,100 @@ public SpongeImplementationExtension(final Project project, final Logger logger) this.logger = logger; } - public void copyModulesExcludingProvided(final Configuration source, final Configuration provided, final Configuration target) { - final DependencyHandler deps = this.project.getDependencies(); + private record Module(ModuleIdentifier id, String classifier) {} + + public String buildRuntimeFileNames(final Configuration config) { + final Configuration runtimeConfig = this.project.getConfigurations().getByName("runtimeClasspath"); + + final Map runtimeFileNames = new HashMap<>(); + for (final ResolvedArtifact artifact : runtimeConfig.getResolvedConfiguration().getResolvedArtifacts()) { + final ComponentIdentifier id = artifact.getId().getComponentIdentifier(); + if (id instanceof ModuleComponentIdentifier moduleId) { + runtimeFileNames.put(new Module(moduleId.getModuleIdentifier(), artifact.getClassifier()), artifact.getFile().getName()); + } + } + + final StringJoiner joiner = new StringJoiner(File.pathSeparator); + + for (final ResolvedArtifact artifact : config.getResolvedConfiguration().getResolvedArtifacts()) { + final ComponentIdentifier id = artifact.getId().getComponentIdentifier(); + + String fileName = null; + if (id instanceof ModuleComponentIdentifier moduleId) { + fileName = runtimeFileNames.get(new Module(moduleId.getModuleIdentifier(), artifact.getClassifier())); + } + + if (fileName == null) { + fileName = artifact.getFile().getName(); + } + + joiner.add(fileName); + } + + return joiner.toString(); + } + private String buildDependencyNotation(final ModuleComponentIdentifier moduleId, final String classifier) { + final ModuleIdentifier module = moduleId.getModuleIdentifier(); + String notation = module.getGroup() + ":" + module.getName() + ":" + moduleId.getVersion(); + if (classifier != null) { + notation += ":" + classifier; + } + return notation; + } + + public void copyModulesExcludingProvided(final Configuration source, final Configuration provided, final Configuration target) { final Map providedModuleVersions = new HashMap<>(); - for (ResolvedArtifact artifact : provided.getResolvedConfiguration().getResolvedArtifacts()) { + for (final ResolvedArtifact artifact : provided.getResolvedConfiguration().getResolvedArtifacts()) { final ComponentIdentifier id = artifact.getId().getComponentIdentifier(); - if (id instanceof ModuleComponentIdentifier) { - final ModuleComponentIdentifier moduleId = (ModuleComponentIdentifier) id; + if (id instanceof ModuleComponentIdentifier moduleId) { providedModuleVersions.put(moduleId.getModuleIdentifier(), moduleId.getVersion()); } } + this.copyModulesExcludingPredicate(source, (moduleId) -> { + final ModuleIdentifier module = moduleId.getModuleIdentifier(); + final String version = moduleId.getVersion(); + + final String providedVersion = providedModuleVersions.get(module); + if (providedVersion == null) { + return false; + } + + if (!providedVersion.equals(version)) { + this.logger.warn("Version mismatch for module {}. {} expects {} but {} has {}.", module, source.getName(), version, provided.getName(), providedVersion); + } + return true; + }, target); + } + + public void copyModulesExcludingPrefix(final Configuration source, final String group, final String namePrefix, final Configuration target) { + this.copyModulesExcludingPredicate(source, (moduleId) -> { + final ModuleIdentifier module = moduleId.getModuleIdentifier(); + return module.getGroup().equals(group) && module.getName().startsWith(namePrefix); + }, target); + } + + public void copyModulesExcludingExact(final Configuration source, final String group, final String name, final Configuration target) { + this.copyModulesExcludingPredicate(source, (moduleId) -> { + final ModuleIdentifier module = moduleId.getModuleIdentifier(); + return module.getGroup().equals(group) && module.getName().equals(name); + }, target); + } + + private void copyModulesExcludingPredicate(final Configuration source, final Predicate predicate, final Configuration target) { + final DependencyHandler deps = this.project.getDependencies(); + for (ResolvedArtifact artifact : source.getResolvedConfiguration().getResolvedArtifacts()) { final ComponentIdentifier id = artifact.getId().getComponentIdentifier(); - if (id instanceof ModuleComponentIdentifier) { - final ModuleComponentIdentifier moduleId = (ModuleComponentIdentifier) id; - final ModuleIdentifier module = moduleId.getModuleIdentifier(); - final String version = moduleId.getVersion(); - - final String providedVersion = providedModuleVersions.get(module); - if (providedVersion == null) { - ModuleDependency dep = (ModuleDependency) deps.create(module.getGroup() + ":" + module.getName() + ":" + version); - dep.setTransitive(false); - target.getDependencies().add(dep); - } else if (!providedVersion.equals(version)) { - this.logger.warn("Version mismatch for module {}. {} expects {} but {} has {}.", module, source.getName(), version, provided.getName(), providedVersion); + if (id instanceof ModuleComponentIdentifier moduleId) { + if (predicate.test(moduleId)) { + continue; } + + final ModuleDependency dep = (ModuleDependency) deps.create(this.buildDependencyNotation(moduleId, artifact.getClassifier())); + dep.setTransitive(false); + target.getDependencies().add(dep); } // projects are not copied because we cannot always recreate properly a project dependency from the resolved artefact diff --git a/build.gradle.kts b/build.gradle.kts index 936320ff20b..85ce26ad312 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -176,6 +176,10 @@ dependencies { } } +minecraft { + accessWideners(main.resources.filter { it.name.endsWith(".accesswidener") }) +} + allprojects { configurations.configureEach { resolutionStrategy.dependencySubstitution { @@ -199,13 +203,6 @@ allprojects { minecraft { version(minecraftVersion) injectRepositories(false) - project.sourceSets["main"].resources - .filter { it.name.endsWith(".accesswidener") } - .files - .forEach { - accessWideners(it) - parent?.minecraft?.accessWideners(it) - } } dependencies { diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts index 7aeb4a2e696..34ba73d2a51 100644 --- a/forge/build.gradle.kts +++ b/forge/build.gradle.kts @@ -1,12 +1,13 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import net.fabricmc.loom.api.LoomGradleExtensionAPI +import net.minecraftforge.gradle.userdev.UserDevExtension +import org.spongepowered.gradle.impl.AWToAT buildscript { repositories { maven("https://repo.spongepowered.org/repository/maven-public") { name = "sponge" } - maven("https://maven.architectury.dev/") + maven("https://maven.minecraftforge.net/") } } @@ -14,10 +15,11 @@ plugins { alias(libs.plugins.shadow) id("implementation-structure") alias(libs.plugins.blossom) - id("dev.architectury.loom") version "1.6.411" + id("net.minecraftforge.gradle") version "[6.0.24,6.2)" } val commonProject = parent!! +val bootstrapDevProject = commonProject.project(":bootstrap-dev") val transformersProject = commonProject.project(":modlauncher-transformers") val libraryManagerProject = commonProject.project(":library-manager") val testPluginsProject: Project? = rootProject.subprojects.find { "testplugins" == it.name } @@ -38,6 +40,11 @@ repositories { } // SpongeForge libraries +val bootLibrariesConfig: NamedDomainObjectProvider = configurations.register("bootLibraries") { + // Ideally we would filter minecraft itself and only keep its dependencies for this layer, + // but I couldn't find a way to do it without breaking ForgeGradle. + extendsFrom(configurations.minecraft.get()) +} val serviceLibrariesConfig: NamedDomainObjectProvider = configurations.register("serviceLibraries") val gameLibrariesConfig: NamedDomainObjectProvider = configurations.register("gameLibraries") @@ -46,28 +53,21 @@ val gameManagedLibrariesConfig: NamedDomainObjectProvider = confi val serviceShadedLibrariesConfig: NamedDomainObjectProvider = configurations.register("serviceShadedLibraries") val gameShadedLibrariesConfig: NamedDomainObjectProvider = configurations.register("gameShadedLibraries") -val runTaskOnlyConfig: NamedDomainObjectProvider = configurations.register("runTaskOnly") - -configurations.named("forgeRuntimeLibrary") { - extendsFrom(serviceLibrariesConfig.get()) -} - // ModLauncher layers +val bootLayerConfig: NamedDomainObjectProvider = configurations.register("bootLayer") { + extendsFrom(bootLibrariesConfig.get()) +} val serviceLayerConfig: NamedDomainObjectProvider = configurations.register("serviceLayer") { + extendsFrom(bootLayerConfig.get()) extendsFrom(serviceLibrariesConfig.get()) - extendsFrom(configurations.getByName("forgeDependencies")) } val langLayerConfig: NamedDomainObjectProvider = configurations.register("langLayer") { - extendsFrom(configurations.getByName("forgeDependencies")) + extendsFrom(bootLayerConfig.get()) } val gameLayerConfig: NamedDomainObjectProvider = configurations.register("gameLayer") { extendsFrom(serviceLayerConfig.get()) extendsFrom(langLayerConfig.get()) extendsFrom(gameLibrariesConfig.get()) - - afterEvaluate { - extendsFrom(configurations.getByName("minecraftNamedCompile")) - } } // SpongeCommon source sets @@ -141,6 +141,11 @@ val forgeMain by sourceSets.named("main") { configurations.named(implementationConfigurationName) { extendsFrom(gameLayerConfig.get()) } + + // The rest of the project because we want everything in the initial classpath + spongeImpl.addDependencyToRuntimeOnly(mixins.get(), this) + spongeImpl.addDependencyToRuntimeOnly(forgeMixins, this) + spongeImpl.addDependencyToRuntimeOnly(forgeLang, this) } configurations.configureEach { @@ -155,48 +160,8 @@ configurations.configureEach { } } -extensions.configure(LoomGradleExtensionAPI::class) { - silentMojangMappingsLicense() - accessWidenerPath.set(file("../src/main/resources/common.accesswidener")) - - mixin { - useLegacyMixinAp.set(false) - } - - forge { - useCustomMixin.set(false) - } - - mods { - named("main") { - sourceSet(forgeMixins) - sourceSet(forgeAccessors) - sourceSet(forgeLaunch) - - sourceSet(main.get(), commonProject) - sourceSet(mixins.get(), commonProject) - sourceSet(accessors.get(), commonProject) - sourceSet(launch.get(), commonProject) - - configuration(gameManagedLibrariesConfig.get()) - configuration(gameShadedLibrariesConfig.get()) - } - } - - // Arch-loom bug, skip broken union-relauncher - runs.forEach { - it.mainClass.set("net.minecraftforge.bootstrap.ForgeBootstrap") - } -} - dependencies { - "minecraft"("com.mojang:minecraft:${minecraftVersion}") - "forge"("net.minecraftforge:forge:$minecraftVersion-$forgeVersion") - "mappings"(loom.layered { - officialMojangMappings { - nameSyntheticMembers = true - } - }) + "minecraft"("net.minecraftforge:forge:$minecraftVersion-$forgeVersion") val service = serviceLibrariesConfig.name service(apiLibs.pluginSpi) @@ -231,13 +196,57 @@ dependencies { gameShadedLibraries("org.spongepowered:spongeapi:$apiVersion") { isTransitive = false } afterEvaluate { - spongeImpl.copyModulesExcludingProvided(serviceLibrariesConfig.get(), configurations.getByName("forgeDependencies"), serviceShadedLibrariesConfig.get()) + spongeImpl.copyModulesExcludingProvided(serviceLibrariesConfig.get(), bootLayerConfig.get(), serviceShadedLibrariesConfig.get()) spongeImpl.copyModulesExcludingProvided(gameLibrariesConfig.get(), serviceLayerConfig.get(), gameManagedLibrariesConfig.get()) } - val runTaskOnly = runTaskOnlyConfig.name - // Arch-loom bug, fix support of MOD_CLASSES - runTaskOnly("net.minecraftforge:bootstrap-dev:2.1.3") + runtimeOnly(project(bootstrapDevProject.path)) + testPluginsProject?.also { + runtimeOnly(project(it.path)) + } +} + +val awFiles: Set = files(main.get().resources, forgeMain.resources).filter { it.name.endsWith(".accesswidener") }.files +val atFile = project.layout.buildDirectory.file("generated/resources/at.cfg").get().asFile +AWToAT.convert(awFiles, atFile) + +val mixinConfigs: MutableSet = spongeImpl.mixinConfigurations + +extensions.configure(UserDevExtension::class) { + mappings("official", "1.21.3") + accessTransformers.from(atFile) + reobf = false + + runs { + configureEach { + ideaModule("Sponge.SpongeForge.main") + + // property("forge.logging.console.level", "debug") + // jvmArgs("-Dbsl.debug=true") // Uncomment to debug bootstrap classpath + + args(mixinConfigs.flatMap { sequenceOf("--mixin.config", it) }) + environment("MOD_CLASSES", "nop") + } + + create("client") + + create("server") { + args("--nogui") + } + } +} + +afterEvaluate { + extensions.configure(UserDevExtension::class) { + // Configure bootstrap-dev + val bootFileNames = spongeImpl.buildRuntimeFileNames(serviceLayerConfig.get()) // service in boot during dev + val gameShadedFileNames = spongeImpl.buildRuntimeFileNames(gameShadedLibrariesConfig.get()) + runs.configureEach { + jvmArgs("-Dsponge.dev.root=" + project.rootDir) + jvmArgs("-Dsponge.dev.boot=$bootFileNames") + jvmArgs("-Dsponge.dev.gameShaded=$gameShadedFileNames") + } + } } val forgeManifest = java.manifest { @@ -254,8 +263,6 @@ val forgeManifest = java.manifest { System.getenv()["GIT_BRANCH"]?.apply { attributes("Git-Branch" to this) } } -val mixinConfigs: MutableSet = spongeImpl.mixinConfigurations - tasks { jar { manifest.from(forgeManifest) @@ -292,52 +299,6 @@ tasks { from(forgeLang.output) } - val forgeServicesJar by registering(Jar::class) { - archiveClassifier.set("services") - - manifest { - from(forgeManifest) - attributes("Automatic-Module-Name" to "spongeforge.services") - } - - from(commonProject.sourceSets.named("applaunch").map { it.output }) - from(forgeAppLaunch.output) - - duplicatesStrategy = DuplicatesStrategy.WARN - } - - afterEvaluate { - withType(net.fabricmc.loom.task.AbstractRunTask::class) { - // Default classpath is a mess, we better start a new one from scratch - classpath = files( - configurations.getByName("forgeRuntimeLibrary"), - forgeServicesJar, forgeLangJar, runTaskOnlyConfig - ) - - testPluginsProject?.also { - val testPluginsOutput = it.sourceSets.getByName("main").output - val dirs: MutableList = mutableListOf() - dirs.add(testPluginsOutput.resourcesDir!!) - dirs.addAll(testPluginsOutput.classesDirs) - environment["SPONGE_PLUGINS"] = dirs.joinToString("&") - - dependsOn(it.tasks.classes) - } - - argumentProviders += CommandLineArgumentProvider { - mixinConfigs.asSequence() - .flatMap { sequenceOf("--mixin.config", it) } - .toList() - } - - // jvmArguments.add("-Dbsl.debug=true") // Uncomment to debug bootstrap classpath - - sourceSets.forEach { - dependsOn(it.classesTaskName) - } - } - } - val installerResources = project.layout.buildDirectory.dir("generated/resources/installer") forgeAppLaunch.resources.srcDir(installerResources) diff --git a/forge/gradle.properties b/forge/gradle.properties index acc83580cf4..f3f921aae37 100644 --- a/forge/gradle.properties +++ b/forge/gradle.properties @@ -2,7 +2,7 @@ name=SpongeForge implementation=Forge description=The SpongeAPI implementation for MinecraftForge -forgeVersion=52.0.3 +forgeVersion=53.0.25 loom.platform=forge fabric.loom.dontRemap=true mixinConfigs=mixins.spongeforge.accessors.json,mixins.spongeforge.api.json,mixins.spongeforge.inventory.json,mixins.spongeforge.inventory.shared.json,mixins.spongeforge.core.json,mixins.spongeforge.core.shared.json,mixins.spongeforge.tracker.json \ No newline at end of file diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/AbstractModProvider.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/AbstractModProvider.java new file mode 100644 index 00000000000..5b71dde2099 --- /dev/null +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/AbstractModProvider.java @@ -0,0 +1,80 @@ +/* + * 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.forge.applaunch.loading.moddiscovery; + +import cpw.mods.jarhandling.SecureJar; +import net.minecraftforge.forgespi.locating.IModFile; +import net.minecraftforge.forgespi.locating.IModProvider; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.stream.Stream; + +public abstract class AbstractModProvider implements IModProvider { + private static final Logger LOGGER = LogManager.getLogger(); + + @Override + public boolean isValid(final IModFile modFile) { + return true; + } + + @Override + public void initArguments(final Map arguments) { + } + + @Override + public void scanFile(final IModFile file, final Consumer pathConsumer) { + LOGGER.debug("Scan started: {}", file); + final SecureJar jar = file.getSecureJar(); + + Consumer consumer = pathConsumer; + final AtomicReference minStatus = new AtomicReference<>(SecureJar.Status.NONE); + if (jar.hasSecurityData()) { + minStatus.set(SecureJar.Status.VERIFIED); + consumer = path -> { + pathConsumer.accept(path); + SecureJar.Status status = jar.verifyPath(path); + if (status.ordinal() < minStatus.get().ordinal()) + minStatus.set(status); + }; + } + + try (final Stream files = Files.walk(jar.getRootPath())) { + files.filter(p -> p.toString().endsWith(".class")).forEach(consumer); + } catch (IOException e) { + LOGGER.debug("Scan failed: {}", file); + minStatus.set(SecureJar.Status.NONE); + } + + file.setSecurityStatus(minStatus.get()); + LOGGER.debug("Scan finished: {}", file); + } +} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/PluginFileParser.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/PluginFileParser.java index 9102910689f..f63dfb7c498 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/PluginFileParser.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/PluginFileParser.java @@ -24,14 +24,16 @@ */ package org.spongepowered.forge.applaunch.loading.moddiscovery; +import cpw.mods.jarhandling.JarMetadata; import cpw.mods.jarhandling.SecureJar; +import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider; import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; +import net.minecraftforge.fml.loading.moddiscovery.ModFileParser; import net.minecraftforge.fml.loading.moddiscovery.ModJarMetadata; import net.minecraftforge.forgespi.language.IModFileInfo; import net.minecraftforge.forgespi.locating.IModFile; -import net.minecraftforge.forgespi.locating.IModLocator; -import net.minecraftforge.forgespi.locating.ModFileFactory; +import net.minecraftforge.forgespi.locating.IModProvider; import org.spongepowered.common.applaunch.AppLaunch; import org.spongepowered.common.applaunch.metadata.PluginMetadataFixer; import org.spongepowered.common.applaunch.plugin.PluginPlatformConstants; @@ -46,7 +48,11 @@ import java.nio.file.Path; import java.util.List; +@SuppressWarnings("UnstableApiUsage") public final class PluginFileParser { + private static final String MODS_TOML = "META-INF/mods.toml"; + private static final String MODULE_INFO = "module-info.class"; + private static Constructor modJarMetadataConstructor; static { @@ -58,7 +64,7 @@ public final class PluginFileParser { } } - private static IModFileInfo parsePluginMetadata(final IModFile iModFile) { + private static IModFileInfo parsePluginFileInfo(final IModFile iModFile) { final ModFile modFile = (ModFile) iModFile; AppLaunch.logger().debug("Considering plugin file candidate {}", modFile.getFilePath()); @@ -82,6 +88,14 @@ private static IModFileInfo parsePluginMetadata(final IModFile iModFile) { } } + private static IModFileInfo parseModFileInfo(final IModFile iModFile) { + return ModFileParser.modsTomlParser(iModFile); + } + + private static IModFileInfo parseLibraryFileInfo(final IModFile iModFile) { + return DummyModProvider.INSTANCE.manifestParser(iModFile); + } + private static ModJarMetadata newModJarMetadata() { try { return modJarMetadataConstructor.newInstance(); @@ -90,13 +104,56 @@ private static ModJarMetadata newModJarMetadata() { } } - public static ModFile newPluginInstance(final IModLocator locator, final Path... path) { - ModJarMetadata mjm = newModJarMetadata(); - ModFile modFile = (ModFile) ModFileFactory.FACTORY.build(SecureJar.from(jar -> mjm, path), locator, PluginFileParser::parsePluginMetadata); - mjm.setModFile(modFile); - return modFile; + private static boolean useModJarMetadata(final SecureJar jar) { + final SecureJar.ModuleDataProvider data = jar.moduleDataProvider(); + if (data.findFile(PluginFileParser.MODULE_INFO).isPresent()) { + return false; + } + return data.findFile(PluginFileParser.MODS_TOML).isPresent() || data.findFile(PluginPlatformConstants.METADATA_FILE_LOCATION).isPresent(); + } + + public static ModFile newModFile(final IModProvider provider, boolean allowUnknown, final Path... paths) { + final ModJarMetadata mjm = newModJarMetadata(); + final SecureJar jar = SecureJar.from(j -> useModJarMetadata(j) ? mjm : JarMetadata.from(j, paths), paths); + + final SecureJar.ModuleDataProvider data = jar.moduleDataProvider(); + final String type = data.getManifest().getMainAttributes().getValue("FMLModType"); + + if (data.findFile(PluginFileParser.MODS_TOML).isPresent()) { + ModFile modFile = new ModFile(jar, provider, PluginFileParser::parseModFileInfo, type == null ? "MOD" : type); + mjm.setModFile(modFile); + return modFile; + } + + if (data.findFile(PluginPlatformConstants.METADATA_FILE_LOCATION).isPresent()) { + ModFile modFile = new ModFile(jar, provider, PluginFileParser::parsePluginFileInfo, type == null ? "MOD" : type); + mjm.setModFile(modFile); + return modFile; + } + + if (!allowUnknown && type == null) { + throw new IllegalArgumentException("Unknown mod file type"); + } + + return new ModFile(jar, provider, PluginFileParser::parseLibraryFileInfo, type == null ? "GAMELIBRARY" : type); + } + + public static ModFile newLibraryFile(final IModProvider provider, final Path... paths) { + final SecureJar jar = SecureJar.from(paths); + return new ModFile(jar, provider, PluginFileParser::parseLibraryFileInfo, "GAMELIBRARY"); } - private PluginFileParser() { + private static class DummyModProvider extends AbstractModProvider { + private static final DummyModProvider INSTANCE = new DummyModProvider(); + + @Override + public String name() { + return "dummy"; + } + + @Override + public IModFileInfo manifestParser(IModFile mod) { + return super.manifestParser(mod); + } } } diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeDependencyLocator.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeDependencyLocator.java index b73e593f3c6..4fa07561fdd 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeDependencyLocator.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeDependencyLocator.java @@ -27,10 +27,8 @@ import cpw.mods.modlauncher.Environment; import cpw.mods.modlauncher.Launcher; import net.minecraftforge.fml.loading.FMLEnvironment; -import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider; import net.minecraftforge.forgespi.locating.IDependencyLocator; import net.minecraftforge.forgespi.locating.IModFile; -import net.minecraftforge.forgespi.locating.IModLocator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.spongepowered.forge.applaunch.loading.moddiscovery.library.Log4JLogger; @@ -42,14 +40,13 @@ import java.util.List; import java.util.Map; -// works with ForgeProductionBootstrap to make this whole thing go public class SpongeForgeDependencyLocator extends AbstractModProvider implements IDependencyLocator { private static final Logger LOGGER = LogManager.getLogger(); private LibraryManager libraryManager; @Override - public List scanMods(Iterable loadedMods) { + public List scanMods(final Iterable loadedMods) { final List modFiles = new ArrayList<>(); // Add Sponge-specific libraries @@ -64,23 +61,13 @@ public List scanMods(Iterable loadedMods) { for (final LibraryManager.Library library : this.libraryManager.getAll("main")) { final Path path = library.file(); SpongeForgeDependencyLocator.LOGGER.debug("Proposing jar {} as a game library", path); - - final IModLocator.ModFileOrException fileOrException = createMod(path); - if (fileOrException.ex() != null) { - throw fileOrException.ex(); - } - modFiles.add(fileOrException.file()); + modFiles.add(PluginFileParser.newLibraryFile(this, path)); } } return modFiles; } - @Override - protected String getDefaultJarModType() { - return IModFile.Type.GAMELIBRARY.name(); - } - @Override public String name() { return "spongeforge"; diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeModLocator.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeModLocator.java index e7c704b506b..841f97069a7 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeModLocator.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeModLocator.java @@ -28,20 +28,23 @@ import cpw.mods.modlauncher.Environment; import cpw.mods.modlauncher.Launcher; import net.minecraftforge.fml.loading.FMLEnvironment; -import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider; import net.minecraftforge.forgespi.locating.IModLocator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.spongepowered.forge.applaunch.plugin.ForgePluginPlatform; +import java.io.File; import java.net.URI; import java.net.URL; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.stream.Stream; public final class SpongeForgeModLocator extends AbstractModProvider implements IModLocator { private static final Logger LOGGER = LogManager.getLogger(); @@ -49,7 +52,19 @@ public final class SpongeForgeModLocator extends AbstractModProvider implements @Override public List scanMods() { if (!FMLEnvironment.production) { - return List.of(); + final List resources = new ArrayList<>(); + final String resourcesProp = System.getProperty("sponge.resources"); + if (resourcesProp != null) { + for (final String entry : resourcesProp.split(File.pathSeparator)) { + if (entry.isBlank()) { + continue; + } + + final Path[] paths = Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new); + resources.add(new ModFileOrException(PluginFileParser.newModFile(this, true, paths), null)); + } + } + return resources; } try { @@ -66,7 +81,7 @@ public List scanMods() { } catch (Exception e) { throw new RuntimeException(e); } - }).map(this::createMod).toList(); + }).map((path -> new ModFileOrException(PluginFileParser.newModFile(this, false, path), null))).toList(); } catch (Exception e) { LOGGER.error("Failed to scan mod candidates", 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 e2903123f0d..a76911d39dd 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 @@ -24,15 +24,14 @@ */ package org.spongepowered.forge.applaunch.loading.moddiscovery.locator; -import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider; import net.minecraftforge.forgespi.locating.IModLocator; +import org.spongepowered.forge.applaunch.loading.moddiscovery.AbstractModProvider; import org.spongepowered.forge.applaunch.loading.moddiscovery.PluginFileParser; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.stream.Stream; public final class EnvironmentPluginLocator extends AbstractModProvider implements IModLocator { @@ -41,25 +40,16 @@ public final class EnvironmentPluginLocator extends AbstractModProvider implemen public List scanMods() { final List modFiles = new ArrayList<>(); for (final Path[] paths : EnvironmentPluginLocator.getPluginsPaths()) { - modFiles.add(new ModFileOrException(PluginFileParser.newPluginInstance(this, paths), null)); + modFiles.add(new ModFileOrException(PluginFileParser.newModFile(this, false, paths), null)); } return modFiles; } - @Override - protected ModFileOrException createMod(Path path) { - return new ModFileOrException(PluginFileParser.newPluginInstance(this, path), null); - } - @Override public String name() { return "environment plugin"; } - @Override - public void initArguments(final Map arguments) { - } - private static List getPluginsPaths() { final String env = System.getenv("SPONGE_PLUGINS"); if (env == null) { diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/PluginsDirectoryLocator.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/PluginsDirectoryLocator.java index 4202f337b93..e0ed9845745 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/PluginsDirectoryLocator.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/PluginsDirectoryLocator.java @@ -27,12 +27,12 @@ import cpw.mods.modlauncher.api.LamdbaExceptionUtils; import net.minecraftforge.fml.loading.ModDirTransformerDiscoverer; import net.minecraftforge.fml.loading.StringUtils; -import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider; import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.forgespi.locating.IModLocator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.spongepowered.common.applaunch.AppLaunch; +import org.spongepowered.forge.applaunch.loading.moddiscovery.AbstractModProvider; import org.spongepowered.forge.applaunch.loading.moddiscovery.PluginFileParser; import java.nio.file.Files; @@ -40,7 +40,6 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.stream.Stream; public final class PluginsDirectoryLocator extends AbstractModProvider implements IModLocator { @@ -60,12 +59,13 @@ public List scanMods() { return modFiles; } + @SuppressWarnings("UnstableApiUsage") private Stream scanForPluginsIn(final Path pluginsDirectory) { final List excluded = ModDirTransformerDiscoverer.allExcluded(); return LamdbaExceptionUtils.uncheck(() -> Files.list(pluginsDirectory)) .filter((p) -> !excluded.contains(p) && StringUtils.toLowerCase(p.getFileName().toString()).endsWith(".jar")) .sorted(Comparator.comparing((path) -> StringUtils.toLowerCase(path.getFileName().toString()))) - .map((p) -> PluginFileParser.newPluginInstance(this, p)) + .map((p) -> PluginFileParser.newModFile(this, false, p)) .filter(ModFile::identifyMods); } @@ -73,8 +73,4 @@ private Stream scanForPluginsIn(final Path pluginsDirectory) { public String name() { return "plugins directory"; } - - @Override - public void initArguments(final Map arguments) { - } } diff --git a/forge/src/applaunch/resources/META-INF/MANIFEST.MF b/forge/src/applaunch/resources/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..c54a634b615 --- /dev/null +++ b/forge/src/applaunch/resources/META-INF/MANIFEST.MF @@ -0,0 +1 @@ +Automatic-Module-Name: spongeforge.services diff --git a/forge/src/lang/resources/META-INF/MANIFEST.MF b/forge/src/lang/resources/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..8e85d7c5e2a --- /dev/null +++ b/forge/src/lang/resources/META-INF/MANIFEST.MF @@ -0,0 +1,2 @@ +Automatic-Module-Name: spongeforge.lang +FMLModType: LANGPROVIDER diff --git a/forge/src/main/java/org/spongepowered/forge/SpongeForgeMod.java b/forge/src/main/java/org/spongepowered/forge/SpongeForgeMod.java index 200740664e5..3cccd1d9797 100644 --- a/forge/src/main/java/org/spongepowered/forge/SpongeForgeMod.java +++ b/forge/src/main/java/org/spongepowered/forge/SpongeForgeMod.java @@ -25,6 +25,8 @@ package org.spongepowered.forge; import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; import net.minecraftforge.common.MinecraftForge; @@ -64,10 +66,10 @@ public final class SpongeForgeMod { private final Logger logger = LogManager.getLogger("spongeforge"); - public SpongeForgeMod() { + public SpongeForgeMod(FMLJavaModLoadingContext ctx) { // WorldPersistenceHooks.addHook(SpongeLevelDataPersistence.INSTANCE); // TODO SF 1.19.4 - final IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus(); + final IEventBus modBus = ctx.getModEventBus(); // modBus: add all FML events with it modBus.addListener(this::onCommonSetup); @@ -144,7 +146,7 @@ public void onRegister(RegisterEvent event) { .sized(0.6F, 1.8F) .clientTrackingRange(org.spongepowered.common.util.Constants.Entity.Player.TRACKING_RANGE) .updateInterval(2) - .build("sponge:human") + .build(ResourceKey.create(ForgeRegistries.Keys.ENTITY_TYPES, ResourceLocation.parse("sponge:human"))) ; event.register(ForgeRegistries.Keys.ENTITY_TYPES, helper -> helper.register(HumanEntity.KEY, SpongeEntityTypes.HUMAN)); diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/internal/BrandingControlMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/internal/BrandingControlMixin_Forge.java index 841f6ba78ea..c92ec5c3b54 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/internal/BrandingControlMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/internal/BrandingControlMixin_Forge.java @@ -36,16 +36,7 @@ public abstract class BrandingControlMixin_Forge { * @reason This is also not Kansas anymore */ @Overwrite - public static String getClientBranding() { - return "forge/spongeforge"; - } - - /** - * @author Zidane - * @reason What that said above - */ - @Overwrite - public static String getServerBranding() { + public static String getBranding() { return "forge/spongeforge"; } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java index b06f9cb2cce..25f11215d82 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java @@ -24,8 +24,10 @@ */ package org.spongepowered.forge.mixin.core.server.level; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; @@ -37,10 +39,13 @@ @Mixin(ServerPlayer.class) public abstract class ServerPlayerMixin_Forge extends LivingEntityMixin_Forge { + @Shadow public abstract ServerLevel shadow$serverLevel(); + // override from LivingEntityMixin_Forge @Override protected void forge$onElytraUse(final CallbackInfo ci) { - final PhaseContext context = PhaseTracker.SERVER.getPhaseContext(); + final ServerLevel level = this.shadow$serverLevel(); + final PhaseContext context = PhaseTracker.getWorldInstance(level).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); final net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer) (Object) this; try (final EffectTransactor ignored = transactor.logPlayerInventoryChangeWithEffect(player, PlayerInventoryTransaction.EventCreator.STANDARD)) { diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Forge.java index f8e9a9a30af..f3c363bcbec 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Forge.java @@ -25,6 +25,8 @@ package org.spongepowered.forge.mixin.core.server.network; import net.minecraft.network.protocol.game.ServerGamePacketListener; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.world.inventory.RecipeBookMenu; import net.minecraft.world.item.crafting.RecipeHolder; @@ -33,6 +35,7 @@ import org.spongepowered.api.item.inventory.crafting.CraftingInventory; import org.spongepowered.api.item.inventory.query.QueryTypes; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.SpongeCommon; @@ -44,23 +47,29 @@ @Mixin(ServerGamePacketListenerImpl.class) public abstract class ServerGamePacketListenerImplMixin_Forge implements ServerGamePacketListener { - @SuppressWarnings({"unchecked", "rawtypes"}) - @Redirect(method = "lambda$handlePlaceRecipe$10", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/RecipeBookMenu;handlePlacement(ZLnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/server/level/ServerPlayer;)V")) - private void forge$onPlaceRecipe(final RecipeBookMenu recipeBookMenu, final boolean shift, final RecipeHolder recipe, final net.minecraft.server.level.ServerPlayer player) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + @Shadow public ServerPlayer player; + + @Redirect(method = "handlePlaceRecipe", + at = @At(value = "INVOKE", target = "net/minecraft/world/inventory/RecipeBookMenu.handlePlacement(ZZLnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/player/Inventory;)Lnet/minecraft/world/inventory/RecipeBookMenu$PostPlaceAction;")) + private RecipeBookMenu.PostPlaceAction forge$onPlaceRecipe( + final RecipeBookMenu recipeBookMenu, final boolean shift, final boolean isCreative, final RecipeHolder recipe, + final ServerLevel serverLevel, final net.minecraft.world.entity.player.Inventory inventory) { + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.serverLevel()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); + final var player = this.player; final Inventory craftInv = ((Inventory) player.containerMenu).query(QueryTypes.INVENTORY_TYPE.get().of(CraftingInventory.class)); + final RecipeBookMenu.PostPlaceAction result; if (!(craftInv instanceof CraftingInventory)) { - recipeBookMenu.handlePlacement(shift, recipe, player); + result = recipeBookMenu.handlePlacement(shift,isCreative, recipe, serverLevel, inventory); SpongeCommon.logger().warn("Detected crafting without a InventoryCrafting!? Crafting Event will not fire."); - return; + return result; } try (final EffectTransactor ignored = transactor.logPlaceRecipe(shift, recipe, player, (CraftingInventory) craftInv)) { - recipeBookMenu.handlePlacement(shift, recipe, player); + result = recipeBookMenu.handlePlacement(shift,isCreative, recipe, serverLevel, inventory); player.containerMenu.broadcastChanges(); } + return result; } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java index 050c899e5cc..a91a34fa01e 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java @@ -34,12 +34,12 @@ public abstract class LivingEntityMixin_Forge { @Inject( - method = "updateFallFlying", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/item/ItemStack;elytraFlightTick(Lnet/minecraft/world/entity/LivingEntity;I)Z", - shift = At.Shift.AFTER - ) + method = "updateFallFlying", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/item/ItemStack;hurtAndBreak(ILnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/entity/EquipmentSlot;)V", + shift = At.Shift.AFTER + ) ) protected void forge$onElytraUse(final CallbackInfo ci) { } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/vehicle/BoatMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/vehicle/BoatMixin_Forge.java index f00eb8a1c45..e52b4a69c3c 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/vehicle/BoatMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/vehicle/BoatMixin_Forge.java @@ -26,21 +26,21 @@ import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.entity.vehicle.AbstractBoat; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.bridge.world.entity.vehicle.BoatBridge; +import org.spongepowered.common.bridge.world.entity.vehicle.AbstractBoatBridge; -@Mixin(Boat.class) -public abstract class BoatMixin_Forge implements BoatBridge { +@Mixin(AbstractBoat.class) +public abstract class BoatMixin_Forge implements AbstractBoatBridge { @Redirect(method = "getGroundFriction", at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/level/block/state/BlockState;getFriction(Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/Entity;)F") + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/state/BlockState;getFriction(Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/Entity;)F") ) private float forge$getBlockFrictionIfBoatIsNotOverridingMovingOnLand(BlockState caller, LevelReader level, BlockPos pos, Entity entity) { final boolean movesOnLand = this.bridge$getMoveOnLand(); diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java index 34cab814694..5a8394cd809 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java @@ -27,10 +27,11 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; import net.minecraft.core.RegistryAccess; +import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeHolder; -import net.minecraft.world.level.Level; +import net.minecraft.world.item.crafting.SingleRecipeInput; import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; import net.minecraft.world.level.block.state.BlockState; import org.objectweb.asm.Opcodes; @@ -69,7 +70,7 @@ public abstract class AbstractFurnaceBlockEntityMixin_Forge implements AbstractF // @formatter:off @Shadow protected NonNullList items; @Shadow int cookingProgress; - @Shadow private boolean shadow$canBurn(RegistryAccess registryAccess, @Nullable RecipeHolder recipe, NonNullList slots, int maxStackSize) { + @Shadow private boolean shadow$canBurn(RegistryAccess registryAccess, @Nullable RecipeHolder recipe, SingleRecipeInput input, NonNullList slots, int maxStackSize) { throw new UnsupportedOperationException("Shadowed canBurn"); } // @formatter:on @@ -79,15 +80,16 @@ public abstract class AbstractFurnaceBlockEntityMixin_Forge implements AbstractF // Tick up and Start @Redirect(method = "serverTick", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;canBurn(Lnet/minecraft/core/RegistryAccess;Lnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/core/NonNullList;I)Z", + target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;canBurn(Lnet/minecraft/core/RegistryAccess;Lnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/world/item/crafting/SingleRecipeInput;Lnet/minecraft/core/NonNullList;I)Z", ordinal = 1)) private static boolean forge$checkIfCanSmelt(final AbstractFurnaceBlockEntity entityIn, - final RegistryAccess registryAccess, - final RecipeHolder recipe, - final NonNullList slots, - final int maxStackSize) { + final RegistryAccess registryAccess, + final RecipeHolder recipe, + final SingleRecipeInput input, + final NonNullList slots, + final int maxStackSize) { final var entity = (AbstractFurnaceBlockEntityMixin_Forge) (Object) (entityIn); - if (!entity.shadow$canBurn(registryAccess, recipe, slots, maxStackSize)) { + if (!entity.shadow$canBurn(registryAccess, recipe, input, slots, maxStackSize)) { return false; } @@ -111,7 +113,7 @@ public abstract class AbstractFurnaceBlockEntityMixin_Forge implements AbstractF // Tick down @Redirect(method = "serverTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;clamp(III)I")) private static int forge$resetCookTimeIfCancelled(final int newCookTime, final int zero, final int totalCookTime, - final Level level, final BlockPos entityPos, final BlockState state, final AbstractFurnaceBlockEntity entityIn) { + final ServerLevel level, final BlockPos entityPos, final BlockState state, final AbstractFurnaceBlockEntity entityIn) { final int clampedCookTime = Mth.clamp(newCookTime, zero, totalCookTime); final var entity = (AbstractFurnaceBlockEntityMixin_Forge) (Object) entityIn; final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(entity.items.get(1)); @@ -133,7 +135,7 @@ public abstract class AbstractFurnaceBlockEntityMixin_Forge implements AbstractF slice = @Slice( from = @At(value = "FIELD", target = "Lnet/minecraft/world/level/block/Blocks;WET_SPONGE:Lnet/minecraft/world/level/block/Block;", opcode = Opcodes.GETSTATIC) )) - private void forge$captureBucketFill(final RegistryAccess registryAccess, final RecipeHolder recipe, final NonNullList slots, final int maxStackSize, final CallbackInfoReturnable cir) { + private void forge$captureBucketFill(final RegistryAccess registryAccess, final RecipeHolder recipe, final SingleRecipeInput input, final NonNullList slots, final int maxStackSize, final CallbackInfoReturnable cir) { this.forge$filledWaterBucket = true; } @@ -142,7 +144,7 @@ public abstract class AbstractFurnaceBlockEntityMixin_Forge implements AbstractF locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;shrink(I)V")) private void forge$afterSmeltItem( - final RegistryAccess registryAccess, final RecipeHolder recipe, + final RegistryAccess registryAccess, final RecipeHolder recipe, final SingleRecipeInput input, final NonNullList slots, final int maxStackSize, final CallbackInfoReturnable cir, final ItemStack itemIn, final ItemStack recipeResult, final ItemStack itemOut diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java index b3f8c60f891..0b0b2dd3e13 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java @@ -63,7 +63,7 @@ public abstract class ServerPlayerGameModeMixin_Forge_Tracker { final ItemStack itemStack, final Level level, final BlockState state, final BlockPos pos, final Player player ) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); // Log the prepare drops here because Forge rewrites // this method to call mine block before calling destroy block diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Forge_Tracker.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Forge_Tracker.java index 8784263a379..973df0af53f 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Forge_Tracker.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Forge_Tracker.java @@ -42,7 +42,7 @@ public abstract class BlockBehaviorMixin_Forge_Tracker { method = "getLootTable", at = @At( value = "FIELD", - target = "Lnet/minecraft/world/level/block/state/BlockBehaviour;lootTableSupplier:Ljava/util/function/Supplier;", + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour;drops:Ljava/util/Optional;", remap = false ) ) diff --git a/generator/build.gradle.kts b/generator/build.gradle.kts index 6481b69c15c..da7ca6bedc2 100644 --- a/generator/build.gradle.kts +++ b/generator/build.gradle.kts @@ -5,12 +5,7 @@ plugins { description = "Code generator for automatically producing API catalog classes based off of Vanilla MC data" minecraft { - rootProject.sourceSets["main"].resources - .filter { it.name.endsWith(".accesswidener") } - .files - .forEach { - accessWideners(it) - } + accessWideners(rootProject.sourceSets["main"].resources.filter { it.name.endsWith(".accesswidener") }) } configurations.configureEach { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 867ada7f2fe..d2eca4aaf9b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,8 +8,8 @@ asm = "9.7" log4j = "2.22.1" forgeAutoRenamingTool = "1.0.6" mixin = "0.8.7" -bootstrap = "2.1.1" -modlauncher = "10.2.1" +bootstrap = "2.1.3" +modlauncher = "10.2.2" neo-modlauncher = "11.0.3" securemodules = "2.2.20" jarjar = "0.3.26" @@ -68,6 +68,7 @@ terminalConsoleAppender = { module = "net.minecrell:terminalconsoleappender", ve # buildtime-only vineflower = { module = "org.vineflower:vineflower", version.ref = "vineflower" } +at = { module = "dev.architectury:at", version = "1.0.1" } [plugins] blossom = { id = "net.kyori.blossom", version = "2.1.0" } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 4046353e1be..2cf4b8bf80a 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -6,6 +6,7 @@ + @@ -168,6 +169,9 @@ + + + @@ -208,6 +212,9 @@ + + + @@ -1523,6 +1530,14 @@ + + + + + + + + @@ -1699,6 +1714,14 @@ + + + + + + + + @@ -1707,6 +1730,14 @@ + + + + + + + + @@ -2038,6 +2069,9 @@ + + + @@ -2253,6 +2287,14 @@ + + + + + + + + @@ -2765,6 +2807,14 @@ + + + + + + + + @@ -2803,18 +2853,40 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -2839,10 +2911,21 @@ + + + + + + + + + + + @@ -2882,6 +2965,9 @@ + + + @@ -2917,6 +3003,9 @@ + + + @@ -2964,6 +3053,17 @@ + + + + + + + + + + + @@ -2984,6 +3084,9 @@ + + + @@ -3036,6 +3139,25 @@ + + + + + + + + + + + + + + + + + + + @@ -3084,6 +3206,25 @@ + + + + + + + + + + + + + + + + + + + @@ -3132,6 +3273,25 @@ + + + + + + + + + + + + + + + + + + + @@ -3216,6 +3376,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3232,6 +3428,17 @@ + + + + + + + + + + + @@ -3288,6 +3495,25 @@ + + + + + + + + + + + + + + + + + + + @@ -3336,6 +3562,25 @@ + + + + + + + + + + + + + + + + + + + @@ -3384,6 +3629,25 @@ + + + + + + + + + + + + + + + + + + + @@ -3430,6 +3694,17 @@ + + + + + + + + + + + @@ -3461,6 +3736,9 @@ + + + @@ -3510,6 +3788,14 @@ + + + + + + + + @@ -3521,6 +3807,19 @@ + + + + + + + + + + + + + @@ -4032,6 +4331,14 @@ + + + + + + + + @@ -4047,6 +4354,11 @@ + + + + + @@ -4154,11 +4466,24 @@ + + + + + + + + + + + + + @@ -4604,6 +4929,11 @@ + + + + + @@ -4628,6 +4958,14 @@ + + + + + + + + @@ -4651,6 +4989,11 @@ + + + + + @@ -4813,6 +5156,9 @@ + + + @@ -5178,6 +5524,14 @@ + + + + + + + + diff --git a/library-manager/build.gradle.kts b/library-manager/build.gradle.kts index e2a23945059..11fc683e50f 100644 --- a/library-manager/build.gradle.kts +++ b/library-manager/build.gradle.kts @@ -1,3 +1,11 @@ dependencies { implementation(apiLibs.gson) } + +tasks { + jar { + manifest { + attributes("Automatic-Module-Name" to "sponge.library_manager") + } + } +} diff --git a/library-manager/src/main/resources/META-INF/MANIFEST.MF b/library-manager/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..68336aaf212 --- /dev/null +++ b/library-manager/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1 @@ +Automatic-Module-Name: sponge.library_manager diff --git a/modlauncher-transformers/build.gradle.kts b/modlauncher-transformers/build.gradle.kts index 390fab762bd..ab8303b3673 100644 --- a/modlauncher-transformers/build.gradle.kts +++ b/modlauncher-transformers/build.gradle.kts @@ -31,7 +31,7 @@ dependencies { tasks { jar { manifest { - attributes("Automatic-Module-Name" to "sponge.modlauncher.transformers") + attributes("Automatic-Module-Name" to "sponge.modlauncher_transformers") } } } diff --git a/modlauncher-transformers/src/main/resources/META-INF/MANIFEST.MF b/modlauncher-transformers/src/main/resources/META-INF/MANIFEST.MF index 0a1e1f7343d..8e5fb5b07e5 100644 --- a/modlauncher-transformers/src/main/resources/META-INF/MANIFEST.MF +++ b/modlauncher-transformers/src/main/resources/META-INF/MANIFEST.MF @@ -1 +1 @@ -Automatic-Module-Name: sponge.modlauncher.transformers +Automatic-Module-Name: sponge.modlauncher_transformers diff --git a/neoforge/build.gradle.kts b/neoforge/build.gradle.kts index a7b86632259..96e95d67d69 100644 --- a/neoforge/build.gradle.kts +++ b/neoforge/build.gradle.kts @@ -14,7 +14,7 @@ plugins { alias(libs.plugins.shadow) id("implementation-structure") alias(libs.plugins.blossom) - id("dev.architectury.loom") version "1.6.411" + id("dev.architectury.loom") } val commonProject = parent!! @@ -312,7 +312,7 @@ tasks { .toList() } - jvmArguments.add("-Dbsl.debug=true") // Uncomment to debug bootstrap classpath + // jvmArguments.add("-Dbsl.debug=true") // Uncomment to debug bootstrap classpath sourceSets.forEach { dependsOn(it.classesTaskName) diff --git a/settings.gradle.kts b/settings.gradle.kts index 00d638da944..6202443c650 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -70,6 +70,7 @@ includeBuild("SpongeAPI") { } include("modlauncher-transformers") include("library-manager") +include("bootstrap-dev") include("generator") // Optional projects diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java index 8bce4b1a806..0835f4869de 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java @@ -43,7 +43,6 @@ import org.spongepowered.asm.mixin.injection.*; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.world.entity.LivingEntityBridge; import org.spongepowered.common.event.tracking.PhaseTracker; @@ -170,14 +169,10 @@ public abstract class LivingEntityMixin_Attack_Impl extends EntityMixin implemen * After calling #actuallyHurt (even when invulnerable), if cancelled return early or is still invulnerable * and reset {@link #lastHurt} and {@link #invulnerableTime} */ - @Inject(method = "hurtServer", locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true, + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 0, target = "Lnet/minecraft/world/entity/LivingEntity;actuallyHurt(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;F)V")) - private void attackImpl$afterActuallyHurt1( - final ServerLevel level, final DamageSource source, final float damageTaken, - final CallbackInfoReturnable cir, final float dealtDamage, final boolean isBlocked, - float $$5, boolean wasHurt - ) { + private void attackImpl$afterActuallyHurt1(final ServerLevel level, final DamageSource source, final float damageTaken, final CallbackInfoReturnable cir) { if (this.attackImpl$actuallyHurtCancelled || damageTaken <= this.lastHurt) { this.invulnerableTime = this.attackImpl$InvulnerableTime; this.lastHurt = this.attackImpl$lastHurt; @@ -189,7 +184,7 @@ public abstract class LivingEntityMixin_Attack_Impl extends EntityMixin implemen * After calling #actuallyHurt, if cancelled return early * Also reset values */ - @Inject(method = "hurtServer",cancellable = true, + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 1, target = "Lnet/minecraft/world/entity/LivingEntity;actuallyHurt(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;F)V")) private void attackImpl$afterActuallyHurt2(ServerLevel $$0, DamageSource $$1, float $$2, CallbackInfoReturnable cir) { diff --git a/vanilla/build.gradle.kts b/vanilla/build.gradle.kts index 3595ab06aab..28dd846d589 100644 --- a/vanilla/build.gradle.kts +++ b/vanilla/build.gradle.kts @@ -9,6 +9,7 @@ plugins { } val commonProject = parent!! +val bootstrapDevProject = commonProject.project(":bootstrap-dev") val transformersProject = commonProject.project(":modlauncher-transformers") val libraryManagerProject = commonProject.project(":library-manager") val testPluginsProject: Project? = rootProject.subprojects.find { "testplugins" == it.name } @@ -23,7 +24,6 @@ description = "The SpongeAPI implementation for Vanilla Minecraft" version = spongeImpl.generatePlatformBuildVersionString(apiVersion, minecraftVersion, recommendedVersion) // SpongeVanilla libraries -val devlaunchLibrariesConfig: NamedDomainObjectProvider = configurations.register("devlaunchLibraries") val installerLibrariesConfig: NamedDomainObjectProvider = configurations.register("installerLibraries") val initLibrariesConfig: NamedDomainObjectProvider = configurations.register("initLibraries") // JVM initial classpath val bootLibrariesConfig: NamedDomainObjectProvider = configurations.register("bootLibraries") @@ -54,13 +54,6 @@ val mixins: NamedDomainObjectProvider = commonProject.sourceSets.name val main: NamedDomainObjectProvider = commonProject.sourceSets.named("main") // SpongeVanilla source sets -// Dev launch -val vanillaDevLaunch by sourceSets.register("devlaunch") { - configurations.named(implementationConfigurationName) { - extendsFrom(devlaunchLibrariesConfig.get()) - } -} - // Prod launch val vanillaInstaller by sourceSets.register("installer") { configurations.named(implementationConfigurationName) { @@ -126,27 +119,15 @@ val vanillaMain by sourceSets.named("main") { // The rest of the project because we want everything in the initial classpath spongeImpl.addDependencyToRuntimeOnly(mixins.get(), this) - spongeImpl.addDependencyToRuntimeOnly(vanillaDevLaunch, this) spongeImpl.addDependencyToRuntimeOnly(vanillaMixins, this) - - configurations.named(runtimeOnlyConfigurationName) { - extendsFrom(devlaunchLibrariesConfig.get()) - } } val superclassConfigs = spongeImpl.getNamedConfigurations("superClassChanges") val mixinConfigs = spongeImpl.mixinConfigurations minecraft { - main.get().resources - .filter { it.name.endsWith(".accesswidener") } - .files - .forEach { accessWideners(it) } - - vanillaMain.resources - .filter { it.name.endsWith(".accesswidener") } - .files - .forEach { accessWideners(it) } + accessWideners(main.get().resources.filter { it.name.endsWith(".accesswidener") }) + accessWideners(vanillaMain.resources.filter { it.name.endsWith(".accesswidener") }) } configurations.configureEach { @@ -157,9 +138,6 @@ configurations.configureEach { } dependencies { - val devlaunch = devlaunchLibrariesConfig.name - devlaunch(libs.bootstrap.api) - val installer = installerLibrariesConfig.name installer(apiLibs.gson) installer(platform(apiLibs.configurate.bom)) @@ -233,17 +211,8 @@ dependencies { exclude(group = "org.checkerframework", module = "checker-qual") } - // All minecraft deps except itself - configurations.minecraft.get().resolvedConfiguration.resolvedArtifacts - .map { - var id = it.id.componentIdentifier.toString() - if (it.classifier != null) { - id += ":" + it.classifier - } - id - } - .filter { !it.startsWith("net.minecraft:joined") } - .forEach { boot(it) { isTransitive = false } } + // All minecraft dependencies except itself + spongeImpl.copyModulesExcludingPrefix(configurations.minecraft.get(), "net.minecraft", "joined", bootLibrariesConfig.get()) boot(project(transformersProject.path)) { exclude(group = "cpw.mods", module = "modlauncher") @@ -271,9 +240,9 @@ dependencies { spongeImpl.copyModulesExcludingProvided(gameLibrariesConfig.get(), bootLayerConfig.get(), gameManagedLibrariesConfig.get()) - val runOnly = vanillaMain.runtimeOnlyConfigurationName + runtimeOnly(project(bootstrapDevProject.path)) testPluginsProject?.also { - runOnly(project(it.path)) + runtimeOnly(project(it.path)) } } @@ -295,6 +264,10 @@ minecraft { args("--launchTarget", "sponge_client_it") } + // Configure bootstrap-dev + val bootFileNames = spongeImpl.buildRuntimeFileNames(bootLayerConfig.get()) + val gameShadedFileNames = spongeImpl.buildRuntimeFileNames(gameShadedLibrariesConfig.get()) + configureEach { targetVersion(apiJavaTarget.toInt()) workingDirectory(project.file("run/")) @@ -330,8 +303,8 @@ minecraft { // Configure resources jvmArgs("-Dsponge.dev.root=" + project.rootDir) - jvmArgs("-Dsponge.dev.boot=" + bootLayerConfig.get().resolvedConfiguration.resolvedArtifacts.joinToString(";") { it.file.name }) - jvmArgs("-Dsponge.dev.gameShaded=" + gameShadedLibrariesConfig.get().resolvedConfiguration.resolvedArtifacts.joinToString(";") { it.file.name }) + jvmArgs("-Dsponge.dev.boot=$bootFileNames") + jvmArgs("-Dsponge.dev.gameShaded=$gameShadedFileNames") } } } 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/SpongePropertyLocatorService.java similarity index 93% rename from vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/locator/GameResourceLocatorService.java rename to vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/locator/SpongePropertyLocatorService.java index 0a1791fb459..19e5d2e8b4e 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/locator/GameResourceLocatorService.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/locator/SpongePropertyLocatorService.java @@ -28,6 +28,7 @@ import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; import org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResourceLocatorService; +import java.io.File; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -37,11 +38,11 @@ import java.util.Set; import java.util.stream.Stream; -public final class GameResourceLocatorService implements JVMPluginResourceLocatorService { +public final class SpongePropertyLocatorService implements JVMPluginResourceLocatorService { @Override public String name() { - return "game"; + return "sponge_resources"; } @Override @@ -49,9 +50,9 @@ public Set locatePluginResources(final Environment environmen environment.logger().info("Locating '{}' resources...", this.name()); final Set resources = new HashSet<>(); - final String resourcesProp = System.getProperty("sponge.gameResources"); + final String resourcesProp = System.getProperty("sponge.resources"); if (resourcesProp != null) { - for (final String entry : resourcesProp.split(";")) { + for (final String entry : resourcesProp.split(File.pathSeparator)) { if (entry.isBlank()) { continue; } 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 330fcb92be8..cf8cd29b6e7 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 +1 @@ -org.spongepowered.vanilla.applaunch.plugin.locator.GameResourceLocatorService \ No newline at end of file +org.spongepowered.vanilla.applaunch.plugin.locator.SpongePropertyLocatorService \ No newline at end of file diff --git a/vanilla/src/devlaunch/java/module-info.java b/vanilla/src/devlaunch/java/module-info.java deleted file mode 100644 index 3328ffb314e..00000000000 --- a/vanilla/src/devlaunch/java/module-info.java +++ /dev/null @@ -1,6 +0,0 @@ -module org.spongepowered.vanilla.devlaunch { - requires net.minecraftforge.bootstrap.api; - exports org.spongepowered.vanilla.devlaunch; - - provides net.minecraftforge.bootstrap.api.BootstrapClasspathModifier with org.spongepowered.vanilla.devlaunch.SpongeDevClasspathFixer; -} diff --git a/vanilla/src/devlaunch/resources/META-INF/services/net.minecraftforge.bootstrap.api.BootstrapClasspathModifier b/vanilla/src/devlaunch/resources/META-INF/services/net.minecraftforge.bootstrap.api.BootstrapClasspathModifier deleted file mode 100644 index 8351cd09f4e..00000000000 --- a/vanilla/src/devlaunch/resources/META-INF/services/net.minecraftforge.bootstrap.api.BootstrapClasspathModifier +++ /dev/null @@ -1 +0,0 @@ -org.spongepowered.vanilla.devlaunch.SpongeDevClasspathFixer 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 9c36b84dd5e..370e4c18036 100644 --- a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java +++ b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java @@ -42,6 +42,7 @@ import org.spongepowered.vanilla.installer.model.mojang.VersionManifest; import org.tinylog.Logger; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -68,6 +69,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.StringJoiner; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.jar.JarFile; @@ -177,12 +179,11 @@ public void downloadAndRun() throws Exception { } } - final StringBuilder gameLibsEnv = new StringBuilder(); + final StringJoiner resourcesEnv = new StringJoiner(File.pathSeparator); for (final Path lib : gameLibs) { - gameLibsEnv.append(lib.toAbsolutePath()).append(';'); + resourcesEnv.add(lib.toAbsolutePath().toString()); } - gameLibsEnv.setLength(gameLibsEnv.length() - 1); - System.setProperty("sponge.gameResources", gameLibsEnv.toString()); + System.setProperty("sponge.resources", resourcesEnv.toString()); final List gameArgs = new ArrayList<>(LauncherCommandLine.remainingArgs); gameArgs.add("--launchTarget");