Skip to content

Commit 3a41010

Browse files
Add NeoForge support
1 parent f99f407 commit 3a41010

File tree

59 files changed

+796
-71
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+796
-71
lines changed

build.gradle.kts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ allprojects {
2323
}
2424
maven("https://repo.spongepowered.org/repository/maven-public/")
2525
maven("https://maven.terraformersmc.com/releases") // for ModMenu
26+
27+
maven("https://maven.neoforged.net/releases")
28+
maven("https://thedarkcolour.github.io/KotlinForForge/")
2629
}
2730
}
2831

@@ -35,7 +38,7 @@ commonConfig {
3538
}
3639
val mod_version: String by project
3740
version = snapshotVersion ?: mod_version
38-
platforms = listOf("fabric")
41+
platforms = listOf("fabric", "neoforge")
3942

4043
modules {
4144
subprojects

buildSrc/src/main/kotlin/GradleTypeAccess.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ typealias ModPublishingPlugin = com.teamwizardry.gradle.publish.ModPublishingPlu
1818
typealias ModPublishingExtension = com.teamwizardry.gradle.publish.ModPublishingExtension
1919

2020
typealias GenerateFabricModJson = com.teamwizardry.gradle.task.GenerateFabricModJson
21+
typealias GenerateNeoForgeModsToml = com.teamwizardry.gradle.task.GenerateNeoForgeModsToml
2122
typealias ShadowSources = com.teamwizardry.gradle.task.ShadowSources
2223
typealias CopyFreemarker = com.teamwizardry.gradle.task.CopyFreemarker
2324
typealias ReplaceTextInPlace = com.teamwizardry.gradle.task.ReplaceTextInPlace
@@ -34,8 +35,8 @@ fun Project.configureFabricModJson(block: GenerateFabricModJson.() -> Unit) {
3435
this.tasks.named("generateFabricMod", block)
3536
}
3637

37-
fun Project.configureFabricTestModJson(block: GenerateFabricModJson.() -> Unit) {
38-
this.tasks.named("generateFabricTestMod", block)
38+
fun Project.configureNeoForgeModsToml(block: GenerateNeoForgeModsToml.() -> Unit) {
39+
this.tasks.named("generateNeoForgeMod", block)
3940
}
4041

4142
val Project.generatedResourcesDir get() = this.layout.buildDirectory.dir("generated/main/resources")

buildSrc/src/main/kotlin/com/teamwizardry/gradle/ModuleInfo.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ open class ModuleInfo(val name: String) {
66
val path: String = ":$name"
77
val commonPath = "$path:common"
88
val fabricPath = "$path:fabric"
9+
val neoForgePath = "$path:neoforge"
910
val testModPath = "$path:testmod"
1011

1112
/**

buildSrc/src/main/kotlin/com/teamwizardry/gradle/module/ModuleExtension.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ open class ModuleExtension(private val ctx: DslContext) {
2727
val path get() = moduleInfo.path
2828
val commonPath get() = moduleInfo.commonPath
2929
val fabricPath get() = moduleInfo.fabricPath
30+
val neoForgePath get() = moduleInfo.neoForgePath
3031
val testModPath get() = moduleInfo.testModPath
3132

3233
/**
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
@file:Suppress("UnstableApiUsage")
2+
3+
package com.teamwizardry.gradle.task
4+
5+
import org.gradle.api.DefaultTask
6+
import org.gradle.api.provider.Property
7+
import org.gradle.api.tasks.*
8+
import java.io.File
9+
10+
import com.teamwizardry.gradle.util.DslContext
11+
import org.gradle.api.file.FileCollection
12+
import org.gradle.api.provider.ListProperty
13+
14+
open class GenerateNeoForgeModsToml : DefaultTask() {
15+
private val ctx = DslContext(project)
16+
17+
/**
18+
* The root resources directory (e.g. `$buildDir/generated/main/resources`)
19+
*/
20+
@Input val outputRoot: Property<File> = ctx.property()
21+
22+
@Input val modLoader: Property<String> = ctx.property { "javafml" }
23+
@Input val loaderVersion: Property<String> = ctx.property { "2" }
24+
@Input val license: Property<String> = ctx.property()
25+
@Optional @Input val issueTrackerURL: Property<String> = ctx.property()
26+
@Nested val mods: ListProperty<ModInfo> = ctx.listProperty { listOf() }
27+
@Input val mixins: ListProperty<String> = ctx.listProperty { listOf() }
28+
@Input val accessTransformers: ListProperty<String> = ctx.listProperty { listOf() }
29+
30+
fun mod(block: ModInfo.() -> Unit) {
31+
mods.add(ModInfo().apply(block))
32+
}
33+
34+
fun mixin(path: String) {
35+
mixins.add(path)
36+
}
37+
38+
fun accessTransformer(path: String) {
39+
accessTransformers.add(path)
40+
}
41+
42+
inner class ModInfo {
43+
@Input val modId: Property<String> = ctx.property()
44+
@Input val version: Property<String> = ctx.property()
45+
@Input val displayName: Property<String> = ctx.property()
46+
@Input val description: Property<String> = ctx.property()
47+
@Optional @Input val updateJSONURL: Property<String> = ctx.property()
48+
@Optional @Input val displayURL: Property<String> = ctx.property()
49+
@Optional @Input val logoFileName: Property<String> = ctx.property { "${modId.get()}-logo.png" }
50+
@Optional @InputFile val logoFile: Property<File> = ctx.property()
51+
@Optional @Input val credits: Property<String> = ctx.property()
52+
@Optional @Input val authors: Property<String> = ctx.property()
53+
@Nested val dependencies: ListProperty<Dependency> = ctx.listProperty { listOf() }
54+
55+
fun dependency(
56+
modId: String,
57+
versionRange: String,
58+
type: DependencyType = DependencyType.REQUIRED,
59+
block: Dependency.() -> Unit = {}
60+
) {
61+
dependencies.add(Dependency().also {
62+
it.modId.set(modId)
63+
it.type.set(type)
64+
it.versionRange.set(versionRange)
65+
}.apply(block))
66+
}
67+
68+
inner class Dependency {
69+
@Input val modId: Property<String> = ctx.property()
70+
@Input val type: Property<DependencyType> = ctx.property()
71+
@Input val versionRange: Property<String> = ctx.property()
72+
@Optional @Input val ordering: Property<DependencyOrdering> = ctx.property(DependencyOrdering.NONE)
73+
@Optional @Input val side: Property<DependencySide> = ctx.property(DependencySide.BOTH)
74+
}
75+
}
76+
77+
enum class DependencyType(val tomlValue: String) {
78+
REQUIRED("required"),
79+
OPTIONAL("optional"),
80+
INCOMPATIBLE("incompatible"),
81+
DISCOURAGED("discouraged"),
82+
}
83+
84+
enum class DependencyOrdering(val tomlValue: String) {
85+
BEFORE("BEFORE"),
86+
NONE("NONE"),
87+
AFTER("AFTER"),
88+
}
89+
90+
91+
enum class DependencySide(val tomlValue: String) {
92+
BOTH("BOTH"),
93+
CLIENT("CLIENT"),
94+
SERVER("SERVER"),
95+
}
96+
97+
@get:OutputFiles
98+
protected val outputFile: FileCollection
99+
get() {
100+
val root = outputRoot.get()
101+
val files = ctx.project.files()
102+
files.from(root.resolve("META-INF/neoforge.mods.toml"))
103+
for (mod in mods.get()) {
104+
if (mod.logoFile.isPresent) {
105+
files.from(root.resolve(mod.logoFileName.get()))
106+
}
107+
}
108+
return files
109+
}
110+
111+
@TaskAction
112+
private fun runTask() {
113+
val root = outputRoot.get()
114+
root.resolve("META-INF").mkdirs()
115+
root.resolve("META-INF/neoforge.mods.toml").writeText(makeToml())
116+
for (mod in mods.get()) {
117+
mod.logoFile.orNull?.also { logo ->
118+
val dest = root.resolve(mod.logoFileName.get())
119+
dest.parentFile.mkdirs()
120+
logo.copyTo(dest, overwrite = true)
121+
}
122+
}
123+
}
124+
125+
private fun makeToml(): String = buildString {
126+
tomlValue("modLoader", modLoader.get())
127+
tomlValue("loaderVersion", loaderVersion.get())
128+
tomlValue("license", license.get())
129+
tomlValue("issueTrackerURL", issueTrackerURL.orNull)
130+
131+
for (mod in mods.get()) {
132+
val modId = mod.modId.get()
133+
appendLine("[[mods]]")
134+
tomlValue("modId", modId)
135+
tomlValue("version", mod.version.get())
136+
tomlValue("displayName", mod.displayName.get())
137+
tomlValue("description", mod.description.get())
138+
tomlValue("updateJSONURL", mod.updateJSONURL.orNull)
139+
tomlValue("displayURL", mod.displayURL.orNull)
140+
if (mod.logoFile.isPresent) tomlValue("logoFile", mod.logoFileName.get())
141+
tomlValue("credits", mod.credits.orNull)
142+
tomlValue("authors", mod.authors.orNull)
143+
144+
for (dependency in mod.dependencies.get()) {
145+
appendLine("[[dependencies.${modId}]]")
146+
tomlValue("modId", dependency.modId.get())
147+
tomlValue("type", dependency.type.get().tomlValue)
148+
tomlValue("versionRange", dependency.versionRange.get())
149+
tomlValue("ordering", dependency.ordering.get().tomlValue)
150+
tomlValue("side", dependency.side.get().tomlValue)
151+
}
152+
}
153+
154+
for (mixin in mixins.get()) {
155+
appendLine("[[mixins]]")
156+
tomlValue("config", mixin)
157+
}
158+
159+
for (accessTransformer in accessTransformers.get()) {
160+
appendLine("[[accessTransformers]]")
161+
tomlValue("file", accessTransformer)
162+
}
163+
}
164+
165+
private fun StringBuilder.tomlValue(key: String, value: String?) {
166+
if (value == null) return
167+
if (value.contains("\n")) {
168+
appendLine("$key=\"\"\"$value\"\"\"")
169+
} else {
170+
appendLine("$key=\"${value.replace("\"", "\\\"")}\"")
171+
}
172+
}
173+
}

