diff options
author | Linnea Gräf <nea@nea.moe> | 2024-11-12 21:38:31 +0100 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2024-11-12 21:38:31 +0100 |
commit | fc88e54a2e88c87bcfd5e7dbd6866764faa3e503 (patch) | |
tree | c5ec7980b67c47ce0d89175cb6ac9a180a94c3f3 /src | |
parent | b774daef5bd961f955d365ce07bd5aa4acb161f4 (diff) | |
download | Firmament-fc88e54a2e88c87bcfd5e7dbd6866764faa3e503.tar.gz Firmament-fc88e54a2e88c87bcfd5e7dbd6866764faa3e503.tar.bz2 Firmament-fc88e54a2e88c87bcfd5e7dbd6866764faa3e503.zip |
feat: Add descriptions for config options
Diffstat (limited to 'src')
15 files changed, 228 insertions, 94 deletions
diff --git a/src/compat/moulconfig/java/MCConfigEditorIntegration.kt b/src/compat/moulconfig/java/MCConfigEditorIntegration.kt index 7686beb..f1e7a98 100644 --- a/src/compat/moulconfig/java/MCConfigEditorIntegration.kt +++ b/src/compat/moulconfig/java/MCConfigEditorIntegration.kt @@ -2,7 +2,10 @@ package moe.nea.firmament.compat.moulconfig import com.google.auto.service.AutoService import io.github.notenoughupdates.moulconfig.Config +import io.github.notenoughupdates.moulconfig.DescriptionRendereringBehaviour +import io.github.notenoughupdates.moulconfig.Social import io.github.notenoughupdates.moulconfig.common.IMinecraft +import io.github.notenoughupdates.moulconfig.common.MyResourceLocation import io.github.notenoughupdates.moulconfig.gui.GuiComponent import io.github.notenoughupdates.moulconfig.gui.GuiElementWrapper import io.github.notenoughupdates.moulconfig.gui.GuiOptionEditor @@ -22,10 +25,14 @@ import io.github.notenoughupdates.moulconfig.observer.GetSetter import io.github.notenoughupdates.moulconfig.processor.ProcessedCategory import io.github.notenoughupdates.moulconfig.processor.ProcessedOption import java.lang.reflect.Type +import java.net.URI import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import kotlin.time.DurationUnit import net.minecraft.client.gui.screen.Screen +import net.minecraft.util.Identifier +import net.minecraft.util.Util +import moe.nea.firmament.Firmament import moe.nea.firmament.gui.config.BooleanHandler import moe.nea.firmament.gui.config.ClickHandler import moe.nea.firmament.gui.config.DurationHandler @@ -37,6 +44,7 @@ import moe.nea.firmament.gui.config.KeyBindingHandler import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedOption import moe.nea.firmament.gui.config.StringHandler +import moe.nea.firmament.gui.toMoulConfig import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.util.ErrorUtil import moe.nea.firmament.util.FirmFormatters @@ -287,6 +295,47 @@ class MCConfigEditorIntegration : FirmamentConfigScreenProvider { override fun shouldAutoFocusSearchbar(): Boolean { return true } + + override fun getTitle(): String { + return "Firmament" + } + + @Deprecated("Deprecated in java") + override fun executeRunnable(runnableId: Int) { + if (runnableId >= 0) + ErrorUtil.softError("Executed runnable $runnableId") + } + + override fun getDescriptionBehaviour(option: ProcessedOption?): DescriptionRendereringBehaviour { + return DescriptionRendereringBehaviour.EXPAND_PANEL + } + + fun mkSocial(name: String, identifier: Identifier, link: String) = object : Social() { + override fun onClick() { + Util.getOperatingSystem().open(URI(link)) + } + + override fun getTooltip(): List<String> { + return listOf(name) + } + + override fun getIcon(): MyResourceLocation { + return identifier.toMoulConfig() + } + } + + private val socials = listOf<Social>( + mkSocial("Discord", Firmament.identifier("textures/socials/discord.png"), + Firmament.modContainer.metadata.contact.get("discord").get()), + mkSocial("Source Code", Firmament.identifier("textures/socials/git.png"), + Firmament.modContainer.metadata.contact.get("sources").get()), + mkSocial("Modrinth", Firmament.identifier("textures/socials/modrinth.png"), + Firmament.modContainer.metadata.contact.get("modrinth").get()), + ) + + override fun getSocials(): List<Social> { + return socials + } } val categories = ManagedConfig.Category.entries.map { val options = mutableListOf<ProcessedOptionFirm>() @@ -295,7 +344,7 @@ class MCConfigEditorIntegration : FirmamentConfigScreenProvider { val categoryAccordionId = nextAccordionId++ options.add(object : ProcessedOptionFirm(-1, configObject) { override fun getDebugDeclarationLocation(): String { - return "FirmamentConfig:$config.name" + return "FirmamentConfig:${config.name}" } override fun getName(): String { diff --git a/src/compat/moulconfig/java/ProcessedCategoryFirm.kt b/src/compat/moulconfig/java/ProcessedCategoryFirm.kt index 19e1112..5313441 100644 --- a/src/compat/moulconfig/java/ProcessedCategoryFirm.kt +++ b/src/compat/moulconfig/java/ProcessedCategoryFirm.kt @@ -26,7 +26,7 @@ class ProcessedCategoryFirm( } override fun getDescription(): String { - return "Missing description" // TODO: add description + return category.description.string } override fun getIdentifier(): String { diff --git a/src/compat/moulconfig/java/ProcessedEditableOptionFirm.kt b/src/compat/moulconfig/java/ProcessedEditableOptionFirm.kt index c42ad3f..2bc5cd4 100644 --- a/src/compat/moulconfig/java/ProcessedEditableOptionFirm.kt +++ b/src/compat/moulconfig/java/ProcessedEditableOptionFirm.kt @@ -18,7 +18,7 @@ abstract class ProcessedEditableOptionFirm<T : Any>( } override fun getDescription(): String { - return "Missing description" // TODO: add description + return managedOption.labelDescription.string } override fun explicitNotifyChange() { diff --git a/src/compat/yacl/java/YaclIntegration.kt b/src/compat/yacl/java/YaclIntegration.kt index 239d3a0..9aec501 100644 --- a/src/compat/yacl/java/YaclIntegration.kt +++ b/src/compat/yacl/java/YaclIntegration.kt @@ -6,6 +6,7 @@ import dev.isxander.yacl3.api.ButtonOption import dev.isxander.yacl3.api.ConfigCategory import dev.isxander.yacl3.api.LabelOption import dev.isxander.yacl3.api.Option +import dev.isxander.yacl3.api.OptionDescription import dev.isxander.yacl3.api.OptionGroup import dev.isxander.yacl3.api.YetAnotherConfigLib import dev.isxander.yacl3.api.controller.ControllerBuilder @@ -69,6 +70,7 @@ class YaclIntegration : FirmamentConfigScreenProvider { fun <T> createDefaultBinding(function: (Option<T>) -> ControllerBuilder<T>): Option.Builder<T> { return Option.createBuilder<T>() .name(managedOption.labelText) + .description(OptionDescription.of(managedOption.labelDescription)) .binding(binding as Binding<T>) .controller { function(it) } } diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/EarlyInstantiateTranslations.java b/src/main/java/moe/nea/firmament/mixins/devenv/EarlyInstantiateTranslations.java new file mode 100644 index 0000000..ef8c9eb --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/devenv/EarlyInstantiateTranslations.java @@ -0,0 +1,19 @@ +package moe.nea.firmament.mixins.devenv; + +import net.minecraft.text.TranslatableTextContent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TranslatableTextContent.class) +public abstract class EarlyInstantiateTranslations { + @Shadow + protected abstract void updateTranslations(); + + @Inject(method = "<init>", at = @At("TAIL")) + private void onInit(String key, String fallback, Object[] args, CallbackInfo ci) { + updateTranslations(); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/WarnOnMissingTranslations.java b/src/main/java/moe/nea/firmament/mixins/devenv/WarnOnMissingTranslations.java new file mode 100644 index 0000000..33840c1 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/devenv/WarnOnMissingTranslations.java @@ -0,0 +1,38 @@ +package moe.nea.firmament.mixins.devenv; + +import moe.nea.firmament.features.debug.DeveloperFeatures; +import moe.nea.firmament.util.MC; +import net.minecraft.client.resource.language.TranslationStorage; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Set; +import java.util.TreeSet; + +@Mixin(TranslationStorage.class) +public abstract class WarnOnMissingTranslations { + @Shadow + public abstract boolean hasTranslation(String key); + + @Unique + private final Set<String> missingTranslations = new TreeSet<>(); + + @Inject(method = "get", at = @At("HEAD")) + private void onGetTranslationKey(String key, String fallback, CallbackInfoReturnable<String> cir) { + warnForMissingTranslation(key); + } + + @Unique + private void warnForMissingTranslation(String key) { + if (!key.contains("firmament")) return; + if (hasTranslation(key)) return; + if (!missingTranslations.add(key)) return; + MC.INSTANCE.sendChat(Text.literal("Missing firmament translation: " + key)); + DeveloperFeatures.hookMissingTranslations(missingTranslations); + } +} diff --git a/src/main/kotlin/Firmament.kt b/src/main/kotlin/Firmament.kt index e0541f1..9f15bff 100644 --- a/src/main/kotlin/Firmament.kt +++ b/src/main/kotlin/Firmament.kt @@ -49,6 +49,7 @@ import moe.nea.firmament.util.SBData import moe.nea.firmament.util.data.IDataHolder object Firmament { + val modContainer by lazy { FabricLoader.getInstance().getModContainer(MOD_ID).get() } const val MOD_ID = "firmament" val DEBUG = System.getProperty("firmament.debug") == "true" diff --git a/src/main/kotlin/features/debug/DeveloperFeatures.kt b/src/main/kotlin/features/debug/DeveloperFeatures.kt index d4b118b..8f0c25c 100644 --- a/src/main/kotlin/features/debug/DeveloperFeatures.kt +++ b/src/main/kotlin/features/debug/DeveloperFeatures.kt @@ -1,12 +1,16 @@ package moe.nea.firmament.features.debug +import java.io.File import java.nio.file.Path import java.util.concurrent.CompletableFuture +import kotlinx.serialization.json.encodeToStream import kotlin.io.path.absolute import kotlin.io.path.exists import net.minecraft.client.MinecraftClient import net.minecraft.text.Text import moe.nea.firmament.Firmament +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.TickEvent import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.util.MC @@ -14,41 +18,57 @@ import moe.nea.firmament.util.TimeMark import moe.nea.firmament.util.iterate object DeveloperFeatures : FirmamentFeature { - override val identifier: String - get() = "developer" - override val config: TConfig - get() = TConfig - override val defaultEnabled: Boolean - get() = Firmament.DEBUG - - val gradleDir = - Path.of(".").absolute() - .iterate { it.parent } - .find { it.resolve("settings.gradle.kts").exists() } - - object TConfig : ManagedConfig("developer", Category.DEV) { - val autoRebuildResources by toggle("auto-rebuild") { false } - } - - @JvmStatic - fun hookOnBeforeResourceReload(client: MinecraftClient): CompletableFuture<Void> { - val reloadFuture = if (TConfig.autoRebuildResources && isEnabled && gradleDir != null) { - val builder = ProcessBuilder("./gradlew", ":processResources") - builder.directory(gradleDir.toFile()) - builder.inheritIO() - val process = builder.start() - MC.sendChat(Text.translatable("firmament.dev.resourcerebuild.start")) - val startTime = TimeMark.now() - process.toHandle().onExit().thenApply { - MC.sendChat(Text.stringifiedTranslatable( - "firmament.dev.resourcerebuild.done", - startTime.passedTime())) - Unit - } - } else { - CompletableFuture.completedFuture(Unit) - } - return reloadFuture.thenCompose { client.reloadResources() } - } + override val identifier: String + get() = "developer" + override val config: TConfig + get() = TConfig + override val defaultEnabled: Boolean + get() = Firmament.DEBUG + + val gradleDir = + Path.of(".").absolute() + .iterate { it.parent } + .find { it.resolve("settings.gradle.kts").exists() } + + object TConfig : ManagedConfig("developer", Category.DEV) { + val autoRebuildResources by toggle("auto-rebuild") { false } + } + + var missingTranslations: Set<String>? = null + + @JvmStatic + fun hookMissingTranslations(missingTranslations: Set<String>) { + this.missingTranslations = missingTranslations + } + + @Subscribe + fun dumpMissingTranslations(tickEvent: TickEvent) { + val toDump = missingTranslations ?: return + missingTranslations = null + File("missing_translations.json").outputStream().use { + Firmament.json.encodeToStream(toDump.associateWith { "Mis" + "sing translation" }, it) + } + } + + @JvmStatic + fun hookOnBeforeResourceReload(client: MinecraftClient): CompletableFuture<Void> { + val reloadFuture = if (TConfig.autoRebuildResources && isEnabled && gradleDir != null) { + val builder = ProcessBuilder("./gradlew", ":processResources") + builder.directory(gradleDir.toFile()) + builder.inheritIO() + val process = builder.start() + MC.sendChat(Text.translatable("firmament.dev.resourcerebuild.start")) + val startTime = TimeMark.now() + process.toHandle().onExit().thenApply { + MC.sendChat(Text.stringifiedTranslatable( + "firmament.dev.resourcerebuild.done", + startTime.passedTime())) + Unit + } + } else { + CompletableFuture.completedFuture(Unit) + } + return reloadFuture.thenCompose { client.reloadResources() } + } } diff --git a/src/main/kotlin/gui/config/ManagedConfig.kt b/src/main/kotlin/gui/config/ManagedConfig.kt index c8b6ce8..8222a46 100644 --- a/src/main/kotlin/gui/config/ManagedConfig.kt +++ b/src/main/kotlin/gui/config/ManagedConfig.kt @@ -44,6 +44,7 @@ abstract class ManagedConfig( ; val labelText: Text = Text.translatable("firmament.config.category.${name.lowercase()}") + val description: Text = Text.translatable("firmament.config.category.${name.lowercase()}.description") val configs: MutableList<ManagedConfig> = mutableListOf() } diff --git a/src/main/kotlin/gui/config/ManagedOption.kt b/src/main/kotlin/gui/config/ManagedOption.kt index 4a8d773..d1aba83 100644 --- a/src/main/kotlin/gui/config/ManagedOption.kt +++ b/src/main/kotlin/gui/config/ManagedOption.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.gui.config import io.github.notenoughupdates.moulconfig.observer.GetSetter @@ -9,54 +7,57 @@ import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty import net.minecraft.text.Text import moe.nea.firmament.Firmament +import moe.nea.firmament.util.ErrorUtil class ManagedOption<T : Any>( - val element: ManagedConfig, - val propertyName: String, - val default: () -> T, - val handler: ManagedConfig.OptionHandler<T> + val element: ManagedConfig, + val propertyName: String, + val default: () -> T, + val handler: ManagedConfig.OptionHandler<T> ) : ReadWriteProperty<Any?, T>, GetSetter<T> { - override fun set(newValue: T) { - this.value = newValue - } - - override fun get(): T { - return this.value - } - - val rawLabelText = "firmament.config.${element.name}.${propertyName}" - val labelText = Text.translatable(rawLabelText) - - lateinit var value: T - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - this.value = value - } - - override fun getValue(thisRef: Any?, property: KProperty<*>): T { - return value - } - - fun load(root: JsonElement) { - if (root is JsonObject && root.containsKey(propertyName)) { - try { - value = handler.fromJson(root[propertyName]!!) - return - } catch (e: Exception) { - Firmament.logger.error( - "Exception during loading of config file ${element.name}. This will reset this config.", - e - ) - } - } - value = default() - } - - fun toJson(): JsonElement? { - return handler.toJson(value) - } - - fun appendToGui(guiapp: GuiAppender) { - handler.emitGuiElements(this, guiapp) - } + override fun set(newValue: T) { + this.value = newValue + } + + override fun get(): T { + return this.value + } + + val rawLabelText = "firmament.config.${element.name}.${propertyName}" + val labelText: Text = Text.translatable(rawLabelText) + val descriptionTranslationKey = "firmament.config.${element.name}.${propertyName}.description" + val labelDescription: Text = Text.translatable(descriptionTranslationKey) + + lateinit var value: T + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + this.value = value + } + + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + return value + } + + fun load(root: JsonElement) { + if (root is JsonObject && root.containsKey(propertyName)) { + try { + value = handler.fromJson(root[propertyName]!!) + return + } catch (e: Exception) { + ErrorUtil.softError( + "Exception during loading of config file ${element.name}. This will reset this config.", + e + ) + } + } + value = default() + } + + fun toJson(): JsonElement? { + return handler.toJson(value) + } + + fun appendToGui(guiapp: GuiAppender) { + handler.emitGuiElements(this, guiapp) + } } diff --git a/src/main/kotlin/util/MC.kt b/src/main/kotlin/util/MC.kt index 1b7739f..cbcd8ae 100644 --- a/src/main/kotlin/util/MC.kt +++ b/src/main/kotlin/util/MC.kt @@ -3,6 +3,7 @@ package moe.nea.firmament.util import io.github.moulberry.repo.data.Coordinate import java.util.concurrent.ConcurrentLinkedQueue import net.minecraft.client.MinecraftClient +import net.minecraft.client.gui.hud.InGameHud import net.minecraft.client.gui.screen.Screen import net.minecraft.client.gui.screen.ingame.HandledScreen import net.minecraft.client.network.ClientPlayerEntity @@ -28,9 +29,10 @@ object MC { init { TickEvent.subscribe("MC:push") { - while (true) { - inGameHud.chatHud.addMessage(messageQueue.poll() ?: break) - } + if (inGameHud.chatHud != null && world != null) + while (true) { + inGameHud.chatHud.addMessage(messageQueue.poll() ?: break) + } while (true) { (nextTickTodos.poll() ?: break).invoke() } @@ -41,7 +43,7 @@ object MC { } fun sendChat(text: Text) { - if (instance.isOnThread) + if (instance.isOnThread && inGameHud.chatHud != null && world != null) inGameHud.chatHud.addMessage(text) else messageQueue.add(text) @@ -86,7 +88,7 @@ object MC { inline val interactionManager get() = instance.interactionManager inline val textureManager get() = instance.textureManager inline val options get() = instance.options - inline val inGameHud get() = instance.inGameHud + inline val inGameHud: InGameHud get() = instance.inGameHud inline val font get() = instance.textRenderer inline val soundManager get() = instance.soundManager inline val player: ClientPlayerEntity? get() = instance.player diff --git a/src/main/resources/assets/firmament/textures/socials/discord.png b/src/main/resources/assets/firmament/textures/socials/discord.png Binary files differnew file mode 100644 index 0000000..e9dc50d --- /dev/null +++ b/src/main/resources/assets/firmament/textures/socials/discord.png diff --git a/src/main/resources/assets/firmament/textures/socials/git.png b/src/main/resources/assets/firmament/textures/socials/git.png Binary files differnew file mode 100644 index 0000000..d7ab359 --- /dev/null +++ b/src/main/resources/assets/firmament/textures/socials/git.png diff --git a/src/main/resources/assets/firmament/textures/socials/modrinth.png b/src/main/resources/assets/firmament/textures/socials/modrinth.png Binary files differnew file mode 100644 index 0000000..bfc9403 --- /dev/null +++ b/src/main/resources/assets/firmament/textures/socials/modrinth.png diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 9b00adf..3b988b1 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -14,7 +14,8 @@ ], "contact": { "discord": "https://discord.gg/64pFP94AWA", - "sources": "https://git.nea.moe/nea/firmament/" + "sources": "https://github.com/nea89o/Firmament", + "modrinth": "https://modrinth.com/mod/firmament" }, "license": "GPL 3.0 or Later", "accessWidener": "firmament.accesswidener", |