diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8b5a003..0d06b99 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,6 +50,8 @@ jobs: run: chmod +x ./gradlew - name: Gradle Build + env: + RELEASE_BUILD: ${{ github.event_name == 'release' && github.event.action == 'published' }} run: ./gradlew build - name: Attest Build Provenance @@ -67,6 +69,8 @@ jobs: - name: Maven Publish if: github.event_name == 'release' && github.event.action == 'published' + env: + RELEASE_BUILD: true run: ./gradlew publish - name: Github Publish @@ -90,6 +94,7 @@ jobs: - name: Modrinth Publish env: + RELEASE_BUILD: true MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} CHANGELOG: ${{ github.event.release.body }} RELEASE_TYPE: ${{ steps.detect-release-type.outputs.type }} diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 4bbc8f0..0000000 --- a/build.gradle +++ /dev/null @@ -1,167 +0,0 @@ -//file:noinspection GroovyAssignabilityCheck -//file:noinspection GroovyAccessibility -plugins { - id "maven-publish" - alias libs.plugins.loom - alias libs.plugins.minotaur -} - -boolean inCI = System.getenv("GITHUB_RUN_NUMBER") != null - -version = "$modVersion+$branchName" -base.archivesName = project.slug - -repositories { - exclusiveContent { - forRepository { - //noinspection ForeignDelegate - maven { - name 'Modrinth' - url "https://api.modrinth.com/maven" - } - } - filter { - includeGroup "maven.modrinth" - } - } - exclusiveContent { - forRepository { - //noinspection ForeignDelegate - maven { - name 'Terraformers' - url "https://maven.terraformersmc.com/" - } - } - filter { - includeGroup "com.terraformersmc" - } - } - exclusiveContent { - forRepository { - //noinspection ForeignDelegate - maven { - name 'ParchmentMC' - url "https://maven.parchmentmc.org" - } - } - filter { - includeGroup "org.parchmentmc.data" - } - } - flatDir { - dir("libs") - } -} - -dependencies { - minecraft libs.mc - mappings loom.layered { - it.officialMojangMappings() - it.parchment variantOf(libs.parchment) { artifactType "zip" } - it.mappings file("custom_mappings.tiny") - } - - modImplementation libs.fl - modImplementation libs.fapi - - if (!inCI) { - modRuntimeOnly libs.mm - } - - modCompileOnly libs.sodium - - if (!inCI && enable_sodium.toBoolean()) { - modLocalRuntime libs.sodium - } - - modCompileOnly libs.axiom - - if (!inCI && enable_axiom.toBoolean()) { - modLocalRuntime( - libs.axiom, - libs.axiom.axiomclientapi, - libs.axiom.lattice, - libs.axiom.mixinconstraints - ) - } - - include(implementation(annotationProcessor(libs.mixinextras.get()))) -} - -processResources { - final Map meta = [ - version : version, - modId : modId, - modName : modName, - modDescription: modDescription, - homepage : "https://modrinth.com/mod/${slug}", - issues : "https://github.com/${user}/${slug}/issues", - sources : "https://github.com/${user}/${slug}", - license : license, - authors : authors.split(", ").join("\",\n \""), - contributors : contributors.split(", ").join("\",\n \""), - members : "${authors}${contributors ? ". Contributions by ${contributors}." : ""}", - mc : compatibleVersions.split(", ")[0], - fl : libs.versions.fl.get(), - fapi : libs.versions.fapi.get(), - ] - inputs.properties meta - filesMatching("*.mod.json") { expand(meta) } - filesMatching("META-INF/*mods.toml") { expand(meta) } - - // don't add development or to-do files into built jar - exclude("**/*.bbmodel", "**/*.lnk", "**/*.xcf", "**/*.blend", "**/*.blend1") -} - -loom { - runs.configureEach { - vmArg("-XX:+AllowEnhancedClassRedefinition") - vmArg("-XX:+IgnoreUnrecognizedVMOptions") - vmArg("-Dmixin.debug.export=true") - vmArg("-Dfabric-tag-conventions-v2.missingTagTranslationWarning=VERBOSE") - } -} - -fabricApi { - configureDataGeneration { - client = true - outputDirectory = file("src/generated/resources") - } -} - -tasks.withType(JavaCompile).configureEach { - it.options.encoding = "UTF-8" - it.options.release = 21 -} - -java { - withSourcesJar() - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 -} - - -publishing { - repositories { - } - publications { - mavenJava(MavenPublication) { - from components.java - } - } -} - -modrinth { - token = "$System.env.MODRINTH_TOKEN" - projectId = slug - versionNumber = project.version - versionType = System.getenv().getOrDefault("RELEASE_TYPE", "release") - uploadFile = remapJar - gameVersions = compatibleVersions.split(", ").toList() - loaders = compatibleLoaders.split(", ").toList() - changelog = "$System.env.CHANGELOG" - syncBodyFrom = "\n" + rootProject.file("README.md").text - dependencies { - required.version "fabric-api", libs.versions.fapi.get() - } -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..a8af21a --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,293 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.modrinth.minotaur.ModrinthExtension +import dev.architectury.plugin.ArchitectPluginExtension +import net.fabricmc.loom.api.LoomGradleExtensionAPI +import net.fabricmc.loom.task.RemapJarTask +import org.gradle.kotlin.dsl.libs + +plugins { + java + `maven-publish` + alias(libs.plugins.arch) + alias(libs.plugins.loom) apply false + alias(libs.plugins.shadow) apply false + alias(libs.plugins.minotaur) apply false +} + +println("Crystalline Sky v${"modVersion"()}") + +val isRelease = System.getenv("RELEASE_BUILD")?.toBoolean() ?: false +val buildNumber = System.getenv("GITHUB_RUN_NUMBER")?.toInt() +val inCI = buildNumber != null +val gitHash = "\"${calculateGitHash() + (if (hasUnstaged()) "-modified" else "")}\"" + +extra["inCI"] = inCI + +architectury { + minecraft = libs.versions.mc.get() +} + +allprojects { + val libs = rootProject.libs + + apply(plugin = "java") + apply(plugin = libs.plugins.arch.get().pluginId) + apply(plugin = "maven-publish") + + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } + withSourcesJar() + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + } + + base.archivesName.set("slug"()) + group = "mavenGroup"() + + // Formats the mod version to include the loader, Minecraft version, and build number (if present) + // example: 1.0.0+fabric-1.19.2-build.100 (or -local) + val build = buildNumber?.let { "-build.${it}" } ?: "-local" + + var gitBranchLabel = ""; + if (!isRelease && "modVersion"().endsWith("-alpha")) { + // gitBranchLabel should be "-" + the current git branch (replacing any slashes with underscores) + gitBranchLabel = "-" + calculateGitBranch().replace("/", "_") + } + + version = "${"modVersion"()}${gitBranchLabel}+${project.name}-mc${libs.versions.mc.get()}${if (isRelease) "" else build}" + + tasks.withType().configureEach { + options.encoding = "UTF-8" + options.release = 21 + } +} + +subprojects { + val libs = rootProject.libs + + apply(plugin = libs.plugins.loom.get().pluginId) + + setupRepositories() + + val capitalizedName = + project.name.replaceFirstChar { it.uppercase() } + + val loom = project.extensions.getByType() + loom.apply { + silentMojangMappingsLicense() + runs.configureEach { + vmArg("-XX:+AllowEnhancedClassRedefinition") + vmArg("-XX:+IgnoreUnrecognizedVMOptions") + vmArg("-Dmixin.debug.export=true") + vmArg("-Dmixin.env.remapRefMap=true") + vmArg("-Dmixin.env.refMapRemappingFile=${projectDir}/build/createSrgToMcp/output.srg") + } + } + + configurations.configureEach { + resolutionStrategy { + force(libs.fl) + } + } + + @Suppress("UnstableApiUsage") + dependencies { + "minecraft"(libs.mc) + "mappings"(loom.layered { + officialMojangMappings { nameSyntheticMembers = false } + parchment(variantOf(libs.parchment) { artifactType("zip") }) + mappings(rootProject.file("custom_mappings.tiny")) + }) + } + + // from here down is platform configuration + if(project.path == ":common") { + return@subprojects + } + + apply(plugin = libs.plugins.shadow.get().pluginId) + apply(plugin = libs.plugins.minotaur.get().pluginId) + + architectury { + platformSetupLoomIde() + } + + val common: Configuration by configurations.creating + val shadowCommon: Configuration by configurations.creating + val development = configurations.maybeCreate("development${capitalizedName}") + + configurations { + compileOnly.get().extendsFrom(common) + runtimeOnly.get().extendsFrom(common) + development.extendsFrom(common) + } + + dependencies { + common(project(":common", "namedElements")) { isTransitive = false } + shadowCommon(project(":common", "transformProduction${capitalizedName}")) { isTransitive = false } + } + + tasks.named("shadowJar") { + archiveClassifier = "dev-shadow" + configurations = listOf(shadowCommon) + exclude("architectury.common.json") + destinationDirectory = layout.buildDirectory.dir("devlibs").get() + } + + val remapJar = tasks.named("remapJar") { + from("${rootProject.projectDir}/LICENSE") + val shadowJar = project.tasks.named("shadowJar").get() + inputFile.set(shadowJar.archiveFile) + injectAccessWidener = true + dependsOn(shadowJar) + archiveClassifier = null + } + + tasks.processResources { + val authors = "authors"() + val contributors = "contributors"() + val properties: Map = mapOf( + "version" to version.toString(), + "modId" to "modId"(), + "modName" to "modName"(), + "modDescription" to "modDescription"(), + "homepage" to "https://modrinth.com/mod/${"slug"()}", + "issues" to "https://github.com/${"user"()}/${"slug"()}/issues", + "sources" to "https://github.com/${"user"()}/${"slug"()}", + "license" to "license"(), + "authors" to authors.split(", ").joinToString("\",\n \""), + "contributors" to contributors.split(", ").joinToString("\",\n \""), + "members" to "${authors}${if (contributors.isNotBlank()) ". Contributions by ${contributors}." else ""}", + "mc" to "compatibleVersions"().split(", ")[0], + "fl" to libs.versions.fl.get(), + "fapi" to libs.versions.fapi.get(), + ) + + inputs.properties(properties) + filesMatching("*.mod.json") { expand(properties) } + filesMatching("META-INF/*mods.toml") { expand(properties) } + + // don't add development or to-do files into built jar + exclude("**/*.bbmodel", "**/*.lnk", "**/*.xcf", "**/*.blend", "**/*.blend1") + } + + tasks.jar { + archiveClassifier = "dev" + + manifest { + attributes(mapOf("Git-Hash" to gitHash)) + } + } + + tasks.named("sourcesJar") { + val commonSources = project(":common").tasks.getByName("sourcesJar") + dependsOn(commonSources) + from(commonSources.archiveFile.map { zipTree(it) }) + + manifest { + attributes(mapOf("Git-Hash" to gitHash)) + } + } + + components.getByName("java") { + withVariantsFromConfiguration(project.configurations["shadowRuntimeElements"]) { + skip() + } + } + + val isFabric = project.name == "fabric" + val description = providers.fileContents { rootProject.file("README.md") }.asText.map { + "\n${it}" + } + configure { + token = providers.environmentVariable("MODRINTH_TOKEN") + projectId = "slug"() + versionNumber.set(project.version.toString()) + versionType.set(providers.environmentVariable("RELEASE_TYPE").orElse("release")) + uploadFile.set(remapJar.get().archiveFile) + gameVersions = "compatibleVersions"().split(", ").toList() + if (isFabric) { + loaders.add("fabric") + loaders.add("quilt") + } else { + loaders.add("neoforge") + } + changelog = providers.environmentVariable("CHANGELOG") + syncBodyFrom = description + dependencies { + if (isFabric) { + required.version("fabric-api", libs.versions.fapi.get()) + } + } + } +} + +fun Project.setupRepositories() { + repositories { + exclusiveMaven("https://api.modrinth.com/maven", "maven.modrinth") // Axiom, Sodium + exclusiveMaven("https://maven.terraformersmc.com/", "com.terraformersmc") // Mod Menu + exclusiveMaven("https://maven.parchmentmc.org", "org.parchmentmc.data") // Parchment Mappings + flatDir { + dir("$rootDir/libs") + } + } +} + +fun calculateGitHash(): String { + try { + val output = providers.exec { + commandLine("git", "rev-parse", "HEAD") + } + return output.standardOutput.asText.get().trim() + } catch(_: Throwable) { + return "unknown" + } +} + +fun calculateGitBranch(): String { + try { + val output = providers.exec { + commandLine("git", "rev-parse", "--abbrev-ref", "HEAD") + } + return output.standardOutput.asText.get().trim() + } catch(_: Throwable) { + return "unknown" + } +} + +fun hasUnstaged(): Boolean { + try { + val output = providers.exec { + commandLine("git", "status", "--porcelain") + } + val result = output.standardOutput.asText.get().replace(Regex("M gradlew(\\.bat)?"), "").trimEnd() + if (result.isNotEmpty()) + println("Found stageable results:\n${result}\n") + return result.isNotEmpty() + } catch(_: Throwable) { + return false + } +} + +fun Project.architectury(action: Action) { + action.execute(this.extensions.getByType()) +} + +fun RepositoryHandler.exclusiveMaven(url: String, vararg groups: String) { + exclusiveContent { + forRepository { maven(url) } + filter { + groups.forEach { + @Suppress("UnstableApiUsage") + includeGroupAndSubgroups(it) + } + } + } +} + +operator fun String.invoke(): String { + return rootProject.ext[this] as? String + ?: throw IllegalStateException("Property $this is not defined") +} diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 0000000..0dead92 --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,29 @@ +architectury { + common { + for (p in rootProject.subprojects) { + if (p != project) { + this@common.add(p.name) + } + } + } +} + +dependencies { + // We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies + // Do NOT use other classes from fabric loader + modImplementation(libs.fl) + + // required for proper remapping and compiling + modCompileOnly(libs.fapi) // TODO: try removing this and seeing if that breaks stuff + + modCompileOnly(libs.sodium.fabric) + + compileOnly(annotationProcessor(libs.mixinextras.common.get())!!)!! +} + +sourceSets.main { + resources { // include generated resources in resources + srcDir("src/generated/resources") + exclude(".cache/**") + } +} diff --git a/common/src/main/resources/META-INF/neoforge.mods.toml b/common/src/main/resources/META-INF/neoforge.mods.toml index fcfe7a4..6bede7d 100644 --- a/common/src/main/resources/META-INF/neoforge.mods.toml +++ b/common/src/main/resources/META-INF/neoforge.mods.toml @@ -12,8 +12,11 @@ authors = "${members}" displayURL = "${homepage}" logoFile = "assets/${modId}/icon.png" -[properties] -"connector:placeholder" = true +[[mixins]] +config="${modId}-common.mixins.json" + +[[mixins]] +config="${modId}.mixins.json" [[dependencies."${ modId }"]] modId = "connector" diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts new file mode 100644 index 0000000..b6153d3 --- /dev/null +++ b/fabric/build.gradle.kts @@ -0,0 +1,50 @@ +architectury.fabric() + +val inCI = rootProject.extra["inCI"] as Boolean + +loom { + val common = project(":common") + + runs { + create("datagen") { + client() + + name = "Minecraft Data" + vmArg("-Dfabric-api.data") + vmArg("-Dfabric-api.datagen.output-dir=${common.file("src/generated/resources")}") + vmArg("-Dfabric-api.datagen.modid=${"slug"()}") + + environmentVariable("DATAGEN", "TRUE") + } + } +} + +dependencies { + modImplementation(libs.fl) + modImplementation(libs.fapi) + + if (!inCI) { + modLocalRuntime(libs.mm) + } + + modCompileOnly(libs.sodium.fabric) + if (!inCI && "enable_sodium"().toBoolean()) { + modLocalRuntime(libs.sodium.fabric) + } + + modCompileOnly(libs.axiom) + if (!inCI && "enable_axiom"().toBoolean()) { + modLocalRuntime(libs.axiom) + modLocalRuntime(libs.axiom.axiomclientapi) + modLocalRuntime(libs.axiom.lattice) + modLocalRuntime(libs.axiom.mixinconstraints) + } + + compileOnly(annotationProcessor(libs.mixinextras.common.get())!!)!! + implementation(include(libs.mixinextras.fabric.get())!!)!! +} + +operator fun String.invoke(): String { + return rootProject.ext[this] as? String + ?: throw IllegalStateException("Property $this is not defined") +} diff --git a/gradle.properties b/gradle.properties index df1b54b..f239bfd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,15 +1,17 @@ # Gradle org.gradle.jvmargs=-Xmx2G -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 +# -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006 org.gradle.caching=true org.gradle.configureondemand=true # IntelliJ's run configs don't work with the configuration cache org.gradle.configuration-cache=false # Loom fabric.loom.multiProjectOptimisation=true -# otherwise layering tiny mappings on mojmap explodes -fabric.loom.dropNonIntermediateRootMethods=true +# it appears that this must be false to work with neoforge/arch loom +fabric.loom.dropNonIntermediateRootMethods=false +loom.ignoreDependencyLoomVersionValidation=true # Mod Metadata -group=io.github.slimeistdev +mavenGroup=io.github.slimeistdev user=techno-sam slug=crystalline-sky modId=crystalline_sky @@ -21,9 +23,7 @@ license=EUPL 1.2 or later # Mod Version modVersion=0.1.2 # Branch Metadata -branchName=fabric-1.21.1 compatibleVersions=1.21.1 -compatibleLoaders=fabric # mod compat enable_sodium=false diff --git a/libs.versions.toml b/libs.versions.toml index e474f0b..88f07ea 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -1,14 +1,18 @@ [versions] -loom = "1.15.+" +arch = "3.4-SNAPSHOT" +loom = "1.13.+" minotaur = "2.+" +shadow = "8.3.6" mc = "1.21.1" -fl = "0.18.6" parchment = "2024.11.17" +fl = "0.18.6" fapi = "0.116.10+1.21.1" +nf = "21.1.215" mixinextras = "0.5.0" mm = "11.0.4" -sodium = "mc1.21.1-0.6.13-fabric" +sodium-fabric = "mc1.21.1-0.6.13-fabric" +sodium-neoforge = "mc1.21.1-0.6.13-neoforge" axiom = "jTdCyf2H" # these have to be manually extracted from the jar and put in the flatDir libs folder (only if the enable_axiom property is true) axiom-axiomclientapi = "1.0.0" @@ -16,17 +20,29 @@ axiom-lattice = "1.2.12" axiom-mixinconstraints = "1.0.9" [plugins] -loom = { id = "fabric-loom", version.ref = "loom" } +arch = { id = "architectury-plugin", version.ref = "arch" } +loom = { id = "dev.architectury.loom", version.ref = "loom" } minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" } +shadow = { id = "com.gradleup.shadow", version.ref = "shadow" } [libraries] mc = { group = "mojang", name = "minecraft", version.ref = "mc" } -fl = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fl" } parchment = { group = "org.parchmentmc.data", name = "parchment-1.21.1", version.ref = "parchment" } + +fl = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fl" } fapi = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fapi" } -mixinextras = { group = "io.github.llamalad7", name = "mixinextras-fabric", version.ref = "mixinextras" } + +nf = { group = "net.neoforged", name = "neoforge", version.ref = "nf" } + +mixinextras-common = { group = "io.github.llamalad7", name = "mixinextras-common", version.ref = "mixinextras" } +mixinextras-fabric = { group = "io.github.llamalad7", name = "mixinextras-fabric", version.ref = "mixinextras" } +mixinextras-neoforge = { group = "io.github.llamalad7", name = "mixinextras-neoforge", version.ref = "mixinextras" } + mm = { group = "com.terraformersmc", name = "modmenu", version.ref = "mm" } -sodium = { group = "maven.modrinth", name = "sodium", version.ref = "sodium" } + +sodium-fabric = { group = "maven.modrinth", name = "sodium", version.ref = "sodium-fabric" } +sodium-neoforge = { group = "maven.modrinth", name = "sodium", version.ref = "sodium-neoforge" } + axiom = { group = "maven.modrinth", name = "axiom", version.ref = "axiom" } axiom-axiomclientapi = { group = "local", name = "axiomclientapi", version.ref = "axiom-axiomclientapi" } axiom-lattice = { group = "local", name = "lattice", version.ref = "axiom-lattice" } diff --git a/neoforge/build.gradle.kts b/neoforge/build.gradle.kts new file mode 100644 index 0000000..c3c16d7 --- /dev/null +++ b/neoforge/build.gradle.kts @@ -0,0 +1,36 @@ +architectury.neoForge() + +val inCI = rootProject.extra["inCI"] as Boolean + +loom { + neoForge { + runs.configureEach { + // force proper color logs + vmArg("-Dterminal.jline=true") + } + } +} + +repositories { + maven { + name = "NeoForged" + url = uri("https://maven.neoforged.net/releases") + } +} + +dependencies { + neoForge(libs.nf) + + modCompileOnly(libs.sodium.neoforge) + if (!inCI && "enable_sodium"().toBoolean()) { + modLocalRuntime(libs.sodium.neoforge) + } + + compileOnly(annotationProcessor(libs.mixinextras.common.get())!!)!! + implementation(include(libs.mixinextras.neoforge.get())!!)!! +} + +operator fun String.invoke(): String { + return rootProject.ext[this] as? String + ?: throw IllegalStateException("Property $this is not defined") +} diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties new file mode 100644 index 0000000..7da18ea --- /dev/null +++ b/neoforge/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index dce0b1d..0000000 --- a/settings.gradle +++ /dev/null @@ -1,14 +0,0 @@ -pluginManagement { - repositories { - maven { url "https://maven.fabricmc.net/" } - gradlePluginPortal() - } -} - -dependencyResolutionManagement { - versionCatalogs { - create("libs") { - from(files("libs.versions.toml")) - } - } -} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..71b4674 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,22 @@ +pluginManagement { + repositories { + maven { url = uri("https://maven.fabricmc.net/") } + maven { url = uri("https://maven.architectury.dev") } + maven { url = uri("https://maven.neoforged.net/releases") } + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("libs.versions.toml")) + } + } +} + +include("common") +include("fabric") +include("neoforge") + +rootProject.name = "crystalline-sky"