From fd527cff6b0d5043906ccc656e2225fc05b41583 Mon Sep 17 00:00:00 2001 From: LambdAurora Date: Mon, 25 Nov 2024 22:25:24 +0100 Subject: [PATCH] Add generation of neoforge.mod.toml for API. --- api/build.gradle.kts | 25 ++++ api/moj_xplat/build.gradle.kts | 4 +- .../java/lambdynamiclights/data/Contact.java | 43 ++++++ .../main/java/lambdynamiclights/data/Fmj.java | 54 +------ .../java/lambdynamiclights/data/ModBase.java | 63 +++++--- .../java/lambdynamiclights/data/ModShell.java | 50 +++++++ .../main/java/lambdynamiclights/data/Nmt.java | 136 ++++++++++++++++++ .../main/kotlin/lambdynamiclights.gradle.kts | 2 +- .../lambdynamiclights/task/GenerateNmtTask.kt | 38 +++++ 9 files changed, 337 insertions(+), 78 deletions(-) create mode 100644 build_logic/src/main/java/lambdynamiclights/data/Contact.java create mode 100644 build_logic/src/main/java/lambdynamiclights/data/ModShell.java create mode 100644 build_logic/src/main/java/lambdynamiclights/data/Nmt.java create mode 100644 build_logic/src/main/kotlin/lambdynamiclights/task/GenerateNmtTask.kt diff --git a/api/build.gradle.kts b/api/build.gradle.kts index c958f804..27048c8a 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,4 +1,6 @@ import lambdynamiclights.Constants +import lambdynamiclights.data.Nmt +import lambdynamiclights.task.GenerateNmtTask plugins { id("lambdynamiclights") @@ -22,6 +24,29 @@ tasks.generateFmj.configure { } } +val generateNmt = tasks.register("generateNmt", GenerateNmtTask::class) { + this.nmt.set(tasks.generateFmj.flatMap { + it.fmj.map { fmj -> + fmj.derive(::Nmt) + .withLoaderVersion("[2,)") + .withDepend("minecraft", "[" + libs.versions.minecraft.get() + ",)") + } + }) + outputDir.set(project.file("build/generated/generated_resources/")) +} + +tasks.generateFmj.configure { + dependsOn(generateNmt) +} + +tasks.ideaSyncTask.configure { + dependsOn(generateNmt) +} + +tasks.getByName("sourcesJar") { + dependsOn(generateNmt) +} + val mojmap by sourceSets.creating {} java { diff --git a/api/moj_xplat/build.gradle.kts b/api/moj_xplat/build.gradle.kts index 368e2f60..de3feb1d 100644 --- a/api/moj_xplat/build.gradle.kts +++ b/api/moj_xplat/build.gradle.kts @@ -40,7 +40,7 @@ apiProject.artifacts.add("mojmapRuntimeElements", tasks.remapJar) { classifier = "mojmap" } -val remapMojmapSourcesTask = tasks.register("remapMojmapSourcesJar", RemapSourcesJarTask::class) { +tasks.remapSourcesJar { val remapJar = apiProject.tasks.named("remapSourcesJar", RemapSourcesJarTask::class) dependsOn(remapJar) @@ -55,6 +55,6 @@ val remapMojmapSourcesTask = tasks.register("remapMojmapSourcesJar", RemapSource apiProject.configurations["mojmapSourcesElements"].artifacts.removeIf { true } -apiProject.artifacts.add("mojmapSourcesElements", remapMojmapSourcesTask) { +apiProject.artifacts.add("mojmapSourcesElements", tasks.remapSourcesJar) { classifier = "mojmap-sources" } diff --git a/build_logic/src/main/java/lambdynamiclights/data/Contact.java b/build_logic/src/main/java/lambdynamiclights/data/Contact.java new file mode 100644 index 00000000..c33de93d --- /dev/null +++ b/build_logic/src/main/java/lambdynamiclights/data/Contact.java @@ -0,0 +1,43 @@ +package lambdynamiclights.data; + +import java.io.Serializable; + +public final class Contact implements Serializable { + private String homepage; + private String sources; + private String issues; + + public Contact withHomepage(String homepage) { + this.homepage = homepage; + return this; + } + + public Contact withSources(String sources) { + this.sources = sources; + return this; + } + + public Contact withIssues(String issues) { + this.issues = issues; + return this; + } + + public String homepage() { + return this.homepage; + } + + public String sources() { + return this.sources; + } + + public String issues() { + return this.issues; + } + + public Contact copy() { + return new Contact() + .withHomepage(this.homepage) + .withSources(this.sources) + .withIssues(this.issues); + } +} diff --git a/build_logic/src/main/java/lambdynamiclights/data/Fmj.java b/build_logic/src/main/java/lambdynamiclights/data/Fmj.java index 59eebf8f..7a49ac2e 100644 --- a/build_logic/src/main/java/lambdynamiclights/data/Fmj.java +++ b/build_logic/src/main/java/lambdynamiclights/data/Fmj.java @@ -11,10 +11,6 @@ import java.util.function.Consumer; public final class Fmj extends ModBase { - private final String version; - private final List authors = new ArrayList<>(); - private Contact contact; - private String license; private String environment; private final Map> entrypoints = new LinkedHashMap<>(); private String accessWidener; @@ -25,32 +21,7 @@ public final class Fmj extends ModBase { private final Map custom = new LinkedHashMap<>(); public Fmj(String namespace, String name, String version) { - super(namespace, name); - this.version = version; - } - - public Fmj withAuthors(List authors) { - this.authors.addAll(authors); - return this; - } - - public Fmj withAuthors(String... authors) { - return this.withAuthors(Arrays.asList(authors)); - } - - private Contact useContact() { - if (this.contact == null) this.contact = new Contact(); - return this.contact; - } - - public Fmj withContact(Consumer action) { - action.accept(this.useContact()); - return this; - } - - public Fmj withLicense(String license) { - this.license = license; - return this; + super(namespace, name, version); } public Fmj withEnvironment(String environment) { @@ -98,27 +69,6 @@ public Fmj withModMenu(Consumer action) { return this; } - public static final class Contact implements Serializable { - private String homepage; - private String sources; - private String issues; - - public Contact withHomepage(String homepage) { - this.homepage = homepage; - return this; - } - - public Contact withSources(String sources) { - this.sources = sources; - return this; - } - - public Contact withIssues(String issues) { - this.issues = issues; - return this; - } - } - public static final class ModMenu implements Serializable { private Map links; private List badges; @@ -155,7 +105,7 @@ public ModMenu withParent(String namespace, String name, Consumer act return this.withParent(mod); } - public static final class ParentMod extends ModBase { + public static final class ParentMod extends ModShell { private List badges; public ParentMod(String namespace, String name) { diff --git a/build_logic/src/main/java/lambdynamiclights/data/ModBase.java b/build_logic/src/main/java/lambdynamiclights/data/ModBase.java index 06d1ffc9..37533fc2 100644 --- a/build_logic/src/main/java/lambdynamiclights/data/ModBase.java +++ b/build_logic/src/main/java/lambdynamiclights/data/ModBase.java @@ -1,19 +1,19 @@ package lambdynamiclights.data; -import com.google.gson.annotations.SerializedName; - -import java.io.Serializable; - -public class ModBase> implements Serializable { - @SerializedName("id") - protected String namespace; - protected String name; - protected String description; - protected String icon; - - public ModBase(String namespace, String name) { - this.namespace = namespace; - this.name = name; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +public class ModBase> extends ModShell { + protected final String version; + protected final List authors = new ArrayList<>(); + protected Contact contact; + protected String license; + + public ModBase(String namespace, String name, String version) { + super(namespace, name); + this.version = version; } @SuppressWarnings("unchecked") @@ -21,23 +21,40 @@ public ModBase(String namespace, String name) { return (SELF) this; } - public SELF withNamespace(String namespace) { - this.namespace = namespace; + public SELF withAuthors(List authors) { + this.authors.addAll(authors); return this.$self(); } - public SELF withName(String name) { - this.name = name; - return this.$self(); + public SELF withAuthors(String... authors) { + return this.withAuthors(Arrays.asList(authors)); + } + + private Contact useContact() { + if (this.contact == null) this.contact = new Contact(); + return this.contact; } - public SELF withDescription(String description) { - this.description = description; + public SELF withContact(Consumer action) { + action.accept(this.useContact()); return this.$self(); } - public SELF withIcon(String icon) { - this.icon = icon; + public SELF withLicense(String license) { + this.license = license; return this.$self(); } + + public > VARIANT derive(ModBaseFactory factory) { + var variant = factory.create(this.namespace, this.name, this.version); + this.copyTo(variant); + variant.authors.addAll(this.authors); + variant.contact = this.contact != null ? this.contact.copy() : null; + variant.license = this.license; + return variant; + } + + public interface ModBaseFactory> { + SELF create(String namespace, String name, String version); + } } diff --git a/build_logic/src/main/java/lambdynamiclights/data/ModShell.java b/build_logic/src/main/java/lambdynamiclights/data/ModShell.java new file mode 100644 index 00000000..43dd3716 --- /dev/null +++ b/build_logic/src/main/java/lambdynamiclights/data/ModShell.java @@ -0,0 +1,50 @@ +package lambdynamiclights.data; + +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; + +public class ModShell> implements Serializable { + @SerializedName("id") + protected String namespace; + protected String name; + protected String description; + protected String icon; + + public ModShell(String namespace, String name) { + this.namespace = namespace; + this.name = name; + } + + @SuppressWarnings("unchecked") + private SELF $self() { + return (SELF) this; + } + + public SELF withNamespace(String namespace) { + this.namespace = namespace; + return this.$self(); + } + + public SELF withName(String name) { + this.name = name; + return this.$self(); + } + + public SELF withDescription(String description) { + this.description = description; + return this.$self(); + } + + public SELF withIcon(String icon) { + this.icon = icon; + return this.$self(); + } + + public void copyTo(ModShell target) { + target.namespace = this.namespace; + target.name = this.name; + target.description = this.description; + target.icon = this.icon; + } +} diff --git a/build_logic/src/main/java/lambdynamiclights/data/Nmt.java b/build_logic/src/main/java/lambdynamiclights/data/Nmt.java new file mode 100644 index 00000000..69822bfe --- /dev/null +++ b/build_logic/src/main/java/lambdynamiclights/data/Nmt.java @@ -0,0 +1,136 @@ +package lambdynamiclights.data; + +import java.util.LinkedHashMap; +import java.util.Map; + +public final class Nmt extends ModBase { + private String loaderVersion; + private final Map depends = new LinkedHashMap<>(); + private final Map custom = new LinkedHashMap<>(); + + public Nmt(String namespace, String name, String version) { + super(namespace, name, version); + } + + public Nmt withLoaderVersion(String loaderVersion) { + this.loaderVersion = loaderVersion; + return this; + } + + public Nmt withDepend(String dependency, String constraint) { + this.depends.put(dependency, constraint); + return this; + } + + public Nmt withCustom(String key, Object value) { + this.custom.put(key, value); + return this; + } + + public String toToml() { + var builder = new TomlBuilder(); + + builder + .property("modLoader", "javafml") + .property("loaderVersion", this.loaderVersion) + .property("license", this.license); + + if (this.contact != null && this.contact.issues() != null) { + builder.property("issueTrackerURL", this.contact.issues()); + } + + { + builder.startArray("mods") + .property("modId", this.namespace) + .property("version", this.version) + .property("displayName", this.name) + .property("description", this.description) + .property("authors", String.join(", ", this.authors)); + + if (this.icon != null) { + builder.property("logoFile", this.icon); + builder.property("logoBlur", false); + } + + if (this.contact != null) { + if (this.contact.homepage() != null) builder.property("displayURL", this.contact.homepage()); + if (this.contact.issues() != null) builder.property("issueTrackerURL", this.contact.issues()); + } + + builder.endSection(); + } + + this.depends.forEach((id, constraint) -> { + builder.startArray("dependencies." + this.namespace); + builder.property("modId", id); + builder.property("type", "required"); + builder.property("versionRange", constraint); + builder.property("ordering", "NONE"); + builder.property("side", "BOTH"); + builder.endSection(); + }); + + if (this.icon != null || !this.custom.isEmpty()) { + builder.startSection("modproperties." + this.namespace); + + if (this.icon != null) { + builder.property("catalogueImageIcon", this.icon); + } + + this.custom.forEach((key, value) -> builder.property(key, value.toString())); + + builder.endSection(); + } + + return builder.toString(); + } + + private static class TomlBuilder { + private final StringBuilder builder = new StringBuilder(); + private int indent = 0; + + private void useIndent() { + if (this.indent > 0) { + this.builder.append("\t".repeat(this.indent)); + } + } + + TomlBuilder property(String key, String value) { + this.useIndent(); + this.builder.append(key).append(" = \"").append(value).append("\"\n"); + return this; + } + + TomlBuilder property(String key, boolean value) { + this.useIndent(); + this.builder.append(key).append(" = ").append(value).append("\n"); + return this; + } + + TomlBuilder startSection(String section) { + this.builder.append('\n'); + this.useIndent(); + this.builder.append('[').append(section).append("]\n"); + this.indent++; + return this; + } + + TomlBuilder startArray(String section) { + this.builder.append('\n'); + this.useIndent(); + this.builder.append("[[").append(section).append("]]\n"); + this.indent++; + return this; + } + + TomlBuilder endSection() { + this.indent--; + return this; + } + + @Override + public String toString() { + return this.builder.toString(); + } + } +} diff --git a/build_logic/src/main/kotlin/lambdynamiclights.gradle.kts b/build_logic/src/main/kotlin/lambdynamiclights.gradle.kts index 12a85798..8e00e8a6 100644 --- a/build_logic/src/main/kotlin/lambdynamiclights.gradle.kts +++ b/build_logic/src/main/kotlin/lambdynamiclights.gradle.kts @@ -30,7 +30,7 @@ val generateFmj = tasks.register("generateFmj", GenerateFmjTask::class) { .withIcon("assets/${Constants.NAMESPACE}/icon.png") .withEnvironment("client") .withDepend("fabricloader", ">=${libs.versions.fabric.loader.get()}") - .withDepend("minecraft", "~1.21 >=1.21.2-") + .withDepend("minecraft", "~1.21.2 >=1.21.2-") .withDepend("java", ">=${Constants.JAVA_VERSION}") .withModMenu { it.withLink("modmenu.curseforge", "https://www.curseforge.com/minecraft/mc-mods/lambdynamiclights") diff --git a/build_logic/src/main/kotlin/lambdynamiclights/task/GenerateNmtTask.kt b/build_logic/src/main/kotlin/lambdynamiclights/task/GenerateNmtTask.kt new file mode 100644 index 00000000..1932e537 --- /dev/null +++ b/build_logic/src/main/kotlin/lambdynamiclights/task/GenerateNmtTask.kt @@ -0,0 +1,38 @@ +package lambdynamiclights.task + +import lambdynamiclights.data.Nmt +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import java.io.IOException +import java.nio.file.Files +import javax.inject.Inject + +abstract class GenerateNmtTask @Inject constructor() : DefaultTask() { + @get:Input + abstract val nmt: Property + + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + init { + this.group = "generation" + } + + @TaskAction + @Throws(IOException::class) + fun generateManifest() { + val metaInfDir = this.outputDir.asFile.get().toPath().resolve("META-INF") + val output = metaInfDir.resolve("neoforge.mods.toml") + + Files.createDirectories(metaInfDir) + if (Files.exists(output)) { + Files.delete(output) + } + + Files.writeString(output, this.nmt.get().toToml()) + } +}