buildSrc/src/main/kotlin/liblib-module-fabric.gradle.kts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,19 +84,19 @@ val generateFabricMod = tasks.register<GenerateFabricModJson>("generateFabricMod
8484
icon.set("ll/icon.png")
8585
iconFile.set(rootDir.resolve("logo/icon.png"))
8686

87-
depends("fabric-api", project.property("mod.dependencies.fabricapi") as String)
88-
depends("fabricloader", project.property("mod.dependencies.fabricloader") as String)
89-
depends("minecraft", project.property("mod.dependencies.minecraft") as String)
90-
depends("fabric-language-kotlin", project.property("mod.dependencies.flk") as String)
87+
depends("fabric-api", project.property("fabric.dependencies.fabricapi") as String)
88+
depends("fabricloader", project.property("fabric.dependencies.fabricloader") as String)
89+
depends("minecraft", project.property("fabric.dependencies.minecraft") as String)
90+
depends("fabric-language-kotlin", project.property("fabric.dependencies.flk") as String)
9191

9292
module.moduleInfo.dependencies {
9393
depends(it.modid, commonConfig.version)
9494
}
9595
modMenu.badges.add("library")
9696
modMenu.parent(
9797
id = "librarianlib",
98-
name = project.property("mod.modmenu.liblib_name") as String,
99-
description = project.property("mod.modmenu.liblib_description") as String,
98+
name = project.property("fabric.modmenu.liblib_name") as String,
99+
description = project.property("fabric.modmenu.liblib_description") as String,
100100
badges = listOf("library")
101101
)
102102
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
2+
import com.github.jengelman.gradle.plugins.shadow.transformers.DontIncludeResourceTransformer
3+
import net.fabricmc.loom.task.RemapJarTask
4+
import org.gradle.kotlin.dsl.named
5+
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
6+
7+
plugins {
8+
id("liblib-shared-langs")
9+
id("liblib-shared-loom")
10+
id("com.github.johnrengelman.shadow")
11+
}
12+
13+
if (project.findProperty("loom.platform") != "neoforge") {
14+
throw IllegalStateException("NeoForge modules must have `loom.platform=neoforge` in their gradle.properties file")
15+
}
16+
17+
val module = parent!!.extensions.getByType<ModuleExtension>()
18+
19+
configure<KotlinProjectExtension> {
20+
explicitApi()
21+
}
22+
23+
architectury {
24+
neoForge()
25+
}
26+
27+
loom {
28+
// mixin.defaultRefmapName.set("ll/${module.name}/${module.name}-neoforge-refmap.json")
29+
}
30+
31+
configurations {
32+
// Files in this configuration will be bundled into your mod using the Shadow plugin.
33+
// Don't use the `shadow` configuration from the plugin itself as it's meant for excluding files.
34+
create("shadowBundle") {
35+
canBe(consumed = false, resolved = true)
36+
}
37+
create("devRuntime") {
38+
canBe(consumed = true, resolved = false)
39+
}
40+
}
41+
42+
dependencies {
43+
"devRuntime"(sourceSets.main.get().output)
44+
45+
"neoForge"("net.neoforged:neoforge:${rootProject.property("neoforge_version")}")
46+
47+
api(project(path = module.commonPath, configuration = "namedElements"))
48+
module.dependencies {
49+
api(project(it.neoForgePath, configuration = "namedElements"))
50+
}
51+
52+
modApi(project(path = module.path, configuration = "includeNeoForge"))
53+
include(project(path = module.path, configuration = "includeNeoForge"))
54+
55+
"shadowBundle"(project(path = module.commonPath, configuration = "transformProductionNeoForge"))
56+
"shadowBundle"(project(path = module.path, configuration = "shade"))
57+
"shadowBundle"(project(path = module.path, configuration = "transitiveShade"))
58+
}
59+
60+
val shadowJar = tasks.named<ShadowJar>("shadowJar") {
61+
configurations = listOf(project.configurations.getByName("shadowBundle"))
62+
archiveClassifier = "dev-shadow"
63+
64+
// The common module needs a `fabric.mod.json` file for assets to load from it. Don't include it in the shadow jar.
65+
// (classpath entries without a mod json aren't treated like resource packs by fabric)
66+
transform(DontIncludeResourceTransformer::class.java) { resource = "fabric.mod.json" }
67+
68+
commonConfig.shadowRules {
69+
relocate(it.from, it.to)
70+
}
71+
}
72+
73+
tasks.named<RemapJarTask>("remapJar") {
74+
dependsOn(shadowJar)
75+
inputFile.set(shadowJar.map { it.archiveFile.get() })
76+
}
77+
78+
val generateNeoForgeMod = tasks.register<GenerateNeoForgeModsToml>("generateNeoForgeMod") {
79+
outputRoot.set(generatedResourcesDir.map { it.asFile })
80+
81+
// We don't use KFF for its mod loader, only the kotlin stdlib
82+
// modLoader.set("kotlinforforge")
83+
license.set("LGPL-3.0")
84+
85+
mod {
86+
modId.set(module.moduleInfo.modid)
87+
version.set(commonConfig.version)
88+
displayName.set(provider { module.modName })
89+
description.set(provider { module.description })
90+
displayURL.set("https://github.com/TeamWizardry/LibrarianLib")
91+
logoFile.set(rootDir.resolve("logo/icon.png"))
92+
logoFileName.set("ll/icon.png")
93+
// credits.set("")
94+
// authors.set("")
95+
96+
dependency(
97+
"neoforge",
98+
project.property("neoforge.dependencies.neoforge") as String
99+
)
100+
dependency(
101+
"minecraft",
102+
project.property("neoforge.dependencies.minecraft") as String
103+
)
104+
dependency(
105+
"kotlinforforge",
106+
project.property("neoforge.dependencies.kotlinforforge") as String
107+
)
108+
109+
module.moduleInfo.dependencies {
110+
dependency(it.modid, commonConfig.version)
111+
}
112+
}
113+
}
114+
115+
tasks.named<ProcessResources>("processResources") {
116+
dependsOn(generateNeoForgeMod)
117+
}

buildSrc/src/main/kotlin/liblib-module-root.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,8 @@ configurations {
2222
description = "Fabric mods to be included as jar-in-jar deps"
2323
canBe(consumed = true, resolved = false)
2424
}
25+
create("includeNeoForge") {
26+
description = "NeoForge mods to be included as jar-in-jar deps"
27+
canBe(consumed = true, resolved = false)
28+
}
2529
}

0 commit comments

Comments
 (0)