aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/moe/nea
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/moe/nea')
-rw-r--r--src/main/java/moe/nea/firmament/gui/config/storage/ArrayIndexedJsonPointer.kt17
-rw-r--r--src/main/java/moe/nea/firmament/gui/config/storage/ConfigEditor.kt104
-rw-r--r--src/main/java/moe/nea/firmament/gui/config/storage/ConfigFixEvent.kt38
-rw-r--r--src/main/java/moe/nea/firmament/gui/config/storage/JsonPointer.kt8
-rw-r--r--src/main/java/moe/nea/firmament/gui/config/storage/ObjectIndexedJsonPointer.kt17
-rw-r--r--src/main/java/moe/nea/firmament/mixins/FirmKeybindsInVanillaControlsPatch.java1
-rw-r--r--src/main/java/moe/nea/firmament/util/data/ManagedConfig.kt282
7 files changed, 466 insertions, 1 deletions
diff --git a/src/main/java/moe/nea/firmament/gui/config/storage/ArrayIndexedJsonPointer.kt b/src/main/java/moe/nea/firmament/gui/config/storage/ArrayIndexedJsonPointer.kt
new file mode 100644
index 0000000..1e204d6
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/gui/config/storage/ArrayIndexedJsonPointer.kt
@@ -0,0 +1,17 @@
+package moe.nea.firmament.gui.config.storage
+
+import com.google.gson.JsonArray
+import com.google.gson.JsonElement
+
+data class ArrayIndexedJsonPointer(
+ val owner: JsonArray,
+ val index: Int
+) : JsonPointer {
+ override fun get(): JsonElement {
+ return owner.get(index)
+ }
+
+ override fun set(value: JsonElement) {
+ owner.set(index, value)
+ }
+}
diff --git a/src/main/java/moe/nea/firmament/gui/config/storage/ConfigEditor.kt b/src/main/java/moe/nea/firmament/gui/config/storage/ConfigEditor.kt
new file mode 100644
index 0000000..df1ed33
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/gui/config/storage/ConfigEditor.kt
@@ -0,0 +1,104 @@
+package moe.nea.firmament.gui.config.storage
+
+import com.google.gson.JsonArray
+import com.google.gson.JsonObject
+import kotlinx.serialization.json.JsonElement
+import moe.nea.firmament.util.json.intoGson
+import moe.nea.firmament.util.json.intoKotlinJson
+
+data class ConfigEditor(
+ val roots: List<JsonPointer>,
+) {
+ fun transform(transform: (JsonElement) -> JsonElement) {
+ roots.forEach { root ->
+ root.set(transform(root.get().intoKotlinJson()).intoGson())
+ }
+ }
+
+ fun move(fromPath: String, toPath: String) {
+ if (fromPath == toPath) return
+ val fromSegments = fromPath.split(".").filter { it.isNotEmpty() }
+ val toSegments = toPath.split(".").filter { it.isNotEmpty() }
+ roots.forEach { root ->
+ var fp = root.get()
+ if (fromSegments.isEmpty()) {
+ root.set(JsonObject())
+ } else {
+ fromSegments.dropLast(1).forEach {
+ fp = (fp as JsonObject)[it] ?: return@forEach // todo warn if we dont find the object maybe
+ }
+ fp as JsonObject
+ fp = fp.remove(fromSegments.last())?.deepCopy() ?: return@forEach // in theory i don't need to deepcopy but fuck theory
+ }
+ if (toSegments.isEmpty()) {
+ root.set(fp)
+ } else {
+ var lp = root.get()
+ toSegments.dropLast(1).forEach { name ->
+ val parent = lp as JsonObject
+ var child = parent[name]
+ if (child == null) {
+ child = JsonObject()
+ parent.add(name, child)
+ }
+ lp = child
+ }
+ lp as JsonObject
+ if (lp.has(toSegments.last())) {
+ error("Cannot overwrite $lp.${toSegments.last()} with $fp")
+ }
+ lp.add(toSegments.last(), fp)
+ }
+ }
+ }
+
+ fun at(path: String, block: ConfigEditor.() -> Unit) {
+ block(at(path))
+ }
+
+ fun at(path: String): ConfigEditor {
+ var lastRoots = roots
+ for (segment in path.split(".")) {
+ if (segment.isEmpty()) {
+ continue
+ } else if (segment == "*") {
+ lastRoots = lastRoots.flatMap { root ->
+ when (val ele = root.get()) {
+ is JsonObject -> {
+ ele.entrySet().map {
+ (ObjectIndexedJsonPointer(ele, it.key))
+ }
+ }
+
+ is JsonArray -> {
+ (0..<ele.size()).map {
+ (ArrayIndexedJsonPointer(ele, it))
+ }
+ }
+
+ else -> {
+ error("Cannot expand a json primitive $ele at $path")
+ }
+ }
+ }
+ } else {
+ lastRoots = lastRoots.map { root ->
+ when (val ele = root.get()) {
+ is JsonObject -> {
+ ObjectIndexedJsonPointer(ele, segment)
+ }
+
+ is JsonArray -> {
+ ArrayIndexedJsonPointer(ele, segment.toInt())
+ }
+
+ else -> {
+ error("Cannot expand a json primitive $ele at $path")
+ }
+ }
+ }
+ }
+ }
+ return ConfigEditor(lastRoots)
+ }
+}
diff --git a/src/main/java/moe/nea/firmament/gui/config/storage/ConfigFixEvent.kt b/src/main/java/moe/nea/firmament/gui/config/storage/ConfigFixEvent.kt
new file mode 100644
index 0000000..07148d5
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/gui/config/storage/ConfigFixEvent.kt
@@ -0,0 +1,38 @@
+package moe.nea.firmament.gui.config.storage
+
+import com.google.gson.JsonElement
+import com.google.gson.JsonObject
+import moe.nea.firmament.events.FirmamentEvent
+import moe.nea.firmament.events.FirmamentEventBus
+
+data class ConfigFixEvent(
+ val storageClass: ConfigStorageClass,
+ val toVersion: Int,
+ var data: JsonObject,
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<ConfigFixEvent>() {
+
+ }
+ fun on(
+ toVersion: Int,
+ storageClass: ConfigStorageClass,
+ block: ConfigEditor.() -> Unit
+ ) {
+ require(toVersion <= FirmamentConfigLoader.currentConfigVersion)
+ if (this.toVersion == toVersion && this.storageClass == storageClass) {
+ block(ConfigEditor(listOf(object : JsonPointer {
+ override fun get(): JsonObject {
+ return data
+ }
+
+ override fun set(value: JsonElement) {
+ data = value as JsonObject
+ }
+
+ override fun toString(): String {
+ return "ConfigRoot($storageClass)"
+ }
+ })))
+ }
+ }
+}
diff --git a/src/main/java/moe/nea/firmament/gui/config/storage/JsonPointer.kt b/src/main/java/moe/nea/firmament/gui/config/storage/JsonPointer.kt
new file mode 100644
index 0000000..e34c312
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/gui/config/storage/JsonPointer.kt
@@ -0,0 +1,8 @@
+package moe.nea.firmament.gui.config.storage
+
+import com.google.gson.JsonElement
+
+interface JsonPointer {
+ fun get(): JsonElement
+ fun set(value: JsonElement)
+}
diff --git a/src/main/java/moe/nea/firmament/gui/config/storage/ObjectIndexedJsonPointer.kt b/src/main/java/moe/nea/firmament/gui/config/storage/ObjectIndexedJsonPointer.kt
new file mode 100644
index 0000000..091275d
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/gui/config/storage/ObjectIndexedJsonPointer.kt
@@ -0,0 +1,17 @@
+package moe.nea.firmament.gui.config.storage
+
+import com.google.gson.JsonElement
+import com.google.gson.JsonObject
+
+data class ObjectIndexedJsonPointer(
+ val owner: JsonObject,
+ val name: String
+) : JsonPointer {
+ override fun get(): JsonElement {
+ return owner.get(name)
+ }
+
+ override fun set(value: JsonElement) {
+ owner.add(name, value)
+ }
+}
diff --git a/src/main/java/moe/nea/firmament/mixins/FirmKeybindsInVanillaControlsPatch.java b/src/main/java/moe/nea/firmament/mixins/FirmKeybindsInVanillaControlsPatch.java
index 699d5b7..4c9f925 100644
--- a/src/main/java/moe/nea/firmament/mixins/FirmKeybindsInVanillaControlsPatch.java
+++ b/src/main/java/moe/nea/firmament/mixins/FirmKeybindsInVanillaControlsPatch.java
@@ -3,7 +3,6 @@
package moe.nea.firmament.mixins;
import moe.nea.firmament.gui.config.KeyBindingHandler;
-import moe.nea.firmament.gui.config.ManagedConfig;
import moe.nea.firmament.keybindings.FirmamentKeyBindings;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.option.ControlsListWidget;
diff --git a/src/main/java/moe/nea/firmament/util/data/ManagedConfig.kt b/src/main/java/moe/nea/firmament/util/data/ManagedConfig.kt
new file mode 100644
index 0000000..169dad1
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/util/data/ManagedConfig.kt
@@ -0,0 +1,282 @@
+package moe.nea.firmament.util.data
+
+import com.mojang.serialization.Codec
+import io.github.notenoughupdates.moulconfig.ChromaColour
+import io.github.notenoughupdates.moulconfig.gui.CloseEventListener
+import io.github.notenoughupdates.moulconfig.gui.GuiContext
+import io.github.notenoughupdates.moulconfig.gui.component.CenterComponent
+import io.github.notenoughupdates.moulconfig.gui.component.ColumnComponent
+import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent
+import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
+import io.github.notenoughupdates.moulconfig.gui.component.ScrollPanelComponent
+import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
+import io.github.notenoughupdates.moulconfig.platform.MoulConfigScreenComponent
+import kotlinx.serialization.json.JsonElement
+import kotlinx.serialization.json.JsonObject
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.gui.FirmButtonComponent
+import moe.nea.firmament.gui.config.AllConfigsGui
+import moe.nea.firmament.gui.config.BooleanHandler
+import moe.nea.firmament.gui.config.ChoiceHandler
+import moe.nea.firmament.gui.config.ClickHandler
+import moe.nea.firmament.gui.config.ColourHandler
+import moe.nea.firmament.gui.config.DurationHandler
+import moe.nea.firmament.gui.config.GuiAppender
+import moe.nea.firmament.gui.config.HudMeta
+import moe.nea.firmament.gui.config.HudMetaHandler
+import moe.nea.firmament.gui.config.HudPosition
+import moe.nea.firmament.gui.config.IntegerHandler
+import moe.nea.firmament.gui.config.KeyBindingHandler
+import moe.nea.firmament.gui.config.ManagedOption
+import moe.nea.firmament.gui.config.StringHandler
+import moe.nea.firmament.keybindings.SavedKeyBinding
+import moe.nea.firmament.util.ScreenUtil
+import moe.nea.firmament.util.collections.InstanceList
+import net.minecraft.client.gui.screen.Screen
+import net.minecraft.text.Text
+import net.minecraft.util.StringIdentifiable
+import org.joml.Vector2i
+import kotlinx.serialization.json.buildJsonObject
+import kotlin.io.path.createDirectories
+import kotlin.io.path.readText
+import kotlin.io.path.writeText
+import kotlin.time.Duration
+import moe.nea.firmament.gui.config.storage.ConfigStorageClass
+
+abstract class ManagedConfig(
+ val name: String,
+ val category: Category,
+) : IDataHolder<Unit> {
+ enum class Category {
+ // Böse Kategorie, nicht benutzten lol
+ MISC,
+ CHAT,
+ INVENTORY,
+ ITEMS,
+ MINING,
+ GARDEN,
+ EVENTS,
+ INTEGRATIONS,
+ META,
+ DEV,
+ ;
+
+ 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()
+ }
+
+ companion object {
+ val allManagedConfigs = InstanceList<ManagedConfig>("ManagedConfig")
+ }
+
+ interface OptionHandler<T : Any> {
+ fun initOption(opt: ManagedOption<T>) {}
+ fun toJson(element: T): JsonElement?
+ fun fromJson(element: JsonElement): T
+ fun emitGuiElements(opt: ManagedOption<T>, guiAppender: GuiAppender)
+ }
+
+ init {
+ allManagedConfigs.getAll().forEach {
+ require(it.name != name) { "Duplicate name '$name' used for config" }
+ }
+ allManagedConfigs.add(this)
+ category.configs.add(this)
+ }
+
+ override fun keys(): Collection<Unit> {
+ return listOf(Unit)
+ }
+
+ override fun clear() {
+ sortedOptions.forEach {
+ it._actualValue = null
+ }
+ }
+
+ override val storageClass: ConfigStorageClass
+ get() = ConfigStorageClass.CONFIG
+
+ override fun saveTo(key: Unit): JsonObject {
+ return buildJsonObject {
+ sortedOptions.forEach {
+ put(it.propertyName, it.toJson() ?: return@forEach)
+ }
+ }
+ }
+
+ override fun loadFrom(key: Unit, jsonObject: JsonObject) {
+ sortedOptions.forEach {
+ it.load(jsonObject)
+ }
+ }
+
+ val allOptions = mutableMapOf<String, ManagedOption<*>>()
+ val sortedOptions = mutableListOf<ManagedOption<*>>()
+
+ private var latestGuiAppender: GuiAppender? = null
+
+ protected fun <T : Any> option(
+ propertyName: String,
+ default: () -> T,
+ handler: OptionHandler<T>
+ ): ManagedOption<T> {
+ if (propertyName in allOptions) error("Cannot register the same name twice")
+ return ManagedOption(this, propertyName, default, handler).also {
+ it.handler.initOption(it)
+ allOptions[propertyName] = it
+ sortedOptions.add(it)
+ }
+ }
+
+ protected fun toggle(propertyName: String, default: () -> Boolean): ManagedOption<Boolean> {
+ return option(propertyName, default, BooleanHandler(this))
+ }
+
+ protected fun colour(propertyName: String, default: () -> ChromaColour): ManagedOption<ChromaColour> {
+ return option(propertyName, default, ColourHandler(this))
+ }
+
+ protected fun <E> choice(
+ propertyName: String,
+ enumClass: Class<E>,
+ default: () -> E
+ ): ManagedOption<E> where E : Enum<E>, E : StringIdentifiable {
+ return option(propertyName, default, ChoiceHandler(enumClass, enumClass.enumConstants.toList()))
+ }
+
+ protected inline fun <reified E> choice(
+ propertyName: String,
+ noinline default: () -> E
+ ): ManagedOption<E> where E : Enum<E>, E : StringIdentifiable {
+ return choice(propertyName, E::class.java, default)
+ }
+
+ private fun <E> createStringIdentifiable(x: () -> Array<out E>): Codec<E> where E : Enum<E>, E : StringIdentifiable {
+ return StringIdentifiable.createCodec { x() }
+ }
+
+ // TODO: wait on https://youtrack.jetbrains.com/issue/KT-73434
+// protected inline fun <reified E> choice(
+// propertyName: String,
+// noinline default: () -> E
+// ): ManagedOption<E> where E : Enum<E>, E : StringIdentifiable {
+// return choice(
+// propertyName,
+// enumEntries<E>().toList(),
+// StringIdentifiable.createCodec { enumValues<E>() },
+// EnumRenderer.default(),
+// default
+// )
+// }
+ open fun onChange(option: ManagedOption<*>) {
+ }
+
+ protected fun duration(
+ propertyName: String,
+ min: Duration,
+ max: Duration,
+ default: () -> Duration,
+ ): ManagedOption<Duration> {
+ return option(propertyName, default, DurationHandler(this, min, max))
+ }
+
+
+ protected fun position(
+ propertyName: String,
+ width: Int,
+ height: Int,
+ default: () -> Vector2i,
+ ): ManagedOption<HudMeta> {
+ val label = Text.translatable("firmament.config.${name}.${propertyName}")
+ return option(propertyName, {
+ val p = default()
+ HudMeta(HudPosition(p.x(), p.y(), 1F), Firmament.identifier(propertyName), label, width, height)
+ }, HudMetaHandler(this, propertyName, label, width, height))
+ }
+
+ protected fun keyBinding(
+ propertyName: String,
+ default: () -> Int,
+ ): ManagedOption<SavedKeyBinding> = keyBindingWithOutDefaultModifiers(propertyName) {
+ SavedKeyBinding.Companion.keyWithoutMods(default())
+ }
+
+ protected fun keyBindingWithOutDefaultModifiers(
+ propertyName: String,
+ default: () -> SavedKeyBinding,
+ ): ManagedOption<SavedKeyBinding> {
+ return option(propertyName, default, KeyBindingHandler("firmament.config.${name}.${propertyName}", this))
+ }
+
+ protected fun keyBindingWithDefaultUnbound(
+ propertyName: String,
+ ): ManagedOption<SavedKeyBinding> {
+ return keyBindingWithOutDefaultModifiers(propertyName) { SavedKeyBinding.Companion.unbound() }
+ }
+
+ protected fun integer(
+ propertyName: String,
+ min: Int,
+ max: Int,
+ default: () -> Int,
+ ): ManagedOption<Int> {
+ return option(propertyName, default, IntegerHandler(this, min, max))
+ }
+
+ protected fun button(propertyName: String, runnable: () -> Unit): ManagedOption<Unit> {
+ return option(propertyName, { }, ClickHandler(this, runnable))
+ }
+
+ protected fun string(propertyName: String, default: () -> String): ManagedOption<String> {
+ return option(propertyName, default, StringHandler(this))
+ }
+
+
+ fun reloadGui() {
+ latestGuiAppender?.reloadables?.forEach { it() }
+ }
+
+ val translationKey get() = "firmament.config.${name}"
+ val labelText: Text = Text.translatable(translationKey)
+
+ fun getConfigEditor(parent: Screen? = null): Screen {
+ var screen: Screen? = null
+ val guiapp = GuiAppender(400) { requireNotNull(screen) { "Screen Accessor called too early" } }
+ latestGuiAppender = guiapp
+ guiapp.appendFullRow(
+ RowComponent(
+ FirmButtonComponent(TextComponent("←")) {
+ if (parent != null) {
+ markDirty()
+ ScreenUtil.setScreenLater(parent)
+ } else {
+ AllConfigsGui.showAllGuis()
+ }
+ }
+ ))
+ sortedOptions.forEach { it.appendToGui(guiapp) }
+ guiapp.reloadables.forEach { it() }
+ val component = CenterComponent(
+ PanelComponent(
+ ScrollPanelComponent(400, 300, ColumnComponent(guiapp.panel)),
+ 10,
+ PanelComponent.DefaultBackgroundRenderer.VANILLA
+ )
+ )
+ screen = object : MoulConfigScreenComponent(Text.empty(), GuiContext(component), parent) {
+ override fun close() {
+ if (guiContext.onBeforeClose() == CloseEventListener.CloseAction.NO_OBJECTIONS_TO_CLOSE) {
+ client!!.setScreen(parent)
+ }
+ }
+ }
+ return screen
+ }
+
+ fun showConfigEditor(parent: Screen? = null) {
+ ScreenUtil.setScreenLater(getConfigEditor(parent))
+ }
+
+}