Refactor source layout

Introduce compat source sets and move all kotlin sources to the main directory

[no changelog]
This commit is contained in:
Linnea Gräf
2024-08-28 19:04:24 +02:00
parent a690630816
commit d2f240ff0c
251 changed files with 295 additions and 38 deletions

View File

@@ -0,0 +1,46 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.observer.ObservableList
import io.github.notenoughupdates.moulconfig.xml.Bind
import net.minecraft.client.gui.screen.Screen
import net.minecraft.text.Text
import moe.nea.firmament.features.FeatureManager
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.MoulConfigUtils
import moe.nea.firmament.util.ScreenUtil.setScreenLater
object AllConfigsGui {
val allConfigs
get() = listOf(
RepoManager.Config
) + FeatureManager.allFeatures.mapNotNull { it.config }
fun <T> List<T>.toObservableList(): ObservableList<T> = ObservableList(this)
class MainMapping(val allConfigs: List<ManagedConfig>) {
@get:Bind("configs")
val configs = allConfigs.map { EntryMapping(it) }.toObservableList()
class EntryMapping(val config: ManagedConfig) {
@Bind
fun name() = Text.translatable("firmament.config.${config.name}").string
@Bind
fun openEditor() {
config.showConfigEditor(MC.screen)
}
}
}
fun makeScreen(parent: Screen? = null): Screen {
return MoulConfigUtils.loadScreen("config/main", MainMapping(allConfigs), parent)
}
fun showAllGuis() {
setScreenLater(makeScreen())
}
}

View File

@@ -0,0 +1,37 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.gui.component.CenterComponent
import io.github.notenoughupdates.moulconfig.gui.component.SwitchComponent
import io.github.notenoughupdates.moulconfig.observer.GetSetter
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.jsonPrimitive
class BooleanHandler(val config: ManagedConfig) : 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: ManagedOption<Boolean>, guiAppender: GuiAppender) {
guiAppender.appendLabeledRow(
opt.labelText,
CenterComponent(SwitchComponent(object : GetSetter<Boolean> {
override fun get(): Boolean {
return opt.get()
}
override fun set(newValue: Boolean) {
opt.set(newValue)
config.save()
}
}, 200)
))
}
}

View File

@@ -0,0 +1,24 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import kotlinx.serialization.json.JsonElement
import moe.nea.firmament.gui.FirmButtonComponent
class ClickHandler(val config: ManagedConfig, val runnable: () -> Unit) : ManagedConfig.OptionHandler<Unit> {
override fun toJson(element: Unit): JsonElement? {
return null
}
override fun fromJson(element: JsonElement) {}
override fun emitGuiElements(opt: ManagedOption<Unit>, guiAppender: GuiAppender) {
guiAppender.appendLabeledRow(
opt.labelText,
FirmButtonComponent(
TextComponent(opt.labelText.string),
action = runnable),
)
}
}

View File

@@ -0,0 +1,58 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.common.IMinecraft
import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
import io.github.notenoughupdates.moulconfig.gui.component.SliderComponent
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import io.github.notenoughupdates.moulconfig.observer.GetSetter
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import kotlin.time.Duration
import kotlin.time.DurationUnit
import kotlin.time.toDuration
import net.minecraft.text.Text
import moe.nea.firmament.util.FirmFormatters
class DurationHandler(val config: ManagedConfig, val min: Duration, val max: Duration) :
ManagedConfig.OptionHandler<Duration> {
override fun toJson(element: Duration): JsonElement? {
return JsonPrimitive(element.inWholeMilliseconds)
}
override fun fromJson(element: JsonElement): Duration {
return element.jsonPrimitive.long.toDuration(DurationUnit.MILLISECONDS)
}
override fun emitGuiElements(opt: ManagedOption<Duration>, guiAppender: GuiAppender) {
guiAppender.appendLabeledRow(
opt.labelText,
RowComponent(
TextComponent(IMinecraft.instance.defaultFontRenderer,
{ FirmFormatters.formatTimespan(opt.value) },
40,
TextComponent.TextAlignment.CENTER,
true,
false),
SliderComponent(
object : GetSetter<Float> {
override fun get(): Float {
return opt.value.toDouble(DurationUnit.SECONDS).toFloat()
}
override fun set(newValue: Float) {
opt.value = newValue.toDouble().toDuration(DurationUnit.SECONDS)
}
},
min.toDouble(DurationUnit.SECONDS).toFloat(),
max.toDouble(DurationUnit.SECONDS).toFloat(),
0.1F,
130
)
))
}
}

