diff --git a/.gitignore b/.gitignore index f35ca9e..58d8ad9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ +.kotlin/ ### IntelliJ IDEA ### .idea/ @@ -36,4 +37,4 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store diff --git a/conventions/build.gradle.kts b/conventions/build.gradle.kts index e6fd7a4..ff1f78b 100644 --- a/conventions/build.gradle.kts +++ b/conventions/build.gradle.kts @@ -7,7 +7,7 @@ plugins { group = "de.siphalor.tweed5" dependencies { - implementation(project(":helpers")) + implementation(project(":tweed5-conventions-helpers")) implementation(pluginMarker(libs.plugins.lombok)) implementation(pluginMarker(libs.plugins.shadow)) } diff --git a/conventions/helpers/build.gradle.kts b/conventions/helpers/build.gradle.kts index ab0830d..5bbff41 100644 --- a/conventions/helpers/build.gradle.kts +++ b/conventions/helpers/build.gradle.kts @@ -3,6 +3,8 @@ plugins { `java-gradle-plugin` } +group = "de.siphalor.tweed5" + gradlePlugin { plugins.register("minecraftModComponent") { id = "de.siphalor.tweed5.minecraft.mod.component" diff --git a/conventions/helpers/src/main/kotlin/de/siphalor/tweed5/gradle/plugin/minecraft/mod/MinecraftModComponentPlugin.kt b/conventions/helpers/src/main/kotlin/de/siphalor/tweed5/gradle/plugin/minecraft/mod/MinecraftModComponentPlugin.kt index b2b545f..d1dd6f0 100644 --- a/conventions/helpers/src/main/kotlin/de/siphalor/tweed5/gradle/plugin/minecraft/mod/MinecraftModComponentPlugin.kt +++ b/conventions/helpers/src/main/kotlin/de/siphalor/tweed5/gradle/plugin/minecraft/mod/MinecraftModComponentPlugin.kt @@ -43,6 +43,20 @@ abstract class MinecraftModComponentPlugin : Plugin { } } + val modApiElementsConfiguration = project.configurations.consumable("minecraftModApiElements") { + attributes { + attribute(MinecraftModded.MINECRAFT_MODDED_ATTRIBUTE, objectFactory.named(MinecraftModded.MODDED)) + attribute(Category.CATEGORY_ATTRIBUTE, objectFactory.named(Category.LIBRARY)) + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objectFactory.named(LibraryElements.JAR)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objectFactory.named(Bundling.SHADOWED)) + attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.JAVA_API)) + + project.afterEvaluate { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion.get().toInt()) + } + } + } + val apiConfiguration = project.configurations.named("api") val modSourcesElementsConfiguration = project.configurations.consumable("minecraftModSourcesElements") { extendsFrom(apiConfiguration.get()) @@ -57,6 +71,9 @@ abstract class MinecraftModComponentPlugin : Plugin { modComponent.addVariantsFromConfiguration(modElementsConfiguration.get()) { mapToMavenScope("runtime") } + modComponent.addVariantsFromConfiguration(modApiElementsConfiguration.get()) { + mapToMavenScope("compile") + } modComponent.addVariantsFromConfiguration(modSourcesElementsConfiguration.get()) { mapToOptional() } diff --git a/conventions/settings.gradle.kts b/conventions/settings.gradle.kts index 463bc98..e8785e1 100644 --- a/conventions/settings.gradle.kts +++ b/conventions/settings.gradle.kts @@ -17,6 +17,7 @@ dependencyResolutionManagement { } } -include("helpers") +include(":tweed5-conventions-helpers") +project(":tweed5-conventions-helpers").projectDir = file("helpers") rootProject.name = "tweed5-conventions" diff --git a/conventions/src/main/kotlin/de.siphalor.tweed5.base-module.gradle.kts b/conventions/src/main/kotlin/de.siphalor.tweed5.base-module.gradle.kts index 7002017..d322b29 100644 --- a/conventions/src/main/kotlin/de.siphalor.tweed5.base-module.gradle.kts +++ b/conventions/src/main/kotlin/de.siphalor.tweed5.base-module.gradle.kts @@ -9,8 +9,8 @@ plugins { } java { - sourceCompatibility = JavaVersion.toVersion(libs.versions.java.main.get()) - targetCompatibility = JavaVersion.toVersion(libs.versions.java.main.get()) + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } val testAgent = configurations.dependencyScope("mockitoAgent") diff --git a/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.base.gradle.kts b/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.base.gradle.kts index 8592bdf..f8b526a 100644 --- a/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.base.gradle.kts +++ b/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.base.gradle.kts @@ -6,14 +6,40 @@ plugins { id("de.siphalor.tweed5.minecraft.mod.component") } +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + tasks.shadowJar { relocate("org.apache.commons", "de.siphalor.tweed5.shadowed.org.apache.commons") } +val processMinecraftModResources = tasks.register("processMinecraftModResources") { + inputs.property("id", project.name) + inputs.property("version", project.version) + inputs.property("name", properties["module.name"]) + inputs.property("description", properties["module.description"]) + inputs.property("repoUrl", properties["git.url"]) + + from(project.layout.settingsDirectory.dir("../tweed5-minecraft/mod-template/resources")) + expand(mapOf( + "id" to project.name.replace('-', '_'), + "version" to project.version, + "name" to properties["module.name"], + "description" to properties["module.description"], + "repoUrl" to properties["git.url"], + )) + into(project.layout.buildDirectory.dir("minecraftModResources")) +} + val minecraftModJar = tasks.register("minecraftModJar") { group = LifecycleBasePlugin.BUILD_GROUP + dependsOn(processMinecraftModResources) + from(zipTree(tasks.shadowJar.get().archiveFile)) + from(project.layout.buildDirectory.dir("minecraftModResources")) destinationDirectory.set(layout.buildDirectory.dir("minecraftModLibs")) } @@ -24,11 +50,15 @@ tasks.assemble { val minecraftModSourcesJar = tasks.register("minecraftModSourcesJar") { group = LifecycleBasePlugin.BUILD_GROUP + dependsOn(processMinecraftModResources) + from(zipTree(tasks.named("sourcesJar").get().archiveFile)) + from(project.layout.buildDirectory.dir("minecraftModResources")) archiveClassifier = "sources" destinationDirectory.set(layout.buildDirectory.dir("minecraftModLibs")) } artifacts.add("minecraftModElements", minecraftModJar) +artifacts.add("minecraftModApiElements", minecraftModJar) artifacts.add("minecraftModSourcesElements", minecraftModSourcesJar) diff --git a/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.dummy.gradle.kts b/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.dummy.gradle.kts index 50b26c2..87a1446 100644 --- a/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.dummy.gradle.kts +++ b/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.dummy.gradle.kts @@ -4,32 +4,6 @@ plugins { id("de.siphalor.tweed5.minecraft.mod.base") } -val processMinecraftModResources = tasks.register("processMinecraftModResources") { - inputs.property("id", project.name) - inputs.property("version", project.version) - inputs.property("name", properties["module.name"]) - inputs.property("description", properties["module.description"]) - - from(project.layout.settingsDirectory.dir("../tweed5-minecraft/mod-template/resources")) - expand(mapOf( - "id" to project.name, - "version" to project.version, - "name" to properties["module.name"], - "description" to properties["module.description"] - )) - into(project.layout.buildDirectory.dir("minecraftModResources")) -} - -tasks.named("minecraftModJar") { - from(project.layout.buildDirectory.dir("minecraftModResources")) - dependsOn(processMinecraftModResources) -} - -tasks.named("minecraftModSourcesJar") { - from(project.layout.buildDirectory.dir("minecraftModResources")) - dependsOn(processMinecraftModResources) -} - publishing { publications { create("minecraftMod") { diff --git a/tweed5-minecraft/.gitignore b/tweed5-minecraft/.gitignore new file mode 100644 index 0000000..85e23c9 --- /dev/null +++ b/tweed5-minecraft/.gitignore @@ -0,0 +1 @@ +/*/run/ diff --git a/tweed5-minecraft/conventions/build.gradle.kts b/tweed5-minecraft/conventions/build.gradle.kts index be6dd54..5c3a719 100644 --- a/tweed5-minecraft/conventions/build.gradle.kts +++ b/tweed5-minecraft/conventions/build.gradle.kts @@ -6,6 +6,8 @@ plugins { dependencies { implementation("de.siphalor.tweed5:tweed5-conventions") + implementation("de.siphalor.tweed5:tweed5-conventions-helpers") implementation(pluginMarker(mcCommonLibs.plugins.fabric.loom)) implementation(pluginMarker(mcCommonLibs.plugins.jcyo)) + implementation(pluginMarker(libs.plugins.lombok)) } diff --git a/tweed5-minecraft/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.cross-version.gradle.kts b/tweed5-minecraft/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.cross-version.gradle.kts index 7a005e9..105b7f8 100644 --- a/tweed5-minecraft/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.cross-version.gradle.kts +++ b/tweed5-minecraft/conventions/src/main/kotlin/de.siphalor.tweed5.minecraft.mod.cross-version.gradle.kts @@ -1,8 +1,13 @@ +import de.siphalor.tweed5.gradle.plugin.minecraft.mod.MinecraftModded import java.util.Properties plugins { + java id("fabric-loom") + id("de.siphalor.tweed5.expanded-sources-jar") id("de.siphalor.jcyo") + id("io.freefair.lombok") + id("de.siphalor.tweed5.shadow.explicit") id("de.siphalor.tweed5.minecraft.mod.base") } @@ -23,11 +28,20 @@ val shortVersion = project.property("tweed5.version").toString() val minecraftVersion = getMcCatalogVersion("minecraft") version = "$shortVersion+mc$minecraftVersion" -sourceSets { - create("testmod") { - compileClasspath += sourceSets.main.get().compileClasspath - runtimeClasspath += sourceSets.main.get().runtimeClasspath +val testmod by sourceSets.creating { + compileClasspath += sourceSets.main.get().compileClasspath + runtimeClasspath += sourceSets.main.get().runtimeClasspath +} + +loom { + runs { + create("testmodClient") { + client() + name("${properties["module.name"]} Test Mod (client)") + source(testmod) + } } + createRemapConfigurations(testmod) } // For some reason dependencyResolutionManagement from the settings.gradle doesn't seem to be passed through correctly, @@ -49,6 +63,14 @@ repositories { } } +configurations { + named("testmodRuntimeClasspath") { + attributes { + attribute(MinecraftModded.MINECRAFT_MODDED_ATTRIBUTE, objects.named(MinecraftModded.MODDED)) + } + } +} + dependencies { minecraft(mcCatalog.findLibrary("minecraft").get()) mappings(loom.layered { @@ -56,6 +78,48 @@ dependencies { parchment("org.parchmentmc.data:parchment-$minecraftVersion:${getMcCatalogVersion("parchment")}@zip") }) modImplementation(mcCommonLibs.fabric.loader) + + compileOnly(libs.jspecify.annotations) + + "testmodImplementation"(sourceSets.main.map { it.output }) +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +lombok { + version = libs.versions.lombok.get() +} + +tasks.jar { + dependsOn(tasks.processMinecraftModResources) + from(project.layout.buildDirectory.dir("minecraftModResources")) +} + +tasks.sourcesJar { + dependsOn(tasks.processMinecraftModResources) + from(project.layout.buildDirectory.dir("minecraftModResources")) +} + +tasks.named("processTestmodResources") { + inputs.property("id", project.name) + inputs.property("version", project.version) + inputs.property("name", properties["module.name"]) + inputs.property("description", properties["module.description"]) + inputs.property("repoUrl", properties["git.url"]) + + from(project.layout.settingsDirectory.dir("../tweed5-minecraft/mod-template/resources")) { + expand(mapOf( + "id" to project.name.replace('-', '_') + "_testmod", + "version" to project.version, + "name" to properties["module.name"].toString() + " (test mod)", + "description" to properties["module.description"], + "repoUrl" to properties["git.url"], + )) + } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } fun getMcCatalogVersion(name: String): String { diff --git a/tweed5-minecraft/fabric-helper/build.gradle.kts b/tweed5-minecraft/fabric-helper/build.gradle.kts new file mode 100644 index 0000000..d0676dc --- /dev/null +++ b/tweed5-minecraft/fabric-helper/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("de.siphalor.tweed5.local-runtime-only") + id("de.siphalor.tweed5.minecraft.mod.cross-version") +} + +dependencies { + modCompileOnly(fabricApi.module("fabric-networking-api-v1", mcLibs.versions.fabric.api.get())) + compileOnly("de.siphalor.tweed5:tweed5-comment-loader-extension") + compileOnly("de.siphalor.tweed5:tweed5-core") + compileOnly("de.siphalor.tweed5:tweed5-default-extensions") + compileOnly("de.siphalor.tweed5:tweed5-serde-extension") + compileOnly("de.siphalor.tweed5:tweed5-serde-gson") + + listOf("fabric-networking-api-v1", "fabric-lifecycle-events-v1").forEach { + modTestmodImplementation(fabricApi.module(it, mcLibs.versions.fabric.api.get())) + } + testmodImplementation(project(":tweed5-bundle", configuration = "minecraftModElements")) + testmodImplementation("de.siphalor.tweed5:tweed5-comment-loader-extension") + testmodImplementation("de.siphalor.tweed5:tweed5-serde-hjson") + testmodImplementation("de.siphalor.tweed5:tweed5-serde-gson") +} diff --git a/tweed5-minecraft/fabric-helper/gradle.properties b/tweed5-minecraft/fabric-helper/gradle.properties new file mode 100644 index 0000000..efe68ef --- /dev/null +++ b/tweed5-minecraft/fabric-helper/gradle.properties @@ -0,0 +1,2 @@ +module.name = Tweed 5 Fabric Helper +module.description = A collection of utility classes to make working with Tweed 5 configurations easier on Fabric. diff --git a/tweed5-minecraft/fabric-helper/src/main/java/de/siphalor/tweed5/fabric/helper/api/FabricConfigCommentLoader.java b/tweed5-minecraft/fabric-helper/src/main/java/de/siphalor/tweed5/fabric/helper/api/FabricConfigCommentLoader.java new file mode 100644 index 0000000..ed722a0 --- /dev/null +++ b/tweed5-minecraft/fabric-helper/src/main/java/de/siphalor/tweed5/fabric/helper/api/FabricConfigCommentLoader.java @@ -0,0 +1,79 @@ +package de.siphalor.tweed5.fabric.helper.api; + +import com.google.gson.stream.JsonReader; +import de.siphalor.tweed5.commentloaderextension.api.CommentLoaderExtension; +import de.siphalor.tweed5.commentloaderextension.api.CommentPathProcessor; +import de.siphalor.tweed5.core.api.container.ConfigContainer; +import de.siphalor.tweed5.dataapi.api.TweedDataReader; +import de.siphalor.tweed5.data.gson.GsonReader; +import lombok.Builder; +import lombok.extern.apachecommons.CommonsLog; +import org.jspecify.annotations.Nullable; + +import java.io.InputStream; +import java.io.InputStreamReader; + +@CommonsLog +@Builder +public class FabricConfigCommentLoader { + private final ConfigContainer configContainer; + private final String modId; + /** + * The prefix of the language keys, without the trailing dot! + */ + private final String prefix; + /** + * An optional suffix of the language keys + */ + private final @Nullable String suffix; + + public void loadCommentsFromLanguageFile(String languageCode) { + CommentLoaderExtension commentLoaderExtension = configContainer.extension(CommentLoaderExtension.class) + .orElseThrow(() -> new IllegalStateException("CommentLoaderExtension not declared on config")); + + String langFilePath = "assets/" + modId + "/lang/" + languageCode + ".json"; + + InputStream langInputStream = getClass().getClassLoader().getResourceAsStream(langFilePath); + if (langInputStream == null) { + return; + } + + try (TweedDataReader reader = new GsonReader(new JsonReader(new InputStreamReader(langInputStream)))) { + commentLoaderExtension.loadComments( + reader, new CommentPathProcessor() { + @Override + public MatchStatus matches(String path) { + if (!path.startsWith(prefix)) { + return MatchStatus.NO; + } else if (path.length() == prefix.length()) { + if (suffix != null && !path.endsWith(suffix)) { + return MatchStatus.NO; + } + return MatchStatus.YES; + } else if (path.charAt(prefix.length()) != '.') { + return MatchStatus.NO; + } else if (suffix != null && !path.endsWith(suffix)) { + return MatchStatus.MAYBE_DEEPER; + } else { + return MatchStatus.YES; + } + } + + @Override + public String process(String path) { + if (path.equals(prefix)) { + return ""; + } + path = path.substring(prefix.length() + 1); + if (suffix != null) { + path = path.substring(0, path.length() - suffix.length()); + } + return path; + } + } + ); + } catch (Exception e) { + log.warn("Failed to load comments from language file", e); + } + } +} diff --git a/tweed5-minecraft/fabric-helper/src/main/java/de/siphalor/tweed5/fabric/helper/api/FabricConfigContainerHelper.java b/tweed5-minecraft/fabric-helper/src/main/java/de/siphalor/tweed5/fabric/helper/api/FabricConfigContainerHelper.java new file mode 100644 index 0000000..a0dc11b --- /dev/null +++ b/tweed5-minecraft/fabric-helper/src/main/java/de/siphalor/tweed5/fabric/helper/api/FabricConfigContainerHelper.java @@ -0,0 +1,161 @@ +package de.siphalor.tweed5.fabric.helper.api; + +import de.siphalor.tweed5.core.api.container.ConfigContainer; +import de.siphalor.tweed5.core.api.container.ConfigContainerSetupPhase; +import de.siphalor.tweed5.data.extension.api.ReadWriteExtension; +import de.siphalor.tweed5.dataapi.api.TweedDataReader; +import de.siphalor.tweed5.dataapi.api.TweedDataWriter; +import de.siphalor.tweed5.dataapi.api.TweedSerde; +import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension; +import de.siphalor.tweed5.defaultextensions.patch.api.PatchInfo; +import de.siphalor.tweed5.patchwork.api.Patchwork; +import lombok.Getter; +import lombok.extern.apachecommons.CommonsLog; +import net.fabricmc.loader.api.FabricLoader; +import org.jspecify.annotations.Nullable; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.Consumer; +import java.util.function.Supplier; + +@CommonsLog +public class FabricConfigContainerHelper { + @Getter + private final ConfigContainer configContainer; + private final ReadWriteExtension readWriteExtension; + private final @Nullable PatchExtension patchExtension; + private final TweedSerde serde; + @Getter + private final String modId; + + private @Nullable Path tempConfigDirectory; + + public static FabricConfigContainerHelper create( + ConfigContainer configContainer, + TweedSerde serde, + String modId + ) { + if (configContainer.setupPhase() != ConfigContainerSetupPhase.INITIALIZED) { + throw new IllegalStateException( + "Config container must be fully initialized before creating helper. " + + "Usually you're just missing a call to initialize()" + ); + } + return new FabricConfigContainerHelper<>(configContainer, serde, modId); + } + + private FabricConfigContainerHelper(ConfigContainer configContainer, TweedSerde serde, String modId) { + this.configContainer = configContainer; + this.readWriteExtension = configContainer.extension(ReadWriteExtension.class) + .orElseThrow(() -> new IllegalStateException("ReadWriteExtension not declared in config container")); + this.patchExtension = configContainer.extension(PatchExtension.class).orElse(null); + this.serde = serde; + this.modId = modId; + } + + public T loadAndUpdateInConfigDirectory(Supplier defaultValueSupplier) { + T configValue = readConfigInConfigDirectory(defaultValueSupplier); + writeConfigInConfigDirectory(configValue); + return configValue; + } + + public void readPartialConfigInConfigDirectory(T value, Consumer readContextCustomizer) { + if (patchExtension == null) { + throw new IllegalStateException( + "PatchExtension must be declared in config container for partially loading config" + ); + } + + File configFile = getConfigFile(); + if (!configFile.exists()) { + return; + } + + try (TweedDataReader reader = serde.createReader(new FileInputStream(configFile))) { + Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData(); + readContextCustomizer.accept(contextExtensionsData); + PatchInfo patchInfo = patchExtension.collectPatchInfo(contextExtensionsData); + + T readValue = readWriteExtension.read(reader, configContainer().rootEntry(), contextExtensionsData); + + patchExtension.patch(configContainer.rootEntry(), value, readValue, patchInfo); + } catch (Exception e) { + log.error("Failed loading config file " + configFile.getAbsolutePath(), e); + } + } + + public T readConfigInConfigDirectory(Supplier defaultValueSupplier) { + File configFile = getConfigFile(); + if (!configFile.exists()) { + return defaultValueSupplier.get(); + } + + try (TweedDataReader reader = serde.createReader(new FileInputStream(configFile))) { + Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData(); + return readWriteExtension.read(reader, configContainer.rootEntry(), contextExtensionsData); + } catch (Exception e) { + log.error("Failed loading config file " + configFile.getAbsolutePath(), e); + return defaultValueSupplier.get(); + } + } + + public void writeConfigInConfigDirectory(T configValue) { + File configFile = getConfigFile(); + Path tempConfigDirectory = getOrCreateTempConfigDirectory(); + File tempConfigFile = tempConfigDirectory.resolve(getConfigFileName()).toFile(); + try (TweedDataWriter writer = serde.createWriter(new FileOutputStream(tempConfigFile))) { + Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData(); + + readWriteExtension.write( + writer, + configValue, + configContainer.rootEntry(), + contextExtensionsData + ); + } catch (Exception e) { + log.error("Failed to write config file " + tempConfigFile.getAbsolutePath(), e); + return; + } + + try { + if (configFile.exists()) { + if (!configFile.delete()) { + throw new IOException("Failed to overwrite old config file " + configFile.getAbsolutePath()); + } + } + Files.move(tempConfigFile.toPath(), configFile.toPath()); + } catch (IOException e) { + log.error("Failed to move temporary config file " + tempConfigFile.getAbsolutePath() + " to " + configFile.getAbsolutePath(), e); + } + } + + private File getConfigFile() { + Path configDir = FabricLoader.getInstance().getConfigDir(); + configDir.toFile().mkdirs(); + return configDir.resolve(getConfigFileName()).toFile(); + } + + private String getConfigFileName() { + return modId + serde.getPreferredFileExtension(); + } + + private Path getOrCreateTempConfigDirectory() { + if (tempConfigDirectory == null) { + try { + tempConfigDirectory = Files.createTempDirectory("tweed5-config"); + tempConfigDirectory.toFile().deleteOnExit(); + return tempConfigDirectory; + } catch (IOException e) { + log.warn("Failed to create temporary config directory, using game directory instead"); + } + tempConfigDirectory = FabricLoader.getInstance().getGameDir().resolve(".tweed5-tmp/").resolve(modId); + tempConfigDirectory.toFile().mkdirs(); + } + return tempConfigDirectory; + } +} diff --git a/tweed5-minecraft/fabric-helper/src/main/java/de/siphalor/tweed5/fabric/helper/api/package-info.java b/tweed5-minecraft/fabric-helper/src/main/java/de/siphalor/tweed5/fabric/helper/api/package-info.java new file mode 100644 index 0000000..2a459bb --- /dev/null +++ b/tweed5-minecraft/fabric-helper/src/main/java/de/siphalor/tweed5/fabric/helper/api/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package de.siphalor.tweed5.fabric.helper.api; + +import org.jspecify.annotations.NullMarked; diff --git a/tweed5-minecraft/fabric-helper/src/testmod/java/de/siphalor/tweed5/fabric/helper/testmod/FabricHelperTestMod.java b/tweed5-minecraft/fabric-helper/src/testmod/java/de/siphalor/tweed5/fabric/helper/testmod/FabricHelperTestMod.java new file mode 100644 index 0000000..0223261 --- /dev/null +++ b/tweed5-minecraft/fabric-helper/src/testmod/java/de/siphalor/tweed5/fabric/helper/testmod/FabricHelperTestMod.java @@ -0,0 +1,60 @@ +package de.siphalor.tweed5.fabric.helper.testmod; + +import de.siphalor.tweed5.attributesextension.api.serde.filter.AttributesReadWriteFilterExtension; +import de.siphalor.tweed5.core.api.container.ConfigContainer; +import de.siphalor.tweed5.data.hjson.HjsonSerde; +import de.siphalor.tweed5.data.hjson.HjsonWriter; +import de.siphalor.tweed5.fabric.helper.api.FabricConfigCommentLoader; +import de.siphalor.tweed5.fabric.helper.api.FabricConfigContainerHelper; +import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; +import lombok.extern.apachecommons.CommonsLog; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; + +@CommonsLog +public class FabricHelperTestMod implements ModInitializer { + public static final String MOD_ID = "tweed5_fabric_helper_testmod"; + + private TestModConfig config; + private ConfigContainer configContainer; + private FabricConfigContainerHelper configContainerHelper; + private AttributesReadWriteFilterExtension configFilterExtension; + + @Override + public void onInitialize() { + configContainer = TweedPojoWeaverBootstrapper.create(TestModConfig.class).weave(); + configContainer.extension(AttributesReadWriteFilterExtension.class) + .orElseThrow(() -> new IllegalStateException("AttributesReadWriteFilterExtension not found")) + .markAttributeForFiltering("reload"); + configFilterExtension = configContainer.extension(AttributesReadWriteFilterExtension.class) + .orElseThrow(() -> new IllegalStateException("AttributesReadWriteFilterExtension not found")); + + configContainer.initialize(); + + configContainerHelper = FabricConfigContainerHelper.create( + configContainer, + new HjsonSerde(new HjsonWriter.Options()), + MOD_ID + ); + FabricConfigCommentLoader.builder() + .configContainer(configContainer) + .modId(MOD_ID) + .prefix(MOD_ID + ".config") + .build() + .loadCommentsFromLanguageFile("en_us"); + + config = configContainerHelper.loadAndUpdateInConfigDirectory(TestModConfig::new); + + log.info("Hello " + config.helloStart() + config.helloEnd()); + + ServerLifecycleEvents.SERVER_STARTED.register(_server -> onServerStarted()); + } + + private void onServerStarted() { + configContainerHelper.readPartialConfigInConfigDirectory(config, patchwork -> + configFilterExtension.addFilter(patchwork, "scope", "game") + ); + + log.info("Hello " + config.helloInGame() + config.helloEnd()); + } +} diff --git a/tweed5-minecraft/fabric-helper/src/testmod/java/de/siphalor/tweed5/fabric/helper/testmod/TestModConfig.java b/tweed5-minecraft/fabric-helper/src/testmod/java/de/siphalor/tweed5/fabric/helper/testmod/TestModConfig.java new file mode 100644 index 0000000..af5316a --- /dev/null +++ b/tweed5-minecraft/fabric-helper/src/testmod/java/de/siphalor/tweed5/fabric/helper/testmod/TestModConfig.java @@ -0,0 +1,36 @@ +package de.siphalor.tweed5.fabric.helper.testmod; + +import de.siphalor.tweed5.attributesextension.api.AttributesExtension; +import de.siphalor.tweed5.attributesextension.api.serde.filter.AttributesReadWriteFilterExtension; +import de.siphalor.tweed5.commentloaderextension.api.CommentLoaderExtension; +import de.siphalor.tweed5.data.extension.api.ReadWriteExtension; +import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension; +import de.siphalor.tweed5.weaver.pojo.api.annotation.CompoundWeaving; +import de.siphalor.tweed5.weaver.pojo.api.annotation.DefaultWeavingExtensions; +import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeaving; +import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeavingExtension; +import de.siphalor.tweed5.weaver.pojoext.attributes.api.Attribute; +import de.siphalor.tweed5.weaver.pojoext.attributes.api.AttributesPojoWeavingProcessor; +import de.siphalor.tweed5.weaver.pojoext.serde.api.auto.AutoReadWritePojoWeavingProcessor; +import de.siphalor.tweed5.weaver.pojoext.serde.api.auto.DefaultReadWriteMappings; +import lombok.Data; + +@PojoWeaving(extensions = { + CommentLoaderExtension.class, + ReadWriteExtension.class, + PatchExtension.class, + AttributesExtension.class, + AttributesReadWriteFilterExtension.class, +}) +@PojoWeavingExtension(AutoReadWritePojoWeavingProcessor.class) +@PojoWeavingExtension(AttributesPojoWeavingProcessor.class) +@DefaultWeavingExtensions +@DefaultReadWriteMappings +@CompoundWeaving(namingFormat = "kebab_case") +@Data +public class TestModConfig { + private String helloStart = "Minecraft"; + @Attribute(key = "scope", values = "game") + private String helloInGame = "Game"; + private String helloEnd = "!"; +} diff --git a/tweed5-minecraft/fabric-helper/src/testmod/resources/assets/tweed5_fabric_helper_testmod/lang/en_us.json b/tweed5-minecraft/fabric-helper/src/testmod/resources/assets/tweed5_fabric_helper_testmod/lang/en_us.json new file mode 100644 index 0000000..c04f659 --- /dev/null +++ b/tweed5-minecraft/fabric-helper/src/testmod/resources/assets/tweed5_fabric_helper_testmod/lang/en_us.json @@ -0,0 +1,4 @@ +{ + "tweed5_fabric_helper_testmod.config.hello-start": "Whom to greet when the game starts", + "tweed5_fabric_helper_testmod.config.hello-in-game": "Whom to greet when the player joins a game" +} diff --git a/tweed5-minecraft/fabric-helper/src/testmod/resources/fabric.mod.json b/tweed5-minecraft/fabric-helper/src/testmod/resources/fabric.mod.json new file mode 100644 index 0000000..5b9700f --- /dev/null +++ b/tweed5-minecraft/fabric-helper/src/testmod/resources/fabric.mod.json @@ -0,0 +1,10 @@ +{ + "schemaVersion": 1, + "id": "tweed5_fabric_helper_testmod", + "version": "0.1.0", + "entrypoints": { + "main": [ + "de.siphalor.tweed5.fabric.helper.testmod.FabricHelperTestMod" + ] + } +} diff --git a/tweed5-minecraft/gradle/mc-1.21.10/mcLibs.versions.toml b/tweed5-minecraft/gradle/mc-1.21.10/mcLibs.versions.toml index cdc69eb..6f89465 100644 --- a/tweed5-minecraft/gradle/mc-1.21.10/mcLibs.versions.toml +++ b/tweed5-minecraft/gradle/mc-1.21.10/mcLibs.versions.toml @@ -1,8 +1,10 @@ [versions] coat = "1.0.0-beta.23" +fabric-api = "0.136.0+1.21.10" minecraft = "1.21.10" parchment = "2025.10.12" [libraries] coat = { group = "de.siphalor.coat", name = "coat-mc1.21.10", version.ref = "coat" } +fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" } minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" } diff --git a/tweed5-minecraft/mod-template/resources/fabric.mod.json b/tweed5-minecraft/mod-template/resources/fabric.mod.json index ef8e044..844f44f 100644 --- a/tweed5-minecraft/mod-template/resources/fabric.mod.json +++ b/tweed5-minecraft/mod-template/resources/fabric.mod.json @@ -3,8 +3,8 @@ "license": "LGPL-3.0-only", "contact": { "email": "info@siphalor.de", - "issues": "https://gitea.siphalor.de/Siphalor/tweed5/issues", - "sources": "https://gitea.siphalor.de/Siphalor/tweed5" + "issues": "${repoUrl}/issues", + "sources": "${repoUrl}" }, "custom": { "modmenu": { diff --git a/tweed5-minecraft/settings.gradle.kts b/tweed5-minecraft/settings.gradle.kts index 3216841..6ecca74 100644 --- a/tweed5-minecraft/settings.gradle.kts +++ b/tweed5-minecraft/settings.gradle.kts @@ -27,6 +27,13 @@ pluginManagement { dependencyResolutionManagement { repositories { mavenCentral() + maven { + name = "FabricMC" + url = uri("https://maven.fabricmc.net") + mavenContent { + includeGroupAndSubgroups("net.fabricmc") + } + } } versionCatalogs { @@ -46,7 +53,7 @@ dependencyResolutionManagement { includeBuild("../tweed5") includeNormalModule("bundle") -includeNormalModule("coat-bridge") +includeNormalModule("fabric-helper") fun includeNormalModule(name: String) { includeAs("tweed5-$name", name)