From edaab002c29acda75479914ff5101c451598619c Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Mon, 30 Sep 2024 18:34:12 -0700 Subject: [PATCH] WIP: Use ModDevGradle instead of archloom for common and NeoForge --- build.gradle | 82 ++++---------- buildSrc/build.gradle.kts | 11 ++ buildSrc/src/main/java/Aw2AtTask.java | 129 ++++++++++++++++++++++ buildSrc/src/main/java/CopyTask.java | 44 ++++++++ fabric/build.gradle | 55 ++++++++- neoforge/build.gradle | 58 ++++++++-- neoforge/gradle.properties | 1 - settings.gradle | 3 +- src/main/resources/moonrise.accesswidener | 7 ++ 9 files changed, 315 insertions(+), 75 deletions(-) create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/java/Aw2AtTask.java create mode 100644 buildSrc/src/main/java/CopyTask.java delete mode 100644 neoforge/gradle.properties diff --git a/build.gradle b/build.gradle index 723b492d..4f75c67e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,8 @@ import me.modmuss50.mpp.ReleaseType plugins { - id("xyz.jpenilla.quiet-architectury-loom") + id("java-library") + id("net.neoforged.moddev") id("me.modmuss50.mod-publish-plugin") version "0.7.2" apply false } @@ -18,24 +19,39 @@ def getGitCommit = { -> return stdout.toString().trim() } +Aw2AtTask.configureDefault( + getProject(), + layout.projectDirectory.file("src/main/resources/moonrise.accesswidener").getAsFile(), + sourceSets.main +) + +neoForge { + neoFormVersion = "1.21.3-20241023.131943" + validateAccessTransformers = true +} + +tasks.named("createMinecraftArtifacts") { + dependsOn("copyAt") +} dependencies { - modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + // todo: check versions + compileOnly "net.fabricmc:sponge-mixin:0.13.4+mixin.0.8.5" + compileOnly "io.github.llamalad7:mixinextras-common:0.4.1" api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") api("org.yaml:snakeyaml:${rootProject.snakeyaml_version}") - modImplementation "me.shedaniel.cloth:cloth-config:${rootProject.cloth_version}" + // todo: does cloth publish a platform-agnostic jar in mojang mappings? + compileOnly "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}" } -File awFile = file("src/main/resources/moonrise.accesswidener") - allprojects { group = rootProject.maven_group version = rootProject.mod_version + "+" + getGitCommit() - plugins.apply("xyz.jpenilla.quiet-architectury-loom") + plugins.apply("java-library") java { withSourcesJar() @@ -62,11 +78,6 @@ allprojects { maven { url "https://maven.terraformersmc.com/releases/" } } - dependencies { - minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings loom.officialMojangMappings() - } - // make build reproducible tasks.withType(AbstractArchiveTask).configureEach { preserveFileTimestamps = false @@ -82,32 +93,12 @@ allprojects { rename { "${it}_${rootProject.base.archivesName.get()}"} } } - - loom { - accessWidenerPath = awFile - mixin { - useLegacyMixinAp = false - } - } } subprojects { - loom.mods { - main { - sourceSet("main") - sourceSet("main", project.rootProject) - } - } - loom.runs.all { - ideConfigGenerated true - property "mixin.debug", "true" - property "Moonrise.MaxViewDistance", "128" - } - plugins.apply("me.modmuss50.mod-publish-plugin") publishMods { - file = remapJar.archiveFile if (project.version.contains("-beta.")) { type = ReleaseType.BETA } else { @@ -129,33 +120,4 @@ subprojects { minecraftVersions = supportedMcVersions } } - - // Setup a run with lithium for compatibility testing - sourceSets.create("lithium") - configurations.create("lithium") - loom { - createRemapConfigurations(sourceSets.lithium) - runs { - register("lithiumClient") { - client() - property "mixin.debug", "true" - } - } - } - tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) { - getClasspath().from(configurations.modRuntimeClasspathLithiumMapped) - } - dependencies { - String coordinates = "maven.modrinth:lithium:" - if (getProject().name == "Moonrise-NeoForge") { - coordinates += rootProject.neo_lithium_version - } else { - coordinates += rootProject.fabric_lithium_version - } - modLithiumRuntimeOnly coordinates - } -} - -loom.runs.all { - ideConfigGenerated false } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 00000000..81dbb2bb --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,11 @@ +repositories { + gradlePluginPortal() + mavenCentral() + maven("https://maven.fabricmc.net/") + maven("https://maven.architectury.dev/") +} + +dependencies { + implementation("net.fabricmc:access-widener:2.1.0") + implementation("dev.architectury:at:1.0.1") +} diff --git a/buildSrc/src/main/java/Aw2AtTask.java b/buildSrc/src/main/java/Aw2AtTask.java new file mode 100644 index 00000000..c881ff85 --- /dev/null +++ b/buildSrc/src/main/java/Aw2AtTask.java @@ -0,0 +1,129 @@ +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 java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import javax.inject.Inject; +import net.fabricmc.accesswidener.AccessWidenerReader; +import net.fabricmc.accesswidener.AccessWidenerVisitor; +import org.cadixdev.bombe.type.signature.MethodSignature; +import org.gradle.api.DefaultTask; +import org.gradle.api.Project; +import org.gradle.api.file.ProjectLayout; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.TaskProvider; + +@CacheableTask +public abstract class Aw2AtTask extends DefaultTask { + + @InputFile + @PathSensitive(PathSensitivity.NONE) + public abstract RegularFileProperty getInputFile(); + + @OutputFile + public abstract RegularFileProperty getOutputFile(); + + @Inject + public abstract ProjectLayout getLayout(); + + public static TaskProvider configureDefault( + final Project project, + final File awFile, + final SourceSet sourceSet + ) { + final TaskProvider aw2at = project.getTasks().register("aw2at", Aw2AtTask.class, task -> { + task.getOutputFile().set(project.getLayout().getBuildDirectory().file("aw2at/files/accesstransformer.cfg")); + task.getInputFile().set(awFile); + }); + + final TaskProvider copyTask = project.getTasks().register("copyAt", CopyTask.class, copy -> { + copy.getInputFile().set(aw2at.flatMap(Aw2AtTask::getOutputFile)); + copy.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir("aw2at/dir")); + copy.getDestination().set("META-INF/accesstransformer.cfg"); + }); + + sourceSet.resources(resources -> { + resources.srcDir(copyTask.flatMap(CopyTask::getOutputDirectory)); + }); + + return aw2at; + } + + @TaskAction + public void run() { + try (final BufferedReader reader = Files.newBufferedReader(this.getInputFile().get().getAsFile().toPath())) { + final AccessTransformSet accessTransformSet = toAccessTransformSet(reader); + Files.deleteIfExists(this.getOutputFile().get().getAsFile().toPath()); + Files.createDirectories(this.getOutputFile().get().getAsFile().toPath().getParent()); + AccessTransformFormats.FML.write(this.getOutputFile().get().getAsFile().toPath(), accessTransformSet); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + // Below methods are heavily based on architectury-loom Aw2At class (MIT licensed) + /* + MIT License + + Copyright (c) 2016 FabricMC + + 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. + */ + + public static AccessTransformSet toAccessTransformSet(final BufferedReader reader) throws IOException { + AccessTransformSet atSet = AccessTransformSet.create(); + + new AccessWidenerReader(new AccessWidenerVisitor() { + @Override + public void visitClass(final String name, final AccessWidenerReader.AccessType access, final boolean transitive) { + atSet.getOrCreateClass(name).merge(toAt(access)); + } + + @Override + public void visitMethod(final String owner, final String name, final String descriptor, final AccessWidenerReader.AccessType access, final boolean transitive) { + atSet.getOrCreateClass(owner).mergeMethod(MethodSignature.of(name, descriptor), toAt(access)); + } + + @Override + public void visitField(final String owner, final String name, final String descriptor, final AccessWidenerReader.AccessType access, final boolean transitive) { + atSet.getOrCreateClass(owner).mergeField(name, toAt(access)); + } + }).read(reader); + + return atSet; + } + + public static AccessTransform toAt(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/buildSrc/src/main/java/CopyTask.java b/buildSrc/src/main/java/CopyTask.java new file mode 100644 index 00000000..b727a66d --- /dev/null +++ b/buildSrc/src/main/java/CopyTask.java @@ -0,0 +1,44 @@ +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.stream.Stream; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.TaskAction; + +public abstract class CopyTask extends DefaultTask { + + @InputFile + public abstract RegularFileProperty getInputFile(); + + @Input + public abstract Property getDestination(); + + @OutputDirectory + public abstract DirectoryProperty getOutputDirectory(); + + @TaskAction + public void run() { + final Path outputDirPath = this.getOutputDirectory().get().getAsFile().toPath(); + try { + try (final Stream walk = Files.walk(outputDirPath)) { + for (final Path path : walk.sorted(Comparator.reverseOrder()).toList()) { + Files.delete(path); + } + } + + final Path destFile = outputDirPath.resolve(this.getDestination().get()); + Files.createDirectories(destFile.getParent()); + + Files.copy(this.getInputFile().get().getAsFile().toPath(), destFile); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/fabric/build.gradle b/fabric/build.gradle index 0d2f6b94..b37116d9 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,5 +1,5 @@ plugins { - id("xyz.jpenilla.quiet-architectury-loom") + id("quiet-fabric-loom") id 'maven-publish' id 'com.gradleup.shadow' } @@ -13,10 +13,15 @@ configurations.implementation { } dependencies { - add('shadow', project([path: ":", configuration: "namedElements"])) - runtimeOnly(project(":").sourceSets.main.output) + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings loom.officialMojangMappings() modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + // todo: runs are broken (cannot find aw file) + //add('shadow', project([path: ":", configuration: "namedElements"])) + //runtimeOnly(project(":").sourceSets.main.output) + libs(project(":")) + libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}") @@ -49,6 +54,7 @@ shadowJar { } publishMods { + file = remapJar.archiveFile modLoaders = ["fabric"] modrinth { @@ -66,3 +72,46 @@ publishMods { ) } } + +loom { + accessWidenerPath.set(getRootProject().file("src/main/resources/moonrise.accesswidener")) + mixin { + useLegacyMixinAp = false + } + runs.all { + ideConfigGenerated true + property "mixin.debug", "true" + property "Moonrise.MaxViewDistance", "128" + } + mods { + main { + sourceSet("main") + sourceSet("main", project.rootProject) + } + } +} + +// Setup a run with lithium for compatibility testing +sourceSets.create("lithium") +configurations.create("lithium") +loom { + createRemapConfigurations(sourceSets.lithium) + runs { + register("lithiumClient") { + client() + property "mixin.debug", "true" + } + } +} +tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) { + getClasspath().from(configurations.modRuntimeClasspathLithiumMapped) +} +dependencies { + String coordinates = "maven.modrinth:lithium:" + if (getProject().name == "Moonrise-NeoForge") { + coordinates += rootProject.neo_lithium_version + } else { + coordinates += rootProject.fabric_lithium_version + } + modLithiumRuntimeOnly coordinates +} diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 14027e1a..417a43c3 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -1,7 +1,8 @@ -import net.fabricmc.loom.util.aw2at.Aw2At +import net.neoforged.moddevgradle.internal.RunGameTask +import java.nio.file.Files plugins { - id("xyz.jpenilla.quiet-architectury-loom") + id("net.neoforged.moddev") id 'maven-publish' id 'com.gradleup.shadow' } @@ -13,21 +14,55 @@ repositories { } } +Aw2AtTask.configureDefault( + getProject(), + rootProject.layout.projectDirectory.file("src/main/resources/moonrise.accesswidener").getAsFile(), + sourceSets.main +) + +neoForge { + version = rootProject.neoforge_version + validateAccessTransformers = true + runs { + client { + client() + mods.set([]) // Work around classpath issues by using the production jar for dev runs + } + server { + server() + mods.set([]) // Work around classpath issues by using the production jar for dev runs + } + } +} + +tasks.named("createMinecraftArtifacts") { + dependsOn("copyAt") +} + configurations.implementation { extendsFrom(configurations.shadow) } dependencies { - add('shadow', project([path: ":", configuration: "namedElements"])) - neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}" + add('shadow', project(":")) shadow("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") shadow("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") shadow("org.yaml:snakeyaml:${rootProject.snakeyaml_version}") - forgeExtra("org.yaml:snakeyaml:${rootProject.snakeyaml_version}") - modImplementation "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}" - include "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}" + implementation "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}" + jarJar "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}" +} + +// Work around classpath issues by using the production jar for dev runs +tasks.withType(RunGameTask).configureEach { + dependsOn(tasks.shadowJar) + doFirst { + def jar = file("run/mods/main.jar") + jar.parentFile.mkdirs() + jar.delete() + Files.copy(tasks.shadowJar.archiveFile.get().asFile.toPath(), jar.toPath()) + } } processResources { @@ -38,8 +73,12 @@ processResources { } } +jar { + archiveClassifier = "" +} + shadowJar { - archiveClassifier = "dev-all" + archiveClassifier = "" destinationDirectory = layout.buildDirectory.dir("libs") configurations = [project.configurations.shadow] relocate 'ca.spottedleaf.concurrentutil', 'ca.spottedleaf.moonrise.libs.ca.spottedleaf.concurrentutil' @@ -47,9 +86,8 @@ shadowJar { relocate 'org.yaml.snakeyaml', 'ca.spottedleaf.moonrise.libs.org.yaml.snakeyaml' } -Aw2At.setup(getProject(), tasks.remapJar) - publishMods { + file = shadowJar.archiveFile modLoaders = ["neoforge"] modrinth { diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties deleted file mode 100644 index 7da18ea6..00000000 --- a/neoforge/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -loom.platform=neoforge diff --git a/settings.gradle b/settings.gradle index c23117ba..16e74c05 100644 --- a/settings.gradle +++ b/settings.gradle @@ -24,7 +24,8 @@ pluginManagement { plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" - id("xyz.jpenilla.quiet-architectury-loom") version "1.7.300" apply false + id("quiet-fabric-loom") version "1.8.306" apply false + id("net.neoforged.moddev") version "2.0.49-beta" apply false id 'com.gradleup.shadow' version '8.3.0' apply false } diff --git a/src/main/resources/moonrise.accesswidener b/src/main/resources/moonrise.accesswidener index e217190b..b4415afe 100644 --- a/src/main/resources/moonrise.accesswidener +++ b/src/main/resources/moonrise.accesswidener @@ -22,6 +22,8 @@ accessible field net/minecraft/world/level/chunk/PalettedContainer data Lnet/min # PalettedContainer.Data accessible class net/minecraft/world/level/chunk/PalettedContainer$Data +# MDG requires we AT the constructor if we AT the class +accessible method net/minecraft/world/level/chunk/PalettedContainer$Data (Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Lnet/minecraft/world/level/chunk/Palette;)V # PaletteResize @@ -150,6 +152,11 @@ accessible method net/minecraft/world/level/material/Fluid isEmpty ()Z accessible method net/minecraft/world/level/material/Fluid createLegacyBlock (Lnet/minecraft/world/level/material/FluidState;)Lnet/minecraft/world/level/block/state/BlockState; accessible method net/minecraft/world/level/material/Fluid isRandomlyTicking ()Z +# We need to manually copy these down for MDG +accessible method net/minecraft/world/level/material/EmptyFluid isEmpty ()Z +accessible method net/minecraft/world/level/material/EmptyFluid createLegacyBlock (Lnet/minecraft/world/level/material/FluidState;)Lnet/minecraft/world/level/block/state/BlockState; +accessible method net/minecraft/world/level/material/LavaFluid createLegacyBlock (Lnet/minecraft/world/level/material/FluidState;)Lnet/minecraft/world/level/block/state/BlockState; +accessible method net/minecraft/world/level/material/LavaFluid isRandomlyTicking ()Z # VisibilitySet accessible field net/minecraft/client/renderer/chunk/VisibilitySet FACINGS I