Add YACL config menu

This commit is contained in:
Linnea Gräf
2024-10-13 19:53:10 +02:00
parent e6142bb936
commit 87b8513730
13 changed files with 567 additions and 306 deletions

View File

@@ -85,6 +85,9 @@ allprojects {
maven("https://maven.azureaaron.net/snapshots") maven("https://maven.azureaaron.net/snapshots")
maven("https://maven.azureaaron.net/releases") maven("https://maven.azureaaron.net/releases")
maven("https://www.cursemaven.com") maven("https://www.cursemaven.com")
maven("https://maven.isxander.dev/releases") {
name = "Xander Maven"
}
mavenLocal() mavenLocal()
} }
} }
@@ -154,6 +157,7 @@ val SourceSet.modImplementationConfigurationName
val configuredSourceSet = createIsolatedSourceSet("configured") val configuredSourceSet = createIsolatedSourceSet("configured")
val sodiumSourceSet = createIsolatedSourceSet("sodium") val sodiumSourceSet = createIsolatedSourceSet("sodium")
val citResewnSourceSet = createIsolatedSourceSet("citresewn") val citResewnSourceSet = createIsolatedSourceSet("citresewn")
val yaclSourceSet = createIsolatedSourceSet("yacl")
val shadowMe by configurations.creating { val shadowMe by configurations.creating {
exclude(group = "org.jetbrains.kotlin") exclude(group = "org.jetbrains.kotlin")
@@ -223,6 +227,7 @@ dependencies {
(citResewnSourceSet.modImplementationConfigurationName)( (citResewnSourceSet.modImplementationConfigurationName)(
innerJarsOf("citresewn", dependencies.create(libs.citresewn.get())).asFileTree) innerJarsOf("citresewn", dependencies.create(libs.citresewn.get())).asFileTree)
(citResewnSourceSet.modImplementationConfigurationName)(libs.citresewn) (citResewnSourceSet.modImplementationConfigurationName)(libs.citresewn)
(yaclSourceSet.modImplementationConfigurationName)(libs.yacl)
// Actual dependencies // Actual dependencies
modCompileOnly(libs.rei.api) { modCompileOnly(libs.rei.api) {

View File

@@ -76,6 +76,9 @@ hypixelmodapi_fabric = "1.0.1+build.1+mc1.21"
# Update from https://github.com/shedaniel/fabric-asm or https://maven.shedaniel.me/me/shedaniel/mm/ # Update from https://github.com/shedaniel/fabric-asm or https://maven.shedaniel.me/me/shedaniel/mm/
manninghamMills = "2.4.1" manninghamMills = "2.4.1"
# Update from https://docs.isxander.dev/yet-another-config-lib/installing-yacl
yacl = "3.5.0+1.21-fabric"
[libraries] [libraries]
minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" }
fabric_loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric_loader" } fabric_loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric_loader" }
@@ -109,22 +112,23 @@ sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
freecammod = { module = "maven.modrinth:freecam", version.ref = "freecammod" } freecammod = { module = "maven.modrinth:freecam", version.ref = "freecammod" }
citresewn = { module = "maven.modrinth:cit-resewn", version.ref = "citresewn" } citresewn = { module = "maven.modrinth:cit-resewn", version.ref = "citresewn" }
femalegender = { module = "maven.modrinth:female-gender", version.ref = "femalegender" } femalegender = { module = "maven.modrinth:female-gender", version.ref = "femalegender" }
yacl = { module = "dev.isxander:yet-another-config-lib", version.ref = "yacl" }
[bundles] [bundles]
runtime_required = [ runtime_required = [
"architectury_fabric", "architectury_fabric",
"rei_fabric", "rei_fabric",
"notenoughanimations", "notenoughanimations",
"femalegender", "femalegender",
"hypixelmodapi_fabric", "hypixelmodapi_fabric",
] ]
runtime_optional = [ runtime_optional = [
"devauth", "devauth",
"freecammod", "freecammod",
"sodium", "sodium",
# "qolify", # "qolify",
"ncr", "ncr",
"citresewn", "citresewn",
] ]
[plugins] [plugins]

View File

@@ -0,0 +1,16 @@
package moe.nea.firmament.compat.yacl
import dev.isxander.yacl3.api.Controller
import dev.isxander.yacl3.api.Option
import dev.isxander.yacl3.api.controller.ControllerBuilder
import moe.nea.firmament.gui.config.ManagedOption
import moe.nea.firmament.keybindings.SavedKeyBinding
class KeybindingBuilder(
val option: Option<SavedKeyBinding>,
val managedOption: ManagedOption<SavedKeyBinding>
) : ControllerBuilder<SavedKeyBinding> {
override fun build(): Controller<SavedKeyBinding> {
return KeybindingController(option, managedOption)
}
}

View File

@@ -0,0 +1,71 @@
package moe.nea.firmament.compat.yacl
import dev.isxander.yacl3.api.Controller
import dev.isxander.yacl3.api.Option
import dev.isxander.yacl3.api.utils.Dimension
import dev.isxander.yacl3.gui.AbstractWidget
import dev.isxander.yacl3.gui.YACLScreen
import dev.isxander.yacl3.gui.controllers.ControllerWidget
import net.minecraft.text.Text
import moe.nea.firmament.gui.config.KeyBindingHandler
import moe.nea.firmament.gui.config.KeyBindingStateManager
import moe.nea.firmament.gui.config.ManagedOption
import moe.nea.firmament.keybindings.SavedKeyBinding
class KeybindingController(
val option: Option<SavedKeyBinding>,
val managedOption: ManagedOption<SavedKeyBinding>,
) : Controller<SavedKeyBinding> {
val handler = managedOption.handler as KeyBindingHandler
override fun option(): Option<SavedKeyBinding> {
return option
}
override fun formatValue(): Text {
return option.pendingValue().format()
}
override fun provideWidget(screen: YACLScreen, widgetDimension: Dimension<Int>): AbstractWidget {
lateinit var button: ControllerWidget<KeybindingController>
val sm = KeyBindingStateManager(
{ option.pendingValue() },
{ option.requestSet(it) },
{ screen.focused = null },
{ screen.focused = button }
)
button = object : ControllerWidget<KeybindingController>(this, screen, widgetDimension) {
override fun getHoveredControlWidth(): Int {
return 130
}
override fun getValueText(): Text {
return sm.label
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
return sm.keyboardEvent(keyCode, true)
}
override fun keyReleased(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
return sm.keyboardEvent(keyCode, false)
}
override fun unfocus() {
sm.onLostFocus()
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (button == 0 && isHovered) {
sm.onClick()
return true
}
return super.mouseClicked(mouseX, mouseY, button)
}
}
option.addListener { t, u ->
sm.updateLabel()
}
sm.updateLabel()
return button
}
}

View File

@@ -0,0 +1,119 @@
package moe.nea.firmament.compat.yacl
import com.google.auto.service.AutoService
import dev.isxander.yacl3.api.Binding
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.YetAnotherConfigLib
import dev.isxander.yacl3.api.controller.ControllerBuilder
import dev.isxander.yacl3.api.controller.DoubleSliderControllerBuilder
import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder
import dev.isxander.yacl3.api.controller.StringControllerBuilder
import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit
import net.minecraft.client.gui.screen.Screen
import net.minecraft.text.Text
import moe.nea.firmament.gui.config.AllConfigsGui
import moe.nea.firmament.gui.config.BooleanHandler
import moe.nea.firmament.gui.config.ClickHandler
import moe.nea.firmament.gui.config.DurationHandler
import moe.nea.firmament.gui.config.FirmamentConfigScreenProvider
import moe.nea.firmament.gui.config.HudMeta
import moe.nea.firmament.gui.config.HudMetaHandler
import moe.nea.firmament.gui.config.IntegerHandler
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.keybindings.SavedKeyBinding
import moe.nea.firmament.util.FirmFormatters
@AutoService(FirmamentConfigScreenProvider::class)
class YaclIntegration : FirmamentConfigScreenProvider {
fun buildCategories() =
AllConfigsGui.allConfigs
.map(::buildCategory)
private fun buildCategory(managedConfig: ManagedConfig): ConfigCategory {
return ConfigCategory.createBuilder()
.name(managedConfig.labelText)
.also {
it.rootGroupBuilder()
.options(buildOptions(managedConfig.sortedOptions))
}
.build()
}
fun buildOptions(options: List<ManagedOption<*>>): Collection<Option<*>> =
options.map { buildOption(it) }
private fun <T : Any> buildOption(managedOption: ManagedOption<T>): Option<*> {
val handler = managedOption.handler
val binding = Binding.generic(managedOption.default(), managedOption::value, managedOption::value.setter)
fun <T> createDefaultBinding(function: (Option<T>) -> ControllerBuilder<T>): Option.Builder<T> {
return Option.createBuilder<T>()
.name(managedOption.labelText)
.binding(binding as Binding<T>)
.controller { function(it) }
}
when (handler) {
is ClickHandler -> return ButtonOption.createBuilder()
.name(managedOption.labelText)
.action { t, u ->
handler.runnable()
}
.build()
is HudMetaHandler -> return ButtonOption.createBuilder()
.name(managedOption.labelText)
.action { t, u ->
handler.openEditor(managedOption as ManagedOption<HudMeta>, t)
}
.build()
is BooleanHandler -> return createDefaultBinding(TickBoxControllerBuilder::create).build()
is StringHandler -> return createDefaultBinding(StringControllerBuilder::create).build()
is IntegerHandler -> return createDefaultBinding {
IntegerSliderControllerBuilder.create(it).range(handler.min, handler.max).step(1)
}.build()
is DurationHandler -> return Option.createBuilder<Double>()
.name(managedOption.labelText)
.binding((binding as Binding<Duration>).xmap({ it.toDouble(DurationUnit.SECONDS) }, { it.seconds }))
.controller {
DoubleSliderControllerBuilder.create(it)
.formatValue { Text.literal(FirmFormatters.formatTimespan(it.seconds)) }
.step(0.1)
.range(handler.min.toDouble(DurationUnit.SECONDS), handler.max.toDouble(DurationUnit.SECONDS))
}
.build()
is KeyBindingHandler -> return createDefaultBinding {
KeybindingBuilder(it, managedOption as ManagedOption<SavedKeyBinding>)
}.build()
else -> return LabelOption.create(Text.literal("This option is currently unhandled for this config menu. Please report this as a bug."))
}
}
fun buildConfig(): YetAnotherConfigLib {
return YetAnotherConfigLib.createBuilder()
.title(Text.literal("Firmament"))
.categories(buildCategories())
.build()
}
override val key: String
get() = "yacl"
override fun open(parent: Screen?): Screen {
return buildConfig().generateScreen(parent)
}
}

View File

@@ -12,39 +12,40 @@ import moe.nea.firmament.util.ScreenUtil.setScreenLater
object AllConfigsGui { object AllConfigsGui {
val allConfigs val allConfigs
get() = listOf( get() = listOf(
RepoManager.Config RepoManager.Config
) + FeatureManager.allFeatures.mapNotNull { it.config } ) + FeatureManager.allFeatures.mapNotNull { it.config }
fun <T> List<T>.toObservableList(): ObservableList<T> = ObservableList(this) fun <T> List<T>.toObservableList(): ObservableList<T> = ObservableList(this)
class MainMapping(val allConfigs: List<ManagedConfig>) { class MainMapping(val allConfigs: List<ManagedConfig>) {
@get:Bind("configs") @get:Bind("configs")
val configs = allConfigs.map { EntryMapping(it) }.toObservableList() val configs = allConfigs.map { EntryMapping(it) }.toObservableList()
class EntryMapping(val config: ManagedConfig) { class EntryMapping(val config: ManagedConfig) {
@Bind @Bind
fun name() = Text.translatable("firmament.config.${config.name}").string fun name() = Text.translatable("firmament.config.${config.name}").string
@Bind @Bind
fun openEditor() { fun openEditor() {
config.showConfigEditor(MC.screen) config.showConfigEditor(MC.screen)
} }
} }
} }
fun makeBuiltInScreen(parent: Screen? = null): Screen { fun makeBuiltInScreen(parent: Screen? = null): Screen {
return MoulConfigUtils.loadScreen("config/main", MainMapping(allConfigs), parent) return MoulConfigUtils.loadScreen("config/main", MainMapping(allConfigs), parent)
} }
fun makeScreen(parent: Screen? = null): Screen { fun makeScreen(parent: Screen? = null): Screen {
val provider = FirmamentConfigScreenProvider.providers.find { it.key == "builtin" } val wantedKey = if (RepoManager.Config.enableYacl) "yacl" else "builtin"
?: FirmamentConfigScreenProvider.providers.first() val provider = FirmamentConfigScreenProvider.providers.find { it.key == wantedKey }
return provider.open(parent) ?: FirmamentConfigScreenProvider.providers.first()
} return provider.open(parent)
}
fun showAllGuis() { fun showAllGuis() {
setScreenLater(makeScreen()) setScreenLater(makeScreen())
} }
} }

View File

@@ -6,22 +6,29 @@ import net.minecraft.client.gui.screen.Screen
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
interface FirmamentConfigScreenProvider { interface FirmamentConfigScreenProvider {
val key: String val key: String
val isEnabled: Boolean get() = true val isEnabled: Boolean get() = true
fun open(parent: Screen?): Screen fun open(parent: Screen?): Screen
companion object { companion object {
private val loader = ServiceLoader.load(FirmamentConfigScreenProvider::class.java) private val loader = ServiceLoader.load(FirmamentConfigScreenProvider::class.java)
val providers by lazy { val providers by lazy {
loader.stream().asSequence().mapNotNull { service -> loader.stream().asSequence().mapNotNull { service ->
kotlin.runCatching { service.get() } kotlin.runCatching { service.get() }
.getOrElse { .getOrElse {
Firmament.logger.warn("Could not load config provider ${service.type()}", it) Firmament.logger.warn("Could not load config provider ${service.type()}", it)
null null
} }
}.filter { it.isEnabled }.toList() }.filter { it.isEnabled }
} .sortedWith(Comparator.comparing(
} { it.key },
Comparator<String> { left, right ->
if (left == "builtin") return@Comparator -1
if (right == "builtin") return@Comparator 1
return@Comparator left.compareTo(right)
})).toList()
}
}
} }

View File

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

View File

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

View File

@@ -32,7 +32,7 @@ data class SavedKeyBinding(
return Triple( return Triple(
modifiers and GLFW.GLFW_MOD_SHIFT != 0, modifiers and GLFW.GLFW_MOD_SHIFT != 0,
modifiers and GLFW.GLFW_MOD_CONTROL != 0, modifiers and GLFW.GLFW_MOD_CONTROL != 0,
modifiers and GLFW.GLFW_MOD_ALT != 0 modifiers and GLFW.GLFW_MOD_ALT != 0,
) )
} }

View File

@@ -19,127 +19,129 @@ import moe.nea.firmament.util.MinecraftDispatcher
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
object RepoManager { object RepoManager {
object Config : ManagedConfig("repo") { object Config : ManagedConfig("repo") {
var username by string("username") { "NotEnoughUpdates" } var username by string("username") { "NotEnoughUpdates" }
var reponame by string("reponame") { "NotEnoughUpdates-REPO" } var reponame by string("reponame") { "NotEnoughUpdates-REPO" }
var branch by string("branch") { "master" } var branch by string("branch") { "master" }
val autoUpdate by toggle("autoUpdate") { true } val autoUpdate by toggle("autoUpdate") { true }
val reset by button("reset") { val reset by button("reset") {
username = "NotEnoughUpdates" username = "NotEnoughUpdates"
reponame = "NotEnoughUpdates-REPO" reponame = "NotEnoughUpdates-REPO"
branch = "master" branch = "master"
save() save()
} }
val disableItemGroups by toggle("disable-item-groups") { true } val enableYacl by toggle("enable-yacl") { false }
val reload by button("reload") {
save()
RepoManager.reload()
}
val redownload by button("redownload") {
save()
RepoManager.launchAsyncUpdate(true)
}
}
val currentDownloadedSha by RepoDownloadManager::latestSavedVersionHash val disableItemGroups by toggle("disable-item-groups") { true }
val reload by button("reload") {
save()
RepoManager.reload()
}
val redownload by button("redownload") {
save()
RepoManager.launchAsyncUpdate(true)
}
}
var recentlyFailedToUpdateItemList = false val currentDownloadedSha by RepoDownloadManager::latestSavedVersionHash
val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply { var recentlyFailedToUpdateItemList = false
registerReloadListener(ItemCache)
registerReloadListener(ExpLadders)
registerReloadListener(ItemNameLookup)
ReloadRegistrationEvent.publish(ReloadRegistrationEvent(this))
registerReloadListener {
Firmament.coroutineScope.launch(MinecraftDispatcher) {
if (!trySendClientboundUpdateRecipesPacket()) {
logger.warn("Failed to issue a ClientboundUpdateRecipesPacket (to reload REI). This may lead to an outdated item list.")
recentlyFailedToUpdateItemList = true
}
}
}
}
val essenceRecipeProvider = EssenceRecipeProvider() val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply {
val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider) registerReloadListener(ItemCache)
registerReloadListener(ExpLadders)
registerReloadListener(ItemNameLookup)
ReloadRegistrationEvent.publish(ReloadRegistrationEvent(this))
registerReloadListener {
Firmament.coroutineScope.launch(MinecraftDispatcher) {
if (!trySendClientboundUpdateRecipesPacket()) {
logger.warn("Failed to issue a ClientboundUpdateRecipesPacket (to reload REI). This may lead to an outdated item list.")
recentlyFailedToUpdateItemList = true
}
}
}
}
init { val essenceRecipeProvider = EssenceRecipeProvider()
neuRepo.registerReloadListener(essenceRecipeProvider) val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider)
neuRepo.registerReloadListener(recipeCache)
}
fun getAllRecipes() = neuRepo.items.items.values.asSequence().flatMap { it.recipes } init {
neuRepo.registerReloadListener(essenceRecipeProvider)
neuRepo.registerReloadListener(recipeCache)
}
fun getRecipesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.recipes[skyblockId] ?: setOf() fun getAllRecipes() = neuRepo.items.items.values.asSequence().flatMap { it.recipes }
fun getUsagesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.usages[skyblockId] ?: setOf()
private fun trySendClientboundUpdateRecipesPacket(): Boolean { fun getRecipesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.recipes[skyblockId] ?: setOf()
return MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().networkHandler?.onSynchronizeRecipes( fun getUsagesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.usages[skyblockId] ?: setOf()
SynchronizeRecipesS2CPacket(mutableListOf())
) != null
}
init { private fun trySendClientboundUpdateRecipesPacket(): Boolean {
ClientTickEvents.START_WORLD_TICK.register(ClientTickEvents.StartWorldTick { return MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().networkHandler?.onSynchronizeRecipes(
if (recentlyFailedToUpdateItemList && trySendClientboundUpdateRecipesPacket()) SynchronizeRecipesS2CPacket(mutableListOf())
recentlyFailedToUpdateItemList = false ) != null
}) }
}
fun getNEUItem(skyblockId: SkyblockId): NEUItem? = neuRepo.items.getItemBySkyblockId(skyblockId.neuItem) init {
ClientTickEvents.START_WORLD_TICK.register(ClientTickEvents.StartWorldTick {
if (recentlyFailedToUpdateItemList && trySendClientboundUpdateRecipesPacket())
recentlyFailedToUpdateItemList = false
})
}
fun launchAsyncUpdate(force: Boolean = false) { fun getNEUItem(skyblockId: SkyblockId): NEUItem? = neuRepo.items.getItemBySkyblockId(skyblockId.neuItem)
Firmament.coroutineScope.launch {
ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper boundy bar
ItemCache.ReloadProgressHud.isEnabled = true
try {
RepoDownloadManager.downloadUpdate(force)
ItemCache.ReloadProgressHud.reportProgress("Download complete", 1, 1)
} finally {
ItemCache.ReloadProgressHud.isEnabled = false
}
reload()
}
}
fun reload() { fun launchAsyncUpdate(force: Boolean = false) {
try { Firmament.coroutineScope.launch {
ItemCache.ReloadProgressHud.reportProgress("Reloading from Disk", ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper boundy bar
0, ItemCache.ReloadProgressHud.isEnabled = true
-1) // TODO: replace with a proper boundy bar try {
ItemCache.ReloadProgressHud.isEnabled = true RepoDownloadManager.downloadUpdate(force)
neuRepo.reload() ItemCache.ReloadProgressHud.reportProgress("Download complete", 1, 1)
} catch (exc: NEURepositoryException) { } finally {
MinecraftClient.getInstance().player?.sendMessage( ItemCache.ReloadProgressHud.isEnabled = false
Text.literal("Failed to reload repository. This will result in some mod features not working.") }
) reload()
ItemCache.ReloadProgressHud.isEnabled = false }
exc.printStackTrace() }
}
}
fun initialize() { fun reload() {
if (Config.autoUpdate) { try {
launchAsyncUpdate() ItemCache.ReloadProgressHud.reportProgress("Reloading from Disk",
} else { 0,
reload() -1) // TODO: replace with a proper boundy bar
} ItemCache.ReloadProgressHud.isEnabled = true
} neuRepo.reload()
} catch (exc: NEURepositoryException) {
MinecraftClient.getInstance().player?.sendMessage(
Text.literal("Failed to reload repository. This will result in some mod features not working.")
)
ItemCache.ReloadProgressHud.isEnabled = false
exc.printStackTrace()
}
}
fun getPotentialStubPetData(skyblockId: SkyblockId): PetData? { fun initialize() {
val parts = skyblockId.neuItem.split(";") if (Config.autoUpdate) {
if (parts.size != 2) { launchAsyncUpdate()
return null } else {
} reload()
val (petId, rarityIndex) = parts }
if (!rarityIndex.all { it.isDigit() }) { }
return null
} fun getPotentialStubPetData(skyblockId: SkyblockId): PetData? {
val intIndex = rarityIndex.toInt() val parts = skyblockId.neuItem.split(";")
if (intIndex !in Rarity.values().indices) return null if (parts.size != 2) {
if (petId !in neuRepo.constants.petNumbers) return null return null
return PetData(Rarity.values()[intIndex], petId, 0.0, true) }
} val (petId, rarityIndex) = parts
if (!rarityIndex.all { it.isDigit() }) {
return null
}
val intIndex = rarityIndex.toInt()
if (intIndex !in Rarity.values().indices) return null
if (petId !in neuRepo.constants.petNumbers) return null
return PetData(Rarity.values()[intIndex], petId, 0.0, true)
}
} }

View File

@@ -73,6 +73,7 @@
"firmament.config.repo.username.hint": "NotEnoughUpdates", "firmament.config.repo.username.hint": "NotEnoughUpdates",
"firmament.config.repo.reponame": "Repo Name", "firmament.config.repo.reponame": "Repo Name",
"firmament.config.repo.reponame.hint": "NotEnoughUpdates-REPO", "firmament.config.repo.reponame.hint": "NotEnoughUpdates-REPO",
"firmament.config.repo.enable-yacl": "Use YACL Config",
"firmament.config.repo.branch": "Repo Branch", "firmament.config.repo.branch": "Repo Branch",
"firmament.config.repo.branch.hint": "dangerous", "firmament.config.repo.branch.hint": "dangerous",
"firmament.config.repo.reset": "Reset", "firmament.config.repo.reset": "Reset",