From c975348482da6380180bbd6889e859d38a98c7d1 Mon Sep 17 00:00:00 2001 From: Su5eD Date: Sat, 30 Dec 2023 22:56:22 +0100 Subject: [PATCH] NeoForge project setup --- build.gradle | 253 +++++++++++--------- fabric-api-base/build.gradle | 10 +- gradle.properties | 6 + gradle/ffapi-setup.gradle | 437 +++++++++++++++++++++++++++++++++++ gradle/package-info.gradle | 70 +++--- settings.gradle | 102 ++++---- 6 files changed, 682 insertions(+), 196 deletions(-) create mode 100644 gradle/ffapi-setup.gradle diff --git a/build.gradle b/build.gradle index 6185d0877..751248266 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,17 @@ +buildscript { + dependencies { + classpath 'org.kohsuke:github-api:1.135' + classpath 'org.apache.groovy:groovy-toml:4.0.12' + } +} + plugins { id "java-library" id "eclipse" id "idea" id "maven-publish" id 'jacoco' - id "fabric-loom" version "1.4.4" apply false + id "dev.architectury.loom" version "1.4-SNAPSHOT" apply false id "com.diffplug.spotless" version "6.20.0" id "org.ajoberstar.grgit" version "3.1.0" id "me.modmuss50.remotesign" version "0.4.0" apply false @@ -12,8 +19,10 @@ plugins { } def ENV = System.getenv() +def signingEnabled = ENV.SIGNING_SERVER != null +def configuredVersion = project.version -version = project.version + "+" + (ENV.GITHUB_RUN_NUMBER ? "" : "local-") + getBranch() +version = project.version + '+' + project.forgified_version + "+" + (ENV.GITHUB_RUN_NUMBER ? "" : "local-") + getBranch() logger.lifecycle("Building Fabric: " + version) @@ -59,7 +68,7 @@ def getBranch() { def moduleDependencies(project, List depNames) { def deps = depNames.iterator().collect { project.dependencies.project(path: ":$it", configuration: 'namedElements') } - def clientOutputs = depNames.iterator().collect { findProject(":$it").sourceSets.client.output } + def clientOutputs = /*depNames.iterator().collect { findProject(":$it").sourceSets.client.output }*/ [] project.dependencies { deps.each { @@ -92,7 +101,7 @@ def moduleDependencies(project, List depNames) { def testDependencies(project, List depNames) { def deps = depNames.iterator().collect { project.dependencies.project(path: ":$it", configuration: 'namedElements') } - def clientOutputs = depNames.iterator().collect { findProject(":$it").sourceSets.client.output } + def clientOutputs = /*depNames.iterator().collect { findProject(":$it").sourceSets.client.output }*/ [] project.dependencies { deps.each { @@ -100,13 +109,13 @@ def testDependencies(project, List depNames) { } clientOutputs.each { - testmodClientImplementation it + testmodImplementation it } } } allprojects { - group = "net.fabricmc.fabric-api" + group = "dev.su5ed.sinytra.fabric-api" apply plugin: "maven-publish" apply plugin: "me.modmuss50.remotesign" @@ -115,16 +124,18 @@ allprojects { enabled = false } - remoteSign { - requestUrl = ENV.SIGNING_SERVER - pgpAuthKey = ENV.SIGNING_PGP_KEY - jarAuthKey = ENV.SIGNING_JAR_KEY + if (signingEnabled) { + remoteSign { + requestUrl = ENV.SIGNING_SERVER + pgpAuthKey = ENV.SIGNING_PGP_KEY + jarAuthKey = ENV.SIGNING_JAR_KEY - useDummyForTesting = ENV.SIGNING_SERVER == null + useDummyForTesting = ENV.SIGNING_SERVER == null - afterEvaluate { - // PGP sign all maven publications. - sign publishing.publications.mavenJava + afterEvaluate { + // PGP sign all maven publications. + sign publishing.publications.mavenJava + } } } @@ -137,8 +148,8 @@ allprojects { } apply plugin: "java-library" - apply plugin: "checkstyle" - apply plugin: "fabric-loom" +// apply plugin: "checkstyle" + apply plugin: "dev.architectury.loom" apply plugin: "com.diffplug.spotless" tasks.withType(JavaCompile).configureEach { @@ -151,28 +162,34 @@ allprojects { } loom { - splitEnvironmentSourceSets() +// splitEnvironmentSourceSets() } sourceSets { - testmod { - compileClasspath += main.compileClasspath - runtimeClasspath += main.runtimeClasspath + main { + java { + srcDir "src/client/java" + } + resources { + srcDir "src/client/resources" + } } - - testmodClient { + + testmod { compileClasspath += main.compileClasspath runtimeClasspath += main.runtimeClasspath - compileClasspath += client.compileClasspath - runtimeClasspath += client.runtimeClasspath - compileClasspath += testmod.compileClasspath - runtimeClasspath += testmod.runtimeClasspath + java { + srcDir "src/testmodClient/java" + } + resources { + srcDir "src/testmodClient/resources" + } } test { - compileClasspath += testmodClient.compileClasspath - runtimeClasspath += testmodClient.runtimeClasspath + compileClasspath += testmod.compileClasspath + runtimeClasspath += testmod.runtimeClasspath } } @@ -180,11 +197,15 @@ allprojects { runtimeOnlyLog4j = true runs { + configureEach { + property "mixin.debug", "true" + } + testmodClient { client() ideConfigGenerated project.rootProject == project name = "Testmod Client" - source sourceSets.testmodClient + source sourceSets.testmod } testmodServer { server() @@ -206,27 +227,33 @@ allprojects { loom.mods.register(p.name) { sourceSet p.sourceSets.main - sourceSet p.sourceSets.client +// sourceSet p.sourceSets.client } - loom.mods.register(p.name + "-testmod") { - sourceSet p.sourceSets.testmod - sourceSet p.sourceSets.testmodClient + if (p.file("src/testmod").exists() || p.file("src/testmodClient").exists()) { + loom.mods.register(p.name + "-testmod") { + sourceSet p.sourceSets.testmod +// sourceSet p.sourceSets.testmodClient + } } } + repositories { + maven { url "https://maven.neoforged.net/releases/" } + } + dependencies { minecraft "com.mojang:minecraft:$rootProject.minecraft_version" mappings "net.fabricmc:yarn:${rootProject.minecraft_version}${project.yarn_version}:v2" - modApi "net.fabricmc:fabric-loader:${project.loader_version}" + neoForge "net.neoforged:neoforge:${rootProject.forge_version}" testmodImplementation sourceSets.main.output - testmodClientImplementation sourceSets.main.output - testmodClientImplementation sourceSets.client.output - testmodClientImplementation sourceSets.testmod.output +// testmodClientImplementation sourceSets.main.output +// testmodClientImplementation sourceSets.client.output +// testmodClientImplementation sourceSets.testmod.output testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}" - testImplementation sourceSets.testmodClient.output + testImplementation sourceSets.testmod.output testImplementation 'org.mockito:mockito-core:5.4.0' } @@ -265,18 +292,20 @@ allprojects { } } - checkstyle { - configFile = rootProject.file("checkstyle.xml") - toolVersion = "10.12.1" - } +// checkstyle { +// configFile = rootProject.file("checkstyle.xml") +// toolVersion = "10.12.1" +// } tasks.withType(AbstractArchiveTask).configureEach { preserveFileTimestamps = false reproducibleFileOrder = true } - remoteSign { - sign remapJar + if (signingEnabled) { + remoteSign { + sign remapJar + } } // Run this task after updating minecraft to regenerate any required resources @@ -286,7 +315,7 @@ allprojects { tasks.register('testmodJar', Jar) { from sourceSets.testmod.output - from sourceSets.testmodClient.output +// from sourceSets.testmodClient.output destinationDirectory = new File(project.buildDir, "devlibs") archiveClassifier = "testmod" } @@ -312,14 +341,14 @@ allprojects { input = testmodJar.archiveFile archiveClassifier = "testmod" addNestedDependencies = false - includesClientOnlyClasses = true - clientOnlySourceSetName = sourceSets.testmodClient.name +// includesClientOnlyClasses = true +// clientOnlySourceSetName = sourceSets.testmodClient.name } build.dependsOn remapTestmodJar tasks.register('validateMixinNames', net.fabricmc.loom.task.ValidateMixinNameTask) { source(sourceSets.main.output) - source(sourceSets.client.output) +// source(sourceSets.client.output) source(sourceSets.testmod.output) } @@ -328,21 +357,21 @@ allprojects { apply from: rootProject.file('gradle/validate-annotations.gradle') } -remapTestmodJar { - def testModJarTasks = [] - - subprojects { - if (it.name == "deprecated" || !(it.file("src/testmod").exists() || it.file("src/testmodClient").exists())) { - return - } - - testModJarTasks += it.tasks.remapTestmodJar - } - - nestedJars.setFrom(testModJarTasks) - addNestedDependencies = true - clientOnlySourceSetName = sourceSets.testmodClient.name -} +//remapTestmodJar { +// def testModJarTasks = [] +// +// subprojects { +// if (it.name == "deprecated" || !(it.file("src/testmod").exists() || it.file("src/testmodClient").exists())) { +// return +// } +// +// testModJarTasks += it.tasks.remapTestmodJar +// } +// +// nestedJars.setFrom(testModJarTasks) +// addNestedDependencies = true +// clientOnlySourceSetName = sourceSets.testmodClient.name +//} // Apply auxiliary buildscripts to submodules // This must be done after all plugins are applied to subprojects @@ -351,7 +380,7 @@ apply from: "gradle/module-versioning.gradle" loom { // Required as the item-group API uses access widened classes in its API, without this the javadoc generation fails. - accessWidenerPath = file("fabric-item-group-api-v1/src/main/resources/fabric-item-group-api-v1.accesswidener") +// accessWidenerPath = file("fabric-item-group-api-v1/src/main/resources/fabric-item-group-api-v1.accesswidener") } javadoc { @@ -379,10 +408,10 @@ javadoc { } source(it.sourceSets.main.allJava) - source(it.sourceSets.client.allJava) +// source(it.sourceSets.client.allJava) } - classpath = files(sourceSets.main.compileClasspath, sourceSets.client.compileClasspath) + classpath = files(sourceSets.main.compileClasspath/*, sourceSets.client.compileClasspath*/) include("**/api/**") failOnError true } @@ -433,7 +462,7 @@ loom { } } } -test.dependsOn runGametest +//test.dependsOn runGametest def coverageTasks = [ runGametestCoverage, @@ -457,7 +486,7 @@ tasks.register('coverage', JacocoReport) { if (p.path.startsWith(":deprecated")) { return } - sourceSets p.sourceSets.main, p.sourceSets.client, p.sourceSets.testmod, p.sourceSets.testmodClient + sourceSets p.sourceSets.main, p.sourceSets.client, p.sourceSets.testmod/*, p.sourceSets.testmodClient*/ } // Exclude mixins @@ -566,12 +595,12 @@ def addPomMetadataInformation(Project project, MavenPom pom) { def modJson = new JsonSlurper().parse(modJsonFile) pom.name = modJson.name - pom.url = "https://github.com/FabricMC/fabric/tree/HEAD/${project.rootDir.relativePath(project.projectDir)}" + pom.url = "https://github.com/Sinytra/ForgifiedFabricAPI/tree/HEAD/${project.rootDir.relativePath(project.projectDir)}" pom.description = modJson.description pom.licenses { license { name = "Apache-2.0" - url = "https://github.com/FabricMC/fabric/blob/HEAD/LICENSE" + url = "https://github.com/Sinytra/ForgifiedFabricAPI/blob/HEAD/LICENSE" } } pom.developers { @@ -579,15 +608,18 @@ def addPomMetadataInformation(Project project, MavenPom pom) { name = "FabricMC" url = "https://fabricmc.net/" } + developer { + name = "Sinytra" + } } pom.scm { - connection = "scm:git:https://github.com/FabricMC/fabric.git" - url = "https://github.com/FabricMC/fabric" - developerConnection = "scm:git:git@github.com:FabricMC/fabric.git" + connection = "scm:git:https://github.com/Sinytra/ForgifiedFabricAPI.git" + url = "https://github.com/Sinytra/ForgifiedFabricAPI" + developerConnection = "scm:git:git@github.com:Sinytra/ForgifiedFabricAPI.git" } pom.issueManagement { system = "GitHub" - url = "https://github.com/FabricMC/fabric/issues" + url = "https://github.com/Sinytra/ForgifiedFabricAPI/issues" } } @@ -596,26 +628,22 @@ subprojects { return } - base { - archivesName = project.name - } - dependencies { testmodImplementation sourceSets.main.output // Make all modules depend on the gametest api (and thus res loader) to try and promote its usage. - if (project.name != "fabric-gametest-api-v1") { - testmodImplementation project(path: ':fabric-gametest-api-v1', configuration: 'namedElements') - testmodClientImplementation project(":fabric-gametest-api-v1").sourceSets.client.output - testmodImplementation project(path: ':fabric-resource-loader-v0', configuration: 'namedElements') - testmodClientImplementation project(":fabric-resource-loader-v0").sourceSets.client.output - } +// if (project.name != "fabric-gametest-api-v1") { TODO ENABLE +// testmodImplementation project(path: ':fabric-gametest-api-v1', configuration: 'namedElements') +// testmodClientImplementation project(":fabric-gametest-api-v1").sourceSets.client.output +// testmodImplementation project(path: ':fabric-resource-loader-v0', configuration: 'namedElements') +// testmodClientImplementation project(":fabric-resource-loader-v0").sourceSets.client.output +// } // Make all testmods run with registry-sync-v0 as it is required to register new objects. - if (project.name != "fabric-registry-sync-v0") { - testmodRuntimeOnly project(path: ':fabric-registry-sync-v0', configuration: 'namedElements') - testmodClientImplementation project(":fabric-registry-sync-v0").sourceSets.client.output - } +// if (project.name != "fabric-registry-sync-v0") { TODO ENABLE +// testmodRuntimeOnly project(path: ':fabric-registry-sync-v0', configuration: 'namedElements') +// testmodClientImplementation project(":fabric-registry-sync-v0").sourceSets.client.output +// } } publishing { @@ -624,8 +652,8 @@ subprojects { pom { addPomMetadataInformation(project, pom) } - artifact(signRemapJar.output) { - builtBy(signRemapJar) + artifact(signingEnabled ? signRemapJar.output : remapJar) { + builtBy(signingEnabled ? signRemapJar : remapJar) } artifact(remapSourcesJar) { @@ -644,8 +672,8 @@ subprojects { publishing { publications { mavenJava(MavenPublication) { - artifact(signRemapJar.output) { - builtBy(signRemapJar) + artifact(signingEnabled ? signRemapJar.output : remapJar) { + builtBy(signingEnabled ? signRemapJar : remapJar) } artifact(sourcesJar) { @@ -723,29 +751,29 @@ dependencies { } api project(path: "${it.path}", configuration: "namedElements") - clientImplementation project("${it.path}:").sourceSets.client.output +// clientImplementation project("${it.path}:").sourceSets.client.output testmodImplementation project("${it.path}:").sourceSets.testmod.output - testmodClientImplementation project("${it.path}:").sourceSets.testmodClient.output +// testmodClientImplementation project("${it.path}:").sourceSets.testmodClient.output } } } -remapJar { - afterEvaluate { - subprojects.each { - if (it.name in devOnlyModules || it.name == "deprecated") { - return - } - - // Include the signed or none signed jar from the sub project. - nestedJars.from project("${it.path}").tasks.getByName("signRemapJar") - } - } -} +//remapJar { +// afterEvaluate { +// subprojects.each { +// if (it.name in devOnlyModules || it.name == "deprecated") { +// return +// } +// +// // Include the signed or none signed jar from the sub project. +// nestedJars.from project("${it.path}").tasks.getByName("signRemapJar") +// } +// } +//} publishMods { - file = signRemapJar.output + file = signingEnabled ? signRemapJar.output : remapJar.archiveFile changelog = providers.environmentVariable("CHANGELOG").getOrElse("No changelog provided") type = project.prerelease == "true" ? BETA : STABLE displayName = "[${project.minecraft_version}] Fabric API $project.version" @@ -769,7 +797,9 @@ publishMods { } } -assemble.dependsOn signRemapJar +if (signingEnabled) { + assemble.dependsOn signRemapJar +} import java.util.stream.Collectors @@ -787,3 +817,10 @@ tasks.register('checkVersion') { tasks.publishMods.dependsOn checkVersion publish.mustRunAfter checkVersion + +ext { + DEV_ONLY_MODULES = devOnlyModules + SIGNING_ENABLED = signingEnabled + CONFIGURED_VERSION = configuredVersion +} +apply from: rootProject.file('gradle/ffapi-setup.gradle') diff --git a/fabric-api-base/build.gradle b/fabric-api-base/build.gradle index fc1eb105c..e5f99d0d2 100644 --- a/fabric-api-base/build.gradle +++ b/fabric-api-base/build.gradle @@ -1,7 +1,7 @@ version = getSubprojectVersion(project) -testDependencies(project, [ - ':fabric-command-api-v2', - ':fabric-lifecycle-events-v1', - ':fabric-screen-api-v1' -]) +//testDependencies(project, [ +// ':fabric-command-api-v2', +// ':fabric-lifecycle-events-v1', +// ':fabric-screen-api-v1' +//]) diff --git a/gradle.properties b/gradle.properties index 5643d9624..210623295 100644 --- a/gradle.properties +++ b/gradle.properties @@ -62,3 +62,9 @@ fabric-transfer-api-v1-version=4.0.6 fabric-transitive-access-wideners-v1-version=5.0.13 fabric-convention-tags-v1-version=1.5.9 fabric-client-tags-api-v1-version=1.1.6 + +# FFAPI Properties +loom.platform=neoforge +forge_version=20.4.69-beta +forgified_version=2.0.0 +forge_fabric_loader_version=2.6.0+0.15.0+1.20.1 diff --git a/gradle/ffapi-setup.gradle b/gradle/ffapi-setup.gradle new file mode 100644 index 000000000..5932340bd --- /dev/null +++ b/gradle/ffapi-setup.gradle @@ -0,0 +1,437 @@ +buildscript { + dependencies { + classpath files(rootProject.buildscript.configurations.classpath) + } +} + +import groovy.json.JsonSlurper +import groovy.toml.TomlBuilder +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace +import net.fabricmc.loom.build.nesting.IncludedJarFactory +import net.fabricmc.loom.task.RemapJarTask +import net.fabricmc.loom.task.service.MappingsService +import net.fabricmc.loom.util.FileSystemUtil +import net.fabricmc.loom.util.GroovyXmlUtil +import net.fabricmc.loom.util.LfWriter +import net.fabricmc.loom.util.aw2at.Aw2At +import net.fabricmc.loom.util.service.BuildSharedServiceManager +import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper +import dev.architectury.at.AccessTransformSet +import dev.architectury.at.io.AccessTransformFormats +import org.gradle.build.event.BuildEventsListenerRegistry + +import javax.inject.Inject +import java.nio.charset.StandardCharsets +import java.nio.file.Files + +allprojects { + if (name == 'deprecated') return + + sourceSets.configureEach { sourceSet -> + // We have to capture the source set name for the lazy string literals, + // otherwise it'll just be whatever the last source set is in the list. + def sourceSetName = sourceSet.name + def resourceRoots = sourceSet.resources.srcDirs + def taskName = sourceSet.getTaskName('generate', 'ForgeModMetadata') + def task = tasks.register(taskName, GenerateForgeModMetadata) { + group = 'fabric' + description = "Generates mods.toml files for $sourceSetName fabric mod." + + // Only apply to default source directory since we also add the generated + // sources to the source set. + sourceRoots.from(resourceRoots) + outputDir = file("src/generated/$sourceSetName/resources") + loaderVersionString = "1" + forgeVersionString = rootProject.forge_version + minecraftVersionString = rootProject.minecraft_version + accessWidener = loom.accessWidenerPath + } + sourceSet.resources.srcDir task + + def cleanTask = tasks.register(sourceSet.getTaskName('clean', 'ForgeModMetadata'), Delete) { + group = 'fabric' + delete file("src/generated/$sourceSetName/resources") + } + clean.dependsOn cleanTask + } + + def localDevJar = tasks.register("localDevJar", Jar) { + dependsOn jar + from(zipTree(jar.archiveFile)) + rename "accesstransformer_dev.cfg", "accesstransformer.cfg" + archiveClassifier = "local" + destinationDirectory = project.layout.buildDirectory.dir("devlibs") + manifest.from(jar.manifest) + } + remapJar { + doLast { + try (FileSystemUtil.Delegate fileSystem = FileSystemUtil.getJarFileSystem(archiveFile.get().asFile.toPath(), false)) { + def fs = fileSystem.get() + def atPath = fs.getPath("META-INF/accesstransformer_dev.cfg") + Files.deleteIfExists(atPath) + } + } + } + afterEvaluate { + configurations.namedElements { + outgoing.artifacts.clear() + outgoing.artifact(localDevJar) + } + } + tasks.withType(net.fabricmc.loom.task.AbstractRemapJarTask).configureEach { + remapperIsolation = false + } + + configurations { + remappedJars { + canBeConsumed = true + canBeResolved = false + } + testModRemappedJars { + canBeConsumed = true + canBeResolved = false + } + } + artifacts { + remappedJars(SIGNING_ENABLED ? tasks.named("signRemapJar") : remapJar) + } + // Because it dies on try-with-resources statements + spotlessGroovyGradle { + enabled = false + } + + loom.mixin { + useLegacyMixinAp = true + defaultRefmapName = project.base.archivesName.map { it + "-refmap.json" } + } + + repositories { + maven { url = "https://maven.su5ed.dev/releases" } + } +} + +configurations { + includedRemappedJars + includedTestModRemappedJars +} +subprojects.each { proj -> + if (!(proj.name in DEV_ONLY_MODULES) && proj.name != "deprecated") { + // Include the signed or none signed jar from the sub project. + dependencies { + includedRemappedJars project(path: proj.path, configuration: 'remappedJars') + } + } + + if (proj.name != "deprecated" && (proj.file("src/testmod").exists() || proj.file("src/testmodClient").exists())) { + dependencies { + includedTestModRemappedJars project(path: proj.path, configuration: 'testModRemappedJars') + } + } +} +def includedJarFactory = new IncludedJarFactory(project) +remapJar { + forgeNestedJars.addAll includedJarFactory.getForgeNestedJars(configurations.includedRemappedJars) + .map { it.left().collect { it.resolve() } } +} +remapTestmodJar { + mustRunAfter remapJar + forgeNestedJars.addAll includedJarFactory.getForgeNestedJars(configurations.includedTestModRemappedJars) + .map { it.left().collect { it.resolve() } } + addNestedDependencies = true +} + +subprojects { + loom { + runs { + gametest { + server() + ideConfigGenerated project.rootProject == project + name = "Testmod Game Test Server" + source sourceSets.testmod + + // Enable the gametest runner + property "forge.gameTestServer", "true" + } + } + } +} + +configurations { + referenceApi + jarCompatChecker + javadocDeps +} +dependencies { + referenceApi "net.fabricmc.fabric-api:fabric-api:$CONFIGURED_VERSION+${rootProject.minecraft_version}" + referenceApi "net.fabricmc.fabric-api:fabric-api-deprecated:$CONFIGURED_VERSION+${rootProject.minecraft_version}" + jarCompatChecker "dev.su5ed.sinytra:JarCompatibilityChecker:0.1.+:all" + javadocDeps "net.fabricmc:fabric-loader:${rootProject.loader_version}" + + // Include Forgified Fabric Loader + include "dev.su5ed.sinytra:fabric-loader:${rootProject.forge_fabric_loader_version}:full" +} +afterEvaluate { + javadoc { + classpath += files(configurations.javadocDeps) + } +} +afterEvaluate { + publishing { + publications { + mavenJava(MavenPublication) { + pom.withXml { + def depsNode = GroovyXmlUtil.getOrCreateNode(asNode(), "dependencies") + rootProject.configurations.include.dependencies.each { + def depNode = depsNode.appendNode("dependency") + depNode.appendNode("groupId", it.group) + depNode.appendNode("artifactId", it.name) + depNode.appendNode("version", it.version) + depNode.appendNode("scope", "compile") + } + } + } + } + } +} + +subprojects { + if (name == 'deprecated') return + + configurations { + apiLibs { + extendsFrom api + canBeResolved = true + } + baseLib + } + dependencies { + baseLib "net.fabricmc:fabric-loader:${rootProject.loader_version}" + } + def compareJar = tasks.register("compareJar", Jar) { + from zipTree(jar.archiveFile) + archiveClassifier = "compare" + destinationDirectory = project.layout.buildDirectory.dir("devlibs") + } + def remapReferenceApi = tasks.register("remapReferenceApi", RemapJarTask) { + group = "fabric" + outputs.cacheIf { true } + inputFile.fileProvider provider { + def deps = rootProject.configurations.referenceApi.resolvedConfiguration + def referenceDep = deps.lenientConfiguration.allModuleDependencies.find { it.moduleGroup == "net.fabricmc.fabric-api" && it.moduleName == project.name } + return referenceDep.allModuleArtifacts.first().file + } + sourceNamespace = MappingsNamespace.INTERMEDIARY.toString() + targetNamespace = MappingsNamespace.NAMED.toString() + destinationDirectory = project.layout.buildDirectory.dir(name) + classpath.from(loom.getMinecraftJarsCollection(MappingsNamespace.INTERMEDIARY)) + } + tasks.register("checkReferenceCompatibility", JavaExec) { + group = "verification" + + classpath(rootProject.configurations.jarCompatChecker) + mainClass = "net.minecraftforge.jarcompatibilitychecker.ConsoleTool" + args "--api", "--annotation-check-mode", "warn_added", "--internal-ann-mode", "skip" + def outputLog = project.layout.buildDirectory.dir(name).map { it.file("output.log") } + inputs.file(remapReferenceApi.flatMap { it.inputFile }) + .withPropertyName("inputFile") + .withPathSensitivity(PathSensitivity.RELATIVE) + outputs.file(outputLog) + .withPropertyName("outputFile") + outputs.cacheIf { true } + doFirst { + standardOutput = new FileOutputStream(outputLog.get().asFile) + } + } + afterEvaluate { + tasks.configureEach { + if (name == "prepareRemapReferenceApi") { + doFirst { + loom.mixin.useLegacyMixinAp = false + } + doLast { + loom.mixin.useLegacyMixinAp = true + } + } + } + checkReferenceCompatibility.configure { + dependsOn compareJar, remapReferenceApi + args "--base-jar", remapReferenceApi.get().archiveFile.get().asFile.absolutePath, "--input-jar", compareJar.get().archiveFile.get().asFile.absolutePath + (configurations.minecraftNamedCompile + configurations.apiLibs).each { args "--lib", it.absolutePath } + configurations.baseLib.resolve().each { args "--base-lib", it.absolutePath } + } + } + check { + dependsOn checkReferenceCompatibility + } +} + +abstract class GenerateForgeModMetadata extends DefaultTask { + @SkipWhenEmpty + @InputFiles + final ConfigurableFileCollection sourceRoots = project.objects.fileCollection() + + @OutputDirectory + final DirectoryProperty outputDir = project.objects.directoryProperty() + + @Input + final Property loaderVersionString = project.objects.property(String) + + @Input + final Property forgeVersionString = project.objects.property(String) + + @Input + final Property minecraftVersionString = project.objects.property(String) + + @InputFile + @Optional + final RegularFileProperty accessWidener = project.objects.fileProperty() + + @Inject + protected abstract BuildEventsListenerRegistry getBuildEventsListenerRegistry() + + private final Property mappingServiceUuid = project.objects.property(String) + + @Inject + public GenerateForgeModMetadata() { + Provider serviceManagerProvider = BuildSharedServiceManager.createForTask(this, getBuildEventsListenerRegistry()) + + mappingServiceUuid.value(project.provider { UnsafeWorkQueueHelper.create(MappingsService.createDefault(project, serviceManagerProvider.get().get(), MappingsNamespace.NAMED.toString(), MappingsNamespace.MOJANG.toString())) }) + } + + def normalizeModid(String modid) { + return modid.replaceAll('-', '_') + } + + @TaskAction + def run() { + def output = outputDir.get().asFile.toPath() + output.deleteDir() + def containsCode = sourceRoots.any { new File(it.parentFile, "java").exists() } + for (def sourceRoot in sourceRoots) { + if (!sourceRoot.isDirectory()) { + continue + } + + def root = sourceRoot.toPath() + def fabricMetadata = root.resolve("fabric.mod.json") + + if (Files.notExists(fabricMetadata)) { + continue + } + + def parser = new JsonSlurper() + def json = parser.parse(fabricMetadata) + def toml = new TomlBuilder() + def nextMajor = (minecraftVersionString.get().tokenize('.')[1].toInteger()) + 1 + def excludedDeps = ["fabricloader", "java", "minecraft"] + def modDependencies = json.depends.findAll { !excludedDeps.contains(it.key) }.collect { + def normalModid = normalizeModid(it.key as String) + return { _ -> + modId normalModid + type "required" + versionRange "[0, )" + ordering "NONE" + side "BOTH" + } + } + def normalModid = normalizeModid(json.id) + toml { + modLoader containsCode ? "javafml" : "lowcodefml" + loaderVersion "[${loaderVersionString.get()},)" + license json.license ?: "All Rights Reserved" + if (json.environment == "client") { + displayTest "IGNORE_ALL_VERSION" + } + if (json.environment == "server") { + displayTest "IGNORE_SERVER_VERSION" + } + + def providedMods = [] + mods([ + { + modId normalModid + version '${file.jarVersion}' + displayName json.name + if (json.icon) { + logoFile json.icon + } + if (json.authors) { + authors json.authors.join(', ') + } + if (json.description) { + description json.description + } + if (json.provides) { + providedMods += json.provides + } + if (json.id != normalModid) { + providedMods += json.id + } + if (!providedMods.empty) { + provides providedMods + } + } + ]) + + dependencies { + "$normalModid"([ + { + modId "neoforge" + type "required" + versionRange "[${forgeVersionString.get()},)" + ordering "NONE" + side "BOTH" + }, + { + modId "minecraft" + type "required" + versionRange "[${minecraftVersionString.get()},1.$nextMajor)" + ordering "NONE" + side "BOTH" + } + ] + modDependencies) + } + + if (json.mixins) { + def configs = json.mixins.collect { + if (it instanceof Map) { + return it.config + } else if (it instanceof String) { + return it + } else { + throw new RuntimeException("Unknown mixin config type ${it.getClass()}") + } + }.collect { + return { _ -> + config it + } + } + mixins(configs) + } + } + + def modsToml = output.resolve("META-INF/mods.toml") + Files.deleteIfExists(modsToml) + Files.createDirectories(modsToml.parent) + modsToml.withWriter { toml.writeTo(it) } + + if (accessWidener.isPresent()) { + def awPath = accessWidener.get().asFile.toPath() + def atPath = output.resolve("META-INF/accesstransformer.cfg") + + AccessTransformSet at = AccessTransformSet.create() + try (BufferedReader reader = Files.newBufferedReader(awPath, StandardCharsets.UTF_8)) { + at.merge(Aw2At.toAccessTransformSet(reader)) + } + + MappingsService service = UnsafeWorkQueueHelper.get(mappingServiceUuid, MappingsService.class); + + at = at.remap(service.getMemoryMappingTree(), service.getFromNamespace(), service.getToNamespace()) + + try (Writer writer = new LfWriter(Files.newBufferedWriter(atPath))) { + AccessTransformFormats.FML.write(writer, at); + } + } + } + } +} diff --git a/gradle/package-info.gradle b/gradle/package-info.gradle index 256f9c3c7..5e5e9356b 100644 --- a/gradle/package-info.gradle +++ b/gradle/package-info.gradle @@ -1,9 +1,8 @@ import java.nio.file.Files for (def sourceSet in [ - sourceSets.main, - sourceSets.client - ]) { + sourceSets.main +]) { // We have to capture the source set name for the lazy string literals, // otherwise it'll just be whatever the last source set is in the list. def sourceSetName = sourceSet.name @@ -14,15 +13,15 @@ for (def sourceSet in [ // Only apply to default source directory since we also add the generated // sources to the source set. - sourceRoot = file("src/$sourceSetName/java") + sourceRoots.from(sourceSet.java.srcDirs) header = rootProject.file('HEADER') - outputDir = file("src/generated/$sourceSetName") + outputDir = file("src/generated/$sourceSetName/java") } sourceSet.java.srcDir task def cleanTask = tasks.register(sourceSet.getTaskName('clean', 'ImplPackageInfos'), Delete) { group = 'fabric' - delete file("src/generated/$sourceSetName") + delete file("src/generated/$sourceSetName/java") } clean.dependsOn cleanTask } @@ -32,8 +31,8 @@ class GenerateImplPackageInfos extends DefaultTask { File header @SkipWhenEmpty - @InputDirectory - final DirectoryProperty sourceRoot = project.objects.directoryProperty() + @InputFiles + final ConfigurableFileCollection sourceRoots = project.objects.fileCollection() @OutputDirectory final DirectoryProperty outputDir = project.objects.directoryProperty() @@ -43,36 +42,41 @@ class GenerateImplPackageInfos extends DefaultTask { def output = outputDir.get().asFile.toPath() output.deleteDir() def headerText = header.readLines().join("\n") // normalize line endings - def root = sourceRoot.get().asFile.toPath() - for (def dir in ['impl', 'mixin']) { - def implDir = root.resolve("net/fabricmc/fabric/$dir") + for (def srcDir in sourceRoots) { + if (srcDir.isDirectory()) { + def root = srcDir.toPath() - if (Files.notExists(implDir)) { - continue - } + for (def dir in ['impl', 'mixin']) { + def implDir = root.resolve("net/fabricmc/fabric/$dir") - implDir.eachDirRecurse { - def containsJava = Files.list(it).any { - Files.isRegularFile(it) && it.fileName.toString().endsWith('.java') - } + if (Files.notExists(implDir)) { + continue + } + + implDir.eachDirRecurse { + def containsJava = Files.list(it).any { + Files.isRegularFile(it) && it.fileName.toString().endsWith('.java') + } - if (containsJava && Files.notExists(it.resolve('package-info.java'))) { - def relativePath = root.relativize(it) - def target = output.resolve(relativePath) - Files.createDirectories(target) + if (containsJava && Files.notExists(it.resolve('package-info.java'))) { + def relativePath = root.relativize(it) + def target = output.resolve(relativePath) + Files.createDirectories(target) - target.resolve('package-info.java').withWriter { - def packageName = relativePath.toString().replace(File.separator, '.') - it.write("""$headerText - |/** - | * Implementation code for ${project.name}. - | */ - |@ApiStatus.Internal - |package $packageName; - | - |import org.jetbrains.annotations.ApiStatus; - |""".stripMargin()) + target.resolve('package-info.java').withWriter { + def packageName = relativePath.toString().replace(File.separator, '.') + it.write("""$headerText + |/** + | * Implementation code for ${project.name}. + | */ + |@ApiStatus.Internal + |package $packageName; + | + |import org.jetbrains.annotations.ApiStatus; + |""".stripMargin()) + } + } } } } diff --git a/settings.gradle b/settings.gradle index 2db408cbe..6e4f52bd7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,6 +6,8 @@ pluginManagement { url = 'https://maven.fabricmc.net/' } mavenLocal() + maven { url "https://maven.architectury.dev/" } + maven { url "https://maven.neoforged.net/releases/" } } } @@ -13,54 +15,54 @@ rootProject.name = "fabric-api" include 'fabric-api-base' -include 'fabric-api-lookup-api-v1' -include 'fabric-biome-api-v1' -include 'fabric-block-api-v1' -include 'fabric-block-view-api-v2' -include 'fabric-blockrenderlayer-v1' -include 'fabric-client-tags-api-v1' -include 'fabric-command-api-v2' -include 'fabric-content-registries-v0' -include 'fabric-convention-tags-v1' -include 'fabric-crash-report-info-v1' -include 'fabric-data-generation-api-v1' -include 'fabric-dimensions-v1' -include 'fabric-entity-events-v1' -include 'fabric-events-interaction-v0' -include 'fabric-game-rule-api-v1' -include 'fabric-gametest-api-v1' -include 'fabric-item-api-v1' -include 'fabric-item-group-api-v1' -include 'fabric-key-binding-api-v1' -include 'fabric-lifecycle-events-v1' -include 'fabric-loot-api-v2' -include 'fabric-message-api-v1' -include 'fabric-mining-level-api-v1' -include 'fabric-model-loading-api-v1' -include 'fabric-networking-api-v1' -include 'fabric-object-builder-api-v1' -include 'fabric-particles-v1' -include 'fabric-recipe-api-v1' -include 'fabric-registry-sync-v0' -include 'fabric-renderer-api-v1' -include 'fabric-renderer-indigo' -include 'fabric-rendering-fluids-v1' -include 'fabric-rendering-v1' -include 'fabric-resource-conditions-api-v1' -include 'fabric-resource-loader-v0' -include 'fabric-screen-api-v1' -include 'fabric-screen-handler-api-v1' -include 'fabric-sound-api-v1' -include 'fabric-transfer-api-v1' -include 'fabric-transitive-access-wideners-v1' +//include 'fabric-api-lookup-api-v1' +//include 'fabric-biome-api-v1' +//include 'fabric-block-api-v1' +//include 'fabric-block-view-api-v2' +//include 'fabric-blockrenderlayer-v1' +//include 'fabric-client-tags-api-v1' +//include 'fabric-command-api-v2' +//include 'fabric-content-registries-v0' +//include 'fabric-convention-tags-v1' +//include 'fabric-crash-report-info-v1' +//include 'fabric-data-generation-api-v1' +//include 'fabric-dimensions-v1' +//include 'fabric-entity-events-v1' +//include 'fabric-events-interaction-v0' +//include 'fabric-game-rule-api-v1' +//include 'fabric-gametest-api-v1' +//include 'fabric-item-api-v1' +//include 'fabric-item-group-api-v1' +//include 'fabric-key-binding-api-v1' +//include 'fabric-lifecycle-events-v1' +//include 'fabric-loot-api-v2' +//include 'fabric-message-api-v1' +//include 'fabric-mining-level-api-v1' +//include 'fabric-model-loading-api-v1' +//include 'fabric-networking-api-v1' +//include 'fabric-object-builder-api-v1' +//include 'fabric-particles-v1' +//include 'fabric-recipe-api-v1' +//include 'fabric-registry-sync-v0' +//include 'fabric-renderer-api-v1' +//include 'fabric-renderer-indigo' +//include 'fabric-rendering-fluids-v1' +//include 'fabric-rendering-v1' +//include 'fabric-resource-conditions-api-v1' +//include 'fabric-resource-loader-v0' +//include 'fabric-screen-api-v1' +//include 'fabric-screen-handler-api-v1' +//include 'fabric-sound-api-v1' +//include 'fabric-transfer-api-v1' +//include 'fabric-transitive-access-wideners-v1' -include 'deprecated' -include 'deprecated:fabric-command-api-v1' -include 'deprecated:fabric-commands-v0' -include 'deprecated:fabric-containers-v0' -include 'deprecated:fabric-events-lifecycle-v0' -include 'deprecated:fabric-keybindings-v0' -include 'deprecated:fabric-models-v0' -include 'deprecated:fabric-renderer-registries-v1' -include 'deprecated:fabric-rendering-data-attachment-v1' -include 'deprecated:fabric-rendering-v0' +//include 'deprecated' +//include 'deprecated:fabric-command-api-v1' +//include 'deprecated:fabric-commands-v0' +//include 'deprecated:fabric-containers-v0' +//include 'deprecated:fabric-events-lifecycle-v0' +//include 'deprecated:fabric-keybindings-v0' +//include 'deprecated:fabric-models-v0' +//include 'deprecated:fabric-renderer-registries-v1' +//include 'deprecated:fabric-rendering-data-attachment-v1' +//include 'deprecated:fabric-rendering-v0'