rudimentary config gui (again)
This commit is contained in:
1
TODO.txt
1
TODO.txt
@@ -1,4 +1,3 @@
|
|||||||
- translations
|
|
||||||
- recipes
|
- recipes
|
||||||
- fairy souls
|
- fairy souls
|
||||||
- easy config gui builder
|
- easy config gui builder
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import moe.nea.notenoughupdates.dbus.NEUDbusObject
|
|||||||
import moe.nea.notenoughupdates.features.FeatureManager
|
import moe.nea.notenoughupdates.features.FeatureManager
|
||||||
import moe.nea.notenoughupdates.repo.RepoManager
|
import moe.nea.notenoughupdates.repo.RepoManager
|
||||||
import moe.nea.notenoughupdates.util.SBData
|
import moe.nea.notenoughupdates.util.SBData
|
||||||
import moe.nea.notenoughupdates.util.config.IConfigHolder
|
import moe.nea.notenoughupdates.util.data.IDataHolder
|
||||||
|
|
||||||
object NotEnoughUpdates : ModInitializer, ClientModInitializer {
|
object NotEnoughUpdates : ModInitializer, ClientModInitializer {
|
||||||
const val MOD_ID = "notenoughupdates"
|
const val MOD_ID = "notenoughupdates"
|
||||||
@@ -72,7 +72,7 @@ object NotEnoughUpdates : ModInitializer, ClientModInitializer {
|
|||||||
override fun onInitialize() {
|
override fun onInitialize() {
|
||||||
dbusConnection.requestBusName("moe.nea.notenoughupdates")
|
dbusConnection.requestBusName("moe.nea.notenoughupdates")
|
||||||
dbusConnection.exportObject(NEUDbusObject)
|
dbusConnection.exportObject(NEUDbusObject)
|
||||||
IConfigHolder.registerEvents()
|
IDataHolder.registerEvents()
|
||||||
RepoManager.initialize()
|
RepoManager.initialize()
|
||||||
SBData.init()
|
SBData.init()
|
||||||
FeatureManager.autoload()
|
FeatureManager.autoload()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.mojang.brigadier.CommandDispatcher
|
|||||||
import io.github.cottonmc.cotton.gui.client.CottonClientScreen
|
import io.github.cottonmc.cotton.gui.client.CottonClientScreen
|
||||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
|
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
|
import moe.nea.notenoughupdates.features.world.FairySouls
|
||||||
import moe.nea.notenoughupdates.gui.repoGui
|
import moe.nea.notenoughupdates.gui.repoGui
|
||||||
import moe.nea.notenoughupdates.repo.RepoManager
|
import moe.nea.notenoughupdates.repo.RepoManager
|
||||||
import moe.nea.notenoughupdates.util.SBData
|
import moe.nea.notenoughupdates.util.SBData
|
||||||
@@ -29,6 +30,11 @@ fun neuCommand() = literal("neu") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
thenLiteral("dev") {
|
thenLiteral("dev") {
|
||||||
|
thenLiteral("config") {
|
||||||
|
thenExecute {
|
||||||
|
FairySouls.TConfig.showConfigEditor()
|
||||||
|
}
|
||||||
|
}
|
||||||
thenLiteral("sbdata") {
|
thenLiteral("sbdata") {
|
||||||
thenExecute {
|
thenExecute {
|
||||||
source.sendFeedback(Text.translatable("notenoughupdates.sbinfo.profile", SBData.profileCuteName))
|
source.sendFeedback(Text.translatable("notenoughupdates.sbinfo.profile", SBData.profileCuteName))
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import kotlinx.serialization.Serializable
|
|||||||
import kotlinx.serialization.serializer
|
import kotlinx.serialization.serializer
|
||||||
import moe.nea.notenoughupdates.NotEnoughUpdates
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
import moe.nea.notenoughupdates.features.world.FairySouls
|
import moe.nea.notenoughupdates.features.world.FairySouls
|
||||||
import moe.nea.notenoughupdates.util.config.ConfigHolder
|
import moe.nea.notenoughupdates.util.data.DataHolder
|
||||||
|
|
||||||
object FeatureManager : ConfigHolder<FeatureManager.Config>(serializer(), "features", ::Config) {
|
object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "features", ::Config) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Config(
|
data class Config(
|
||||||
val enabledFeatures: MutableMap<String, Boolean> = mutableMapOf()
|
val enabledFeatures: MutableMap<String, Boolean> = mutableMapOf()
|
||||||
@@ -40,11 +40,11 @@ object FeatureManager : ConfigHolder<FeatureManager.Config>(serializer(), "featu
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isEnabled(identifier: String): Boolean? =
|
fun isEnabled(identifier: String): Boolean? =
|
||||||
config.enabledFeatures[identifier]
|
data.enabledFeatures[identifier]
|
||||||
|
|
||||||
|
|
||||||
fun setEnabled(identifier: String, value: Boolean) {
|
fun setEnabled(identifier: String, value: Boolean) {
|
||||||
config.enabledFeatures[identifier] = value
|
data.enabledFeatures[identifier] = value
|
||||||
markDirty()
|
markDirty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package moe.nea.notenoughupdates.features.world
|
|||||||
import io.github.moulberry.repo.data.Coordinate
|
import io.github.moulberry.repo.data.Coordinate
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.serializer
|
import kotlinx.serialization.serializer
|
||||||
|
import net.minecraft.client.MinecraftClient
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import moe.nea.notenoughupdates.events.ServerChatLineReceivedEvent
|
import moe.nea.notenoughupdates.events.ServerChatLineReceivedEvent
|
||||||
import moe.nea.notenoughupdates.events.SkyblockServerUpdateEvent
|
import moe.nea.notenoughupdates.events.SkyblockServerUpdateEvent
|
||||||
@@ -11,7 +12,8 @@ import moe.nea.notenoughupdates.features.NEUFeature
|
|||||||
import moe.nea.notenoughupdates.repo.RepoManager
|
import moe.nea.notenoughupdates.repo.RepoManager
|
||||||
import moe.nea.notenoughupdates.util.MC
|
import moe.nea.notenoughupdates.util.MC
|
||||||
import moe.nea.notenoughupdates.util.SBData
|
import moe.nea.notenoughupdates.util.SBData
|
||||||
import moe.nea.notenoughupdates.util.config.ProfileSpecificConfigHolder
|
import moe.nea.notenoughupdates.util.config.ManagedConfig
|
||||||
|
import moe.nea.notenoughupdates.util.data.ProfileSpecificDataHolder
|
||||||
import moe.nea.notenoughupdates.util.render.RenderBlockContext.Companion.renderBlocks
|
import moe.nea.notenoughupdates.util.render.RenderBlockContext.Companion.renderBlocks
|
||||||
import moe.nea.notenoughupdates.util.unformattedString
|
import moe.nea.notenoughupdates.util.unformattedString
|
||||||
|
|
||||||
@@ -19,12 +21,22 @@ val Coordinate.blockPos: BlockPos
|
|||||||
get() = BlockPos(x, y, z)
|
get() = BlockPos(x, y, z)
|
||||||
|
|
||||||
object FairySouls : NEUFeature,
|
object FairySouls : NEUFeature,
|
||||||
ProfileSpecificConfigHolder<FairySouls.Config>(serializer(), "fairy-souls.json", ::Config) {
|
ProfileSpecificDataHolder<FairySouls.Config>(serializer(), "found-fairysouls", ::Config) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Config(
|
data class Config(
|
||||||
val foundSouls: MutableMap<String, MutableSet<Int>> = mutableMapOf()
|
val foundSouls: MutableMap<String, MutableSet<Int>> = mutableMapOf()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object TConfig : ManagedConfig("fairysouls") {
|
||||||
|
|
||||||
|
val displaySouls by toggle("show") { false }
|
||||||
|
val resetSouls by button("reset") {
|
||||||
|
FairySouls.data?.foundSouls?.clear() != null
|
||||||
|
updateMissingSouls()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override val name: String get() = "Fairy Souls"
|
override val name: String get() = "Fairy Souls"
|
||||||
override val identifier: String get() = "fairy-souls"
|
override val identifier: String get() = "fairy-souls"
|
||||||
|
|
||||||
@@ -37,7 +49,7 @@ object FairySouls : NEUFeature,
|
|||||||
|
|
||||||
fun updateMissingSouls() {
|
fun updateMissingSouls() {
|
||||||
currentMissingSouls = emptyList()
|
currentMissingSouls = emptyList()
|
||||||
val c = config ?: return
|
val c = data ?: return
|
||||||
val fi = c.foundSouls[currentLocationName] ?: setOf()
|
val fi = c.foundSouls[currentLocationName] ?: setOf()
|
||||||
val cms = currentLocationSouls.toMutableList()
|
val cms = currentLocationSouls.toMutableList()
|
||||||
fi.asSequence().sortedDescending().filter { it in cms.indices }.forEach { cms.removeAt(it) }
|
fi.asSequence().sortedDescending().filter { it in cms.indices }.forEach { cms.removeAt(it) }
|
||||||
@@ -65,7 +77,7 @@ object FairySouls : NEUFeature,
|
|||||||
|
|
||||||
private fun markNearestSoul() {
|
private fun markNearestSoul() {
|
||||||
val nearestSoul = findNearestClickableSoul() ?: return
|
val nearestSoul = findNearestClickableSoul() ?: return
|
||||||
val c = config ?: return
|
val c = data ?: return
|
||||||
val loc = currentLocationName ?: return
|
val loc = currentLocationName ?: return
|
||||||
val idx = currentLocationSouls.indexOf(nearestSoul)
|
val idx = currentLocationSouls.indexOf(nearestSoul)
|
||||||
c.foundSouls.computeIfAbsent(loc) { mutableSetOf() }.add(idx)
|
c.foundSouls.computeIfAbsent(loc) { mutableSetOf() }.add(idx)
|
||||||
@@ -92,6 +104,7 @@ object FairySouls : NEUFeature,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
WorldRenderLastEvent.subscribe {
|
WorldRenderLastEvent.subscribe {
|
||||||
|
if (!TConfig.displaySouls) return@subscribe
|
||||||
renderBlocks(it.camera) {
|
renderBlocks(it.camera) {
|
||||||
color(1F, 1F, 0F, 0.8F)
|
color(1F, 1F, 0F, 0.8F)
|
||||||
currentMissingSouls.forEach {
|
currentMissingSouls.forEach {
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment
|
|||||||
import io.github.cottonmc.cotton.gui.widget.data.Insets
|
import io.github.cottonmc.cotton.gui.widget.data.Insets
|
||||||
import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment
|
import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment
|
||||||
import moe.nea.notenoughupdates.NotEnoughUpdates
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
import moe.nea.notenoughupdates.util.config.ConfigHolder
|
import moe.nea.notenoughupdates.util.data.DataHolder
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import kotlin.reflect.KMutableProperty1
|
import kotlin.reflect.KMutableProperty1
|
||||||
|
|
||||||
class ConfigGui<K>(val holder: ConfigHolder<K>, val build: ConfigGui<K>.() -> Unit) : LightweightGuiDescription() {
|
class ConfigGui<K>(val holder: DataHolder<K>, val build: ConfigGui<K>.() -> Unit) : LightweightGuiDescription() {
|
||||||
private val root = WGridPanelWithPadding(verticalPadding = 4)
|
private val root = WGridPanelWithPadding(verticalPadding = 4)
|
||||||
private val reloadables = mutableListOf<(() -> Unit)>()
|
private val reloadables = mutableListOf<(() -> Unit)>()
|
||||||
|
|
||||||
@@ -43,9 +43,9 @@ class ConfigGui<K>(val holder: ConfigHolder<K>, val build: ConfigGui<K>.() -> Un
|
|||||||
|
|
||||||
fun toggle(text: Text, prop: KMutableProperty1<K, Boolean>) {
|
fun toggle(text: Text, prop: KMutableProperty1<K, Boolean>) {
|
||||||
val toggle = WToggleButton(text)
|
val toggle = WToggleButton(text)
|
||||||
reloadables.add { toggle.toggle = prop.get(holder.config) }
|
reloadables.add { toggle.toggle = prop.get(holder.data) }
|
||||||
toggle.setOnToggle {
|
toggle.setOnToggle {
|
||||||
prop.set(holder.config, true)
|
prop.set(holder.data, true)
|
||||||
holder.markDirty()
|
holder.markDirty()
|
||||||
}
|
}
|
||||||
root.add(toggle, 5, col, 6, 1)
|
root.add(toggle, 5, col, 6, 1)
|
||||||
@@ -72,11 +72,11 @@ class ConfigGui<K>(val holder: ConfigHolder<K>, val build: ConfigGui<K>.() -> Un
|
|||||||
val textfield = WTextField(background)
|
val textfield = WTextField(background)
|
||||||
textfield.isEditable = true
|
textfield.isEditable = true
|
||||||
reloadables.add {
|
reloadables.add {
|
||||||
textfield.text = prop.get(holder.config)
|
textfield.text = prop.get(holder.data)
|
||||||
}
|
}
|
||||||
textfield.maxLength = maxLength
|
textfield.maxLength = maxLength
|
||||||
textfield.setChangedListener {
|
textfield.setChangedListener {
|
||||||
prop.set(holder.config, it)
|
prop.set(holder.data, it)
|
||||||
holder.markDirty()
|
holder.markDirty()
|
||||||
}
|
}
|
||||||
root.add(textfield, 5, col, 6, 11)
|
root.add(textfield, 5, col, 6, 11)
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ fun repoGui(): ConfigGui<RepoManager.Config> {
|
|||||||
Text.translatable("notenoughupdates.gui.repo.reset.label"),
|
Text.translatable("notenoughupdates.gui.repo.reset.label"),
|
||||||
Text.translatable("notenoughupdates.gui.repo.reset"),
|
Text.translatable("notenoughupdates.gui.repo.reset"),
|
||||||
) {
|
) {
|
||||||
RepoManager.config.user = "NotEnoughUpdates"
|
RepoManager.data.user = "NotEnoughUpdates"
|
||||||
RepoManager.config.repo = "NotEnoughUpdates-REPO"
|
RepoManager.data.repo = "NotEnoughUpdates-REPO"
|
||||||
RepoManager.config.branch = "dangerous"
|
RepoManager.data.branch = "dangerous"
|
||||||
reload()
|
reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ object RepoDownloadManager {
|
|||||||
|
|
||||||
private suspend fun requestLatestGithubSha(): String? {
|
private suspend fun requestLatestGithubSha(): String? {
|
||||||
val response =
|
val response =
|
||||||
NotEnoughUpdates.httpClient.get("https://api.github.com/repos/${RepoManager.config.user}/${RepoManager.config.repo}/commits/${RepoManager.config.branch}")
|
NotEnoughUpdates.httpClient.get("https://api.github.com/repos/${RepoManager.data.user}/${RepoManager.data.repo}/commits/${RepoManager.data.branch}")
|
||||||
if (response.status.value != 200) {
|
if (response.status.value != 200) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ object RepoDownloadManager {
|
|||||||
}
|
}
|
||||||
val currentSha = loadSavedVersionHash()
|
val currentSha = loadSavedVersionHash()
|
||||||
if (latestSha != currentSha || force) {
|
if (latestSha != currentSha || force) {
|
||||||
val requestUrl = "https://github.com/${RepoManager.config.user}/${RepoManager.config.repo}/archive/$latestSha.zip"
|
val requestUrl = "https://github.com/${RepoManager.data.user}/${RepoManager.data.repo}/archive/$latestSha.zip"
|
||||||
logger.info("Planning to upgrade repository from $currentSha to $latestSha from $requestUrl")
|
logger.info("Planning to upgrade repository from $currentSha to $latestSha from $requestUrl")
|
||||||
val zipFile = downloadGithubArchive(requestUrl)
|
val zipFile = downloadGithubArchive(requestUrl)
|
||||||
logger.info("Download repository zip file to $zipFile. Deleting old repository")
|
logger.info("Download repository zip file to $zipFile. Deleting old repository")
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import kotlinx.serialization.serializer
|
|||||||
import moe.nea.notenoughupdates.NotEnoughUpdates
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
import moe.nea.notenoughupdates.NotEnoughUpdates.logger
|
import moe.nea.notenoughupdates.NotEnoughUpdates.logger
|
||||||
import moe.nea.notenoughupdates.hud.ProgressBar
|
import moe.nea.notenoughupdates.hud.ProgressBar
|
||||||
import moe.nea.notenoughupdates.util.config.ConfigHolder
|
import moe.nea.notenoughupdates.util.data.DataHolder
|
||||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
|
||||||
import net.minecraft.client.MinecraftClient
|
import net.minecraft.client.MinecraftClient
|
||||||
import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket
|
import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
|
|
||||||
object RepoManager : ConfigHolder<RepoManager.Config>(serializer(), "repo", ::Config) {
|
object RepoManager : DataHolder<RepoManager.Config>(serializer(), "repo", ::Config) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Config(
|
data class Config(
|
||||||
var user: String = "NotEnoughUpdates",
|
var user: String = "NotEnoughUpdates",
|
||||||
@@ -80,7 +80,7 @@ object RepoManager : ConfigHolder<RepoManager.Config>(serializer(), "repo", ::Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun initialize() {
|
fun initialize() {
|
||||||
if (config.autoUpdate) {
|
if (data.autoUpdate) {
|
||||||
launchAsyncUpdate()
|
launchAsyncUpdate()
|
||||||
} else {
|
} else {
|
||||||
reload()
|
reload()
|
||||||
|
|||||||
@@ -0,0 +1,206 @@
|
|||||||
|
package moe.nea.notenoughupdates.util.config
|
||||||
|
|
||||||
|
import io.github.cottonmc.cotton.gui.client.CottonClientScreen
|
||||||
|
import io.github.cottonmc.cotton.gui.client.LibGui
|
||||||
|
import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription
|
||||||
|
import io.github.cottonmc.cotton.gui.widget.WButton
|
||||||
|
import io.github.cottonmc.cotton.gui.widget.WLabel
|
||||||
|
import io.github.cottonmc.cotton.gui.widget.WToggleButton
|
||||||
|
import io.github.cottonmc.cotton.gui.widget.WWidget
|
||||||
|
import io.github.cottonmc.cotton.gui.widget.data.Insets
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
import kotlinx.serialization.json.boolean
|
||||||
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
|
import kotlin.io.path.createDirectories
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
import kotlin.io.path.writeText
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
import net.minecraft.client.MinecraftClient
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
|
import moe.nea.notenoughupdates.gui.WGridPanelWithPadding
|
||||||
|
import moe.nea.notenoughupdates.util.ScreenUtil.setScreenLater
|
||||||
|
|
||||||
|
abstract class ManagedConfig(val name: String) {
|
||||||
|
|
||||||
|
class GuiAppender(val width: Int) {
|
||||||
|
private var row = 0
|
||||||
|
internal val panel = WGridPanelWithPadding(verticalPadding = 4, horizontalPadding = 4)
|
||||||
|
internal val reloadables = mutableListOf<(() -> Unit)>()
|
||||||
|
fun set(x: Int, y: Int, w: Int, h: Int, widget: WWidget) {
|
||||||
|
panel.add(widget, x, y, w, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun onReload(reloadable: () -> Unit) {
|
||||||
|
reloadables.add(reloadable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun skipRows(r: Int) {
|
||||||
|
row += r
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendSplitRow(left: WWidget, right: WWidget) {
|
||||||
|
val lw = width / 2
|
||||||
|
set(0, row, lw, 1, left)
|
||||||
|
set(lw, row, width - lw, 1, right)
|
||||||
|
skipRows(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendFullRow(widget: WWidget) {
|
||||||
|
set(0, row, width, 1, widget)
|
||||||
|
skipRows(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OptionHandler<T : Any> {
|
||||||
|
fun toJson(element: T): JsonElement?
|
||||||
|
fun fromJson(element: JsonElement): T
|
||||||
|
fun emitGuiElements(opt: Option<T>, guiAppender: GuiAppender)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class Option<T : Any> internal constructor(
|
||||||
|
val propertyName: String,
|
||||||
|
val default: () -> T,
|
||||||
|
val handler: OptionHandler<T>
|
||||||
|
) : ReadOnlyProperty<Any?, T> {
|
||||||
|
|
||||||
|
private lateinit var _value: T
|
||||||
|
private var loaded = false
|
||||||
|
var value: T
|
||||||
|
get() {
|
||||||
|
if (!loaded)
|
||||||
|
load()
|
||||||
|
return _value
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
loaded = true
|
||||||
|
_value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun load() {
|
||||||
|
if (data.containsKey(propertyName)) {
|
||||||
|
try {
|
||||||
|
value = handler.fromJson(data[propertyName]!!)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
NotEnoughUpdates.logger.error(
|
||||||
|
"Exception during loading of config file $name. This will reset this config.",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value = default()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun toJson(): JsonElement? {
|
||||||
|
return handler.toJson(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendToGui(guiapp: GuiAppender) {
|
||||||
|
handler.emitGuiElements(this, guiapp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val file = NotEnoughUpdates.CONFIG_DIR.resolve("$name.json")
|
||||||
|
val data: JsonObject by lazy {
|
||||||
|
try {
|
||||||
|
NotEnoughUpdates.json.decodeFromString(
|
||||||
|
file.readText()
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
NotEnoughUpdates.logger.info("Could not read config $name. Loading empty config.")
|
||||||
|
JsonObject(mutableMapOf())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save() {
|
||||||
|
val data = JsonObject(allOptions.mapNotNull { (key, value) ->
|
||||||
|
value.toJson()?.let {
|
||||||
|
key to it
|
||||||
|
}
|
||||||
|
}.toMap())
|
||||||
|
file.parent.createDirectories()
|
||||||
|
file.writeText(NotEnoughUpdates.json.encodeToString(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val allOptions = mutableMapOf<String, Option<*>>()
|
||||||
|
val sortedOptions = mutableListOf<Option<*>>()
|
||||||
|
|
||||||
|
protected fun <T : Any> option(propertyName: String, default: () -> T, handler: OptionHandler<T>): Option<T> {
|
||||||
|
if (propertyName in allOptions) error("Cannot register the same name twice")
|
||||||
|
return Option(propertyName, default, handler).also {
|
||||||
|
allOptions[propertyName] = it
|
||||||
|
sortedOptions.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BooleanHandler(val config: ManagedConfig) : OptionHandler<Boolean> {
|
||||||
|
override fun toJson(element: Boolean): JsonElement? {
|
||||||
|
return JsonPrimitive(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fromJson(element: JsonElement): Boolean {
|
||||||
|
return element.jsonPrimitive.boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun emitGuiElements(opt: Option<Boolean>, guiAppender: GuiAppender) {
|
||||||
|
guiAppender.appendFullRow(
|
||||||
|
WToggleButton(Text.translatable("neu.config.${config.name}.${opt.propertyName}")).apply {
|
||||||
|
guiAppender.onReload { toggle = opt.value }
|
||||||
|
setOnToggle {
|
||||||
|
opt.value = it
|
||||||
|
config.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClickHandler(val config: ManagedConfig, val runnable: () -> Unit) : OptionHandler<Unit> {
|
||||||
|
override fun toJson(element: Unit): JsonElement? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fromJson(element: JsonElement) {}
|
||||||
|
|
||||||
|
override fun emitGuiElements(opt: Option<Unit>, guiAppender: GuiAppender) {
|
||||||
|
guiAppender.appendSplitRow(
|
||||||
|
WLabel(Text.translatable("neu.config.${config.name}.${opt.propertyName}")),
|
||||||
|
WButton(Text.translatable("neu.config.${config.name}.${opt.propertyName}")).apply {
|
||||||
|
setOnClick {
|
||||||
|
runnable()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun toggle(propertyName: String, default: () -> Boolean): Option<Boolean> {
|
||||||
|
return option(propertyName, default, BooleanHandler(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showConfigEditor() {
|
||||||
|
val lwgd = LightweightGuiDescription()
|
||||||
|
val guiapp = GuiAppender(20)
|
||||||
|
guiapp.panel.insets = Insets.ROOT_PANEL
|
||||||
|
sortedOptions.forEach { it.appendToGui(guiapp) }
|
||||||
|
guiapp.reloadables.forEach { it() }
|
||||||
|
lwgd.setRootPanel(guiapp.panel)
|
||||||
|
setScreenLater(CottonClientScreen(lwgd))
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun button(propertyName: String, runnable: () -> Unit): Option<Unit> {
|
||||||
|
return option(propertyName, { }, ClickHandler(this, runnable))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package moe.nea.notenoughupdates.util.config
|
package moe.nea.notenoughupdates.util.data
|
||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
@@ -7,19 +7,19 @@ import kotlin.io.path.readText
|
|||||||
import kotlin.io.path.writeText
|
import kotlin.io.path.writeText
|
||||||
import moe.nea.notenoughupdates.NotEnoughUpdates
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
|
|
||||||
abstract class ConfigHolder<T>(
|
abstract class DataHolder<T>(
|
||||||
val serializer: KSerializer<T>,
|
val serializer: KSerializer<T>,
|
||||||
val name: String,
|
val name: String,
|
||||||
val default: () -> T
|
val default: () -> T
|
||||||
) : IConfigHolder<T> {
|
) : IDataHolder<T> {
|
||||||
|
|
||||||
|
|
||||||
final override var config: T
|
final override var data: T
|
||||||
private set
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
config = readValueOrDefault()
|
data = readValueOrDefault()
|
||||||
IConfigHolder.putConfig(this::class, this)
|
IDataHolder.putDataHolder(this::class, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val file: Path get() = NotEnoughUpdates.CONFIG_DIR.resolve("$name.json")
|
private val file: Path get() = NotEnoughUpdates.CONFIG_DIR.resolve("$name.json")
|
||||||
@@ -32,7 +32,7 @@ abstract class ConfigHolder<T>(
|
|||||||
file.readText()
|
file.readText()
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {/* Expecting IOException and SerializationException, but Kotlin doesn't allow multi catches*/
|
} catch (e: Exception) {/* Expecting IOException and SerializationException, but Kotlin doesn't allow multi catches*/
|
||||||
IConfigHolder.badLoads.add(name)
|
IDataHolder.badLoads.add(name)
|
||||||
NotEnoughUpdates.logger.error(
|
NotEnoughUpdates.logger.error(
|
||||||
"Exception during loading of config file $name. This will reset this config.",
|
"Exception during loading of config file $name. This will reset this config.",
|
||||||
e
|
e
|
||||||
@@ -46,15 +46,15 @@ abstract class ConfigHolder<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun save() {
|
override fun save() {
|
||||||
writeValue(config)
|
writeValue(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun load() {
|
override fun load() {
|
||||||
config = readValueOrDefault()
|
data = readValueOrDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty() {
|
override fun markDirty() {
|
||||||
IConfigHolder.markDirty(this::class)
|
IDataHolder.markDirty(this::class)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package moe.nea.notenoughupdates.util.config
|
package moe.nea.notenoughupdates.util.data
|
||||||
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
|
||||||
@@ -9,17 +9,17 @@ import net.minecraft.text.Text
|
|||||||
import moe.nea.notenoughupdates.NotEnoughUpdates
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
import moe.nea.notenoughupdates.events.ScreenOpenEvent
|
import moe.nea.notenoughupdates.events.ScreenOpenEvent
|
||||||
|
|
||||||
interface IConfigHolder<T> {
|
interface IDataHolder<T> {
|
||||||
companion object {
|
companion object {
|
||||||
internal var badLoads: MutableList<String> = CopyOnWriteArrayList()
|
internal var badLoads: MutableList<String> = CopyOnWriteArrayList()
|
||||||
private val allConfigs: MutableMap<KClass<out IConfigHolder<*>>, IConfigHolder<*>> = mutableMapOf()
|
private val allConfigs: MutableMap<KClass<out IDataHolder<*>>, IDataHolder<*>> = mutableMapOf()
|
||||||
private val dirty: MutableSet<KClass<out IConfigHolder<*>>> = mutableSetOf()
|
private val dirty: MutableSet<KClass<out IDataHolder<*>>> = mutableSetOf()
|
||||||
|
|
||||||
internal fun <T : IConfigHolder<K>, K> putConfig(kClass: KClass<T>, inst: IConfigHolder<K>) {
|
internal fun <T : IDataHolder<K>, K> putDataHolder(kClass: KClass<T>, inst: IDataHolder<K>) {
|
||||||
allConfigs[kClass] = inst
|
allConfigs[kClass] = inst
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : IConfigHolder<K>, K> markDirty(kClass: KClass<T>) {
|
fun <T : IDataHolder<K>, K> markDirty(kClass: KClass<T>) {
|
||||||
if (kClass !in allConfigs) {
|
if (kClass !in allConfigs) {
|
||||||
NotEnoughUpdates.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'IConfigHolder'")
|
NotEnoughUpdates.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'IConfigHolder'")
|
||||||
return
|
return
|
||||||
@@ -68,7 +68,7 @@ interface IConfigHolder<T> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val config: T
|
val data: T
|
||||||
fun save()
|
fun save()
|
||||||
fun markDirty()
|
fun markDirty()
|
||||||
fun load()
|
fun load()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package moe.nea.notenoughupdates.util.config
|
package moe.nea.notenoughupdates.util.data
|
||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
@@ -13,15 +13,15 @@ import kotlin.io.path.writeText
|
|||||||
import moe.nea.notenoughupdates.NotEnoughUpdates
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
import moe.nea.notenoughupdates.util.SBData
|
import moe.nea.notenoughupdates.util.SBData
|
||||||
|
|
||||||
abstract class ProfileSpecificConfigHolder<S>(
|
abstract class ProfileSpecificDataHolder<S>(
|
||||||
private val configSerializer: KSerializer<S>,
|
private val dataSerializer: KSerializer<S>,
|
||||||
val configName: String,
|
val configName: String,
|
||||||
private val configDefault: () -> S
|
private val configDefault: () -> S
|
||||||
) : IConfigHolder<S?> {
|
) : IDataHolder<S?> {
|
||||||
|
|
||||||
var allConfigs: MutableMap<String, S>
|
var allConfigs: MutableMap<String, S>
|
||||||
|
|
||||||
override val config: S?
|
override val data: S?
|
||||||
get() = SBData.profileCuteName?.let {
|
get() = SBData.profileCuteName?.let {
|
||||||
allConfigs.computeIfAbsent(it) { configDefault() }
|
allConfigs.computeIfAbsent(it) { configDefault() }
|
||||||
}
|
}
|
||||||
@@ -29,10 +29,10 @@ abstract class ProfileSpecificConfigHolder<S>(
|
|||||||
init {
|
init {
|
||||||
allConfigs = readValues()
|
allConfigs = readValues()
|
||||||
readValues()
|
readValues()
|
||||||
IConfigHolder.putConfig(this::class, this)
|
IDataHolder.putDataHolder(this::class, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val configDirectory: Path get() = NotEnoughUpdates.CONFIG_DIR.resolve("profiles")
|
private val configDirectory: Path get() = NotEnoughUpdates.CONFIG_DIR.resolve("profiles").resolve(configName)
|
||||||
|
|
||||||
private fun readValues(): MutableMap<String, S> {
|
private fun readValues(): MutableMap<String, S> {
|
||||||
if (!configDirectory.exists()) {
|
if (!configDirectory.exists()) {
|
||||||
@@ -43,9 +43,9 @@ abstract class ProfileSpecificConfigHolder<S>(
|
|||||||
.filter { it.extension == "json" }
|
.filter { it.extension == "json" }
|
||||||
.mapNotNull {
|
.mapNotNull {
|
||||||
try {
|
try {
|
||||||
it.nameWithoutExtension to NotEnoughUpdates.json.decodeFromString(configSerializer, it.readText())
|
it.nameWithoutExtension to NotEnoughUpdates.json.decodeFromString(dataSerializer, it.readText())
|
||||||
} catch (e: Exception) { /* Expecting IOException and SerializationException, but Kotlin doesn't allow multi catches*/
|
} catch (e: Exception) { /* Expecting IOException and SerializationException, but Kotlin doesn't allow multi catches*/
|
||||||
IConfigHolder.badLoads.add(configName)
|
IDataHolder.badLoads.add(configName)
|
||||||
NotEnoughUpdates.logger.error(
|
NotEnoughUpdates.logger.error(
|
||||||
"Exception during loading of profile specific config file $it ($configName). This will reset that profiles config.",
|
"Exception during loading of profile specific config file $it ($configName). This will reset that profiles config.",
|
||||||
e
|
e
|
||||||
@@ -67,12 +67,12 @@ abstract class ProfileSpecificConfigHolder<S>(
|
|||||||
}
|
}
|
||||||
c.forEach { (name, value) ->
|
c.forEach { (name, value) ->
|
||||||
val f = configDirectory.resolve("$name.json")
|
val f = configDirectory.resolve("$name.json")
|
||||||
f.writeText(NotEnoughUpdates.json.encodeToString(configSerializer, value))
|
f.writeText(NotEnoughUpdates.json.encodeToString(dataSerializer, value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty() {
|
override fun markDirty() {
|
||||||
IConfigHolder.markDirty(this::class)
|
IDataHolder.markDirty(this::class)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun load() {
|
override fun load() {
|
||||||
Reference in New Issue
Block a user