View File

@@ -0,0 +1,40 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import io.github.notenoughupdates.moulconfig.observer.GetSetter
import net.minecraft.client.gui.screen.Screen
import net.minecraft.text.Text
import moe.nea.firmament.gui.FixedComponent
class GuiAppender(val width: Int, val screenAccessor: () -> Screen) {
val panel = mutableListOf<GuiComponent>()
internal val reloadables = mutableListOf<(() -> Unit)>()
fun onReload(reloadable: () -> Unit) {
reloadables.add(reloadable)
}
fun appendLabeledRow(label: Text, right: GuiComponent) {
appendSplitRow(
TextComponent(label.string),
right
)
}
fun appendSplitRow(left: GuiComponent, right: GuiComponent) {
// TODO: make this more dynamic
// i could just make a component that allows for using half the available size
appendFullRow(RowComponent(
FixedComponent(GetSetter.constant(width / 2), null, left),
FixedComponent(GetSetter.constant(width / 2), null, right),
))
}
fun appendFullRow(widget: GuiComponent) {
panel.add(widget)
}
}

View File

@@ -0,0 +1,39 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.jarvis.JarvisIntegration
import moe.nea.firmament.util.MC
class HudMetaHandler(val config: ManagedConfig, val label: MutableText, val width: Int, val height: Int) :
ManagedConfig.OptionHandler<HudMeta> {
override fun toJson(element: HudMeta): JsonElement? {
return Json.encodeToJsonElement(element.position)
}
override fun fromJson(element: JsonElement): HudMeta {
return HudMeta(Json.decodeFromJsonElement(element), label, width, height)
}
override fun emitGuiElements(opt: ManagedOption<HudMeta>, guiAppender: GuiAppender) {
guiAppender.appendLabeledRow(
opt.labelText,
FirmButtonComponent(
TextComponent(
Text.stringifiedTranslatable("firmament.hud.edit", label).string),
) {
MC.screen = JarvisIntegration.jarvis.getHudEditor(
guiAppender.screenAccessor.invoke(),
listOf(opt.value)
)
})
}
}

View File

@@ -0,0 +1,54 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.common.IMinecraft
import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
import io.github.notenoughupdates.moulconfig.gui.component.SliderComponent
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import io.github.notenoughupdates.moulconfig.observer.GetSetter
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonPrimitive
import moe.nea.firmament.util.FirmFormatters
class IntegerHandler(val config: ManagedConfig, val min: Int, val max: Int) : ManagedConfig.OptionHandler<Int> {
override fun toJson(element: Int): JsonElement? {
return JsonPrimitive(element)
}
override fun fromJson(element: JsonElement): Int {
return element.jsonPrimitive.int
}
override fun emitGuiElements(opt: ManagedOption<Int>, guiAppender: GuiAppender) {
guiAppender.appendLabeledRow(
opt.labelText,
RowComponent(
TextComponent(IMinecraft.instance.defaultFontRenderer,
{ FirmFormatters.formatCommas(opt.value, 0) },
40,
TextComponent.TextAlignment.CENTER,
true,
false),
SliderComponent(
object : GetSetter<Float> {
override fun get(): Float {
return opt.value.toFloat()
}
override fun set(newValue: Float) {
opt.value = newValue.toInt()
}
},
min.toFloat(),
max.toFloat(),
0.1F,
130
)
))
}
}

View File

@@ -0,0 +1,48 @@
package moe.nea.firmament.gui.config
import moe.nea.jarvis.api.JarvisHud
import moe.nea.jarvis.api.JarvisScalable
import kotlinx.serialization.Serializable
import net.minecraft.text.Text
@Serializable
data class HudPosition(
var x: Double,
var y: Double,
var scale: Float,
)
data class HudMeta(
val position: HudPosition,
private val label: Text,
private val width: Int,
private val height: Int,
) : JarvisScalable, JarvisHud {
override fun getX(): Double = position.x
override fun setX(newX: Double) {
position.x = newX
}
override fun getY(): Double = position.y
override fun setY(newY: Double) {
position.y = newY
}
override fun getLabel(): Text = label
override fun getWidth(): Int = width
override fun getHeight(): Int = height
override fun getScale(): Float = position.scale
override fun setScale(newScale: Float) {
position.scale = newScale
}
}

View File

@@ -0,0 +1,149 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.common.IMinecraft
import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
import io.github.notenoughupdates.moulconfig.deps.libninepatch.NinePatch
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import org.lwjgl.glfw.GLFW
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement
import net.minecraft.text.Text
import net.minecraft.util.Formatting
import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.keybindings.FirmamentKeyBindings
import moe.nea.firmament.keybindings.SavedKeyBinding
class KeyBindingHandler(val name: String, val managedConfig: ManagedConfig) :
ManagedConfig.OptionHandler<SavedKeyBinding> {
override fun initOption(opt: ManagedOption<SavedKeyBinding>) {
FirmamentKeyBindings.registerKeyBinding(name, opt)
}
override fun toJson(element: SavedKeyBinding): JsonElement? {
return Json.encodeToJsonElement(element)
}
override fun fromJson(element: JsonElement): SavedKeyBinding {
return Json.decodeFromJsonElement(element)
}
override fun emitGuiElements(opt: ManagedOption<SavedKeyBinding>, guiAppender: GuiAppender) {
var editing = false
var lastPressed = 0
var lastPressedNonModifier = 0
var label: String = ""
var button: FirmButtonComponent? = null
fun updateLabel() {
var stroke = opt.value.format()
if (editing) {
stroke = Text.literal("")
val (shift, alt, ctrl) = SavedKeyBinding.getMods(SavedKeyBinding.getModInt())
if (shift) {
stroke.append("SHIFT + ")
}
if (alt) {
stroke.append("ALT + ")
}
if (ctrl) {
stroke.append("CTRL + ")
}
stroke.append("???")
stroke.styled { it.withColor(Formatting.YELLOW) }
}
label = (stroke).string
managedConfig.save()
}
button = object : FirmButtonComponent(
TextComponent(
IMinecraft.instance.defaultFontRenderer,
{ label },
130,
TextComponent.TextAlignment.LEFT,
false,
false
), action = {
if (editing) {
button!!.blur()
} else {
editing = true
button!!.requestFocus()
updateLabel()
}
}) {
override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean {
if (event is KeyboardEvent.KeyPressed) {
return if (event.pressed) onKeyPressed(event.keycode, SavedKeyBinding.getModInt())
else onKeyReleased(event.keycode, SavedKeyBinding.getModInt())
}
return super.keyboardEvent(event, context)
}
override fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> {
if (editing) return activeBg
return super.getBackground(context)
}
fun onKeyPressed(ch: Int, modifiers: Int): Boolean {
if (!editing) {
return false
}
if (ch == GLFW.GLFW_KEY_ESCAPE) {
lastPressedNonModifier = 0
editing = false
lastPressed = 0
opt.value = SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN)
updateLabel()
blur()
return true
}
if (ch == GLFW.GLFW_KEY_LEFT_SHIFT || ch == GLFW.GLFW_KEY_RIGHT_SHIFT
|| ch == GLFW.GLFW_KEY_LEFT_ALT || ch == GLFW.GLFW_KEY_RIGHT_ALT
|| ch == GLFW.GLFW_KEY_LEFT_CONTROL || ch == GLFW.GLFW_KEY_RIGHT_CONTROL
) {
lastPressed = ch
} else {
opt.value = SavedKeyBinding(
ch, modifiers
)
editing = false
blur()
lastPressed = 0
lastPressedNonModifier = 0
}
updateLabel()
return true
}
override fun onLostFocus() {
lastPressedNonModifier = 0
editing = false
lastPressed = 0
updateLabel()
}
fun onKeyReleased(ch: Int, modifiers: Int): Boolean {
if (!editing)
return false
if (lastPressedNonModifier == ch || (lastPressedNonModifier == 0 && ch == lastPressed)) {
opt.value = SavedKeyBinding(ch, modifiers)
editing = false
blur()
lastPressed = 0
lastPressedNonModifier = 0
}
updateLabel()
return true
}
}
updateLabel()
guiAppender.appendLabeledRow(opt.labelText, button)
}
}

View File

@@ -0,0 +1,181 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.gui.CloseEventListener
import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
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 moe.nea.jarvis.api.Point
import org.lwjgl.glfw.GLFW
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlin.io.path.createDirectories
import kotlin.io.path.readText
import kotlin.io.path.writeText
import kotlin.time.Duration
import net.minecraft.client.gui.screen.Screen
import net.minecraft.text.Text
import moe.nea.firmament.Firmament
import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.keybindings.SavedKeyBinding
import moe.nea.firmament.util.ScreenUtil.setScreenLater
abstract class ManagedConfig(override val name: String) : ManagedConfigElement() {
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)
}
val file = Firmament.CONFIG_DIR.resolve("$name.json")
val data: JsonObject by lazy {
try {
Firmament.json.decodeFromString(
file.readText()
)
} catch (e: Exception) {
Firmament.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(Firmament.json.encodeToString(data))
}
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)
it.load(data)
allOptions[propertyName] = it
sortedOptions.add(it)
}
}
protected fun toggle(propertyName: String, default: () -> Boolean): ManagedOption<Boolean> {
return option(propertyName, default, BooleanHandler(this))
}
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: () -> Point,
): ManagedOption<HudMeta> {
val label = Text.translatable("firmament.config.${name}.${propertyName}")
return option(propertyName, {
val p = default()
HudMeta(HudPosition(p.x, p.y, 1F), label, width, height)
}, HudMetaHandler(this, label, width, height))
}
protected fun keyBinding(
propertyName: String,
default: () -> Int,
): ManagedOption<SavedKeyBinding> = keyBindingWithOutDefaultModifiers(propertyName) { SavedKeyBinding(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(GLFW.GLFW_KEY_UNKNOWN) }
}
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 labelText = Text.translatable("firmament.config.${name}")
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) {
save()
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 : GuiComponentWrapper(GuiContext(component)) {
override fun close() {
if (context.onBeforeClose() == CloseEventListener.CloseAction.NO_OBJECTIONS_TO_CLOSE) {
client!!.setScreen(parent)
}
}
}
return screen
}
fun showConfigEditor(parent: Screen? = null) {
setScreenLater(getConfigEditor(parent))
}
}

View File

@@ -0,0 +1,8 @@
package moe.nea.firmament.gui.config
abstract class ManagedConfigElement {
abstract val name: String
}

View File

@@ -0,0 +1,62 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.observer.GetSetter
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
import net.minecraft.text.Text
import moe.nea.firmament.Firmament
class ManagedOption<T : Any>(
val element: ManagedConfigElement,
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)
}
}

View File

@@ -0,0 +1,36 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.gui.component.TextFieldComponent
import io.github.notenoughupdates.moulconfig.observer.GetSetter
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonPrimitive
import net.minecraft.text.Text
class StringHandler(val config: ManagedConfig) : ManagedConfig.OptionHandler<String> {
override fun toJson(element: String): JsonElement? {
return JsonPrimitive(element)
}
override fun fromJson(element: JsonElement): String {
return element.jsonPrimitive.content
}
override fun emitGuiElements(opt: ManagedOption<String>, guiAppender: GuiAppender) {
guiAppender.appendLabeledRow(
opt.labelText,
TextFieldComponent(
object : GetSetter<String> by opt {
override fun set(newValue: String) {
opt.set(newValue)
config.save()
}
},
130,
suggestion = Text.translatableWithFallback(opt.rawLabelText + ".hint", "").string
),
)
}
}