diff --git a/src/main/java/com/lambda/mixin/CrashReportMixin.java b/src/main/java/com/lambda/mixin/CrashReportMixin.java index ec4521c81..d0d144c8f 100644 --- a/src/main/java/com/lambda/mixin/CrashReportMixin.java +++ b/src/main/java/com/lambda/mixin/CrashReportMixin.java @@ -18,7 +18,7 @@ package com.lambda.mixin; import com.lambda.Lambda; -import com.lambda.config.AbstractSetting; +import com.lambda.config.Setting; import com.lambda.module.Module; import com.lambda.module.ModuleRegistry; import com.lambda.util.DynamicExceptionKt; @@ -67,7 +67,7 @@ String injectString(ReportType type, List extraInfo, Operation o module.getSettings() .stream() - .filter(AbstractSetting::isModified) + .filter(Setting::isModified) .forEach(setting -> list.add(String.format("\t\t%s -> %s", setting.getName(), setting.getValue()))); }); } diff --git a/src/main/kotlin/com/lambda/command/commands/ConfigCommand.kt b/src/main/kotlin/com/lambda/command/commands/ConfigCommand.kt index 0c500f8b6..f0b50d65e 100644 --- a/src/main/kotlin/com/lambda/command/commands/ConfigCommand.kt +++ b/src/main/kotlin/com/lambda/command/commands/ConfigCommand.kt @@ -76,13 +76,13 @@ object ConfigCommand : LambdaCommand( executeWithResult { val confName = config().value() val settingName = setting().value() - val conf = Configuration.configurableByCommandName(confName) ?: run { + val configurable = Configuration.configurableByCommandName(confName) ?: run { return@executeWithResult failure("$confName is not a valid configurable.") } - val set = Configuration.settingByCommandName(conf, settingName) ?: run { + val setting = Configuration.settingByCommandName(configurable, settingName) ?: run { return@executeWithResult failure("$settingName is not a valid setting for $confName.") } - set.reset() + setting.reset() return@executeWithResult success() } } diff --git a/src/main/kotlin/com/lambda/config/AbstractSetting.kt b/src/main/kotlin/com/lambda/config/AbstractSetting.kt deleted file mode 100644 index 0e08bdc37..000000000 --- a/src/main/kotlin/com/lambda/config/AbstractSetting.kt +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.config - -import com.google.gson.JsonElement -import com.google.gson.JsonParser -import com.lambda.Lambda.LOG -import com.lambda.Lambda.gson -import com.lambda.brigadier.CommandResult.Companion.failure -import com.lambda.brigadier.CommandResult.Companion.success -import com.lambda.brigadier.argument.string -import com.lambda.brigadier.argument.value -import com.lambda.brigadier.executeWithResult -import com.lambda.brigadier.required -import com.lambda.command.CommandRegistry -import com.lambda.command.commands.ConfigCommand -import com.lambda.context.SafeContext -import com.lambda.gui.Layout -import com.lambda.threading.runSafe -import com.lambda.util.Communication.info -import com.lambda.util.Describable -import com.lambda.util.Nameable -import com.lambda.util.NamedEnum -import com.lambda.util.extension.CommandBuilder -import com.lambda.util.text.ClickEvents -import com.lambda.util.text.HoverEvents -import com.lambda.util.text.TextBuilder -import com.lambda.util.text.buildText -import com.lambda.util.text.clickEvent -import com.lambda.util.text.highlighted -import com.lambda.util.text.hoverEvent -import com.lambda.util.text.literal -import net.minecraft.command.CommandRegistryAccess -import java.lang.reflect.Type -import kotlin.properties.Delegates -import kotlin.reflect.KProperty - -/** - * Represents a setting with a [defaultValue], [visibility] condition, and [description]. - * This setting is serializable ([Jsonable]) and has a [name]. - * - * When the [value] is modified, all registered [listeners] are notified. - * The [visibility] of the setting can be checked with the [isVisible] property. - * The setting can be [reset] to its [defaultValue]. - * - * Simple Usage: - * ```kotlin - * // this uses the delegate (by) association to access the setting value in the code directly. - * val mode by setting("Mode", Modes.FREEZE, { page == Page.CUSTOM }, "The mode of the module.") - * - * init { - * listener { - * LOG.info("Mode: $mode") // direct access of the value - * } - * } - * ``` - * - * Advanced usage with listeners: - * ```kotlin - * // notice how this does not use the delegate (by) association, to access the setting object to register listeners. - * val mode = setting("Mode", Modes.FREEZE, { page == Page.CUSTOM }, "The mode of the module.") - * - * init { - * mode.listener { from, to -> - * // Do something when the mode changes in a safe context - * } - * mode.unsafeListener { from, to -> - * // Do something when the mode changes in an unsafe context - * } - * - * listener { - * LOG.info("Mode: ${mode.value}") // indirect access of the value - * } - * } - * ``` - * - * @property defaultValue The default value of the setting. - * @property description A description of the setting. - * @property type The type reflection of the setting. - * @property visibility A function that determines whether the setting is visible. - */ -abstract class AbstractSetting( - override var name: String, - internal var defaultValue: T, - val type: Type, - override var description: String, - var visibility: () -> Boolean, -) : Jsonable, Nameable, Describable, Layout { - private val listeners = mutableListOf>() - var disabled = { false } - var groups: MutableList> = mutableListOf() - - var value by Delegates.observable(defaultValue) { _, from, to -> - listeners.forEach { - if (it.requiresValueChange && from == to) return@forEach - it.execute(from, to) - } - } - - val isModified get() = value != defaultValue - - operator fun getValue(thisRef: Any?, property: KProperty<*>) = value - open operator fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: T) { - value = valueIn - } - - override fun toJson(): JsonElement = - gson.toJsonTree(value, type) - - override fun loadFromJson(serialized: JsonElement) { - runCatching { - value = gson.fromJson(serialized, type) - }.onFailure { - LOG.warn("Failed to load setting ${this.name} with value $serialized. Resetting to default value $defaultValue") - value = defaultValue - } - } - - class ValueListener(val requiresValueChange: Boolean, val execute: (from: T, to: T) -> Unit) - - /** - * Will only register changes of the variable, not the content of the variable! - * E.g., if the variable is a list, it will only register if the list reference changes, not if the content of the list changes. - */ - fun onValueChange(block: SafeContext.(from: T, to: T) -> Unit) = apply { - listeners.add(ValueListener(true) { from, to -> - runSafe { - block(from, to) - } - }) - } - - fun onValueChangeUnsafe(block: (from: T, to: T) -> Unit) = apply { - listeners.add(ValueListener(true, block)) - } - - fun onValueSet(block: (from: T, to: T) -> Unit) = apply { - listeners.add(ValueListener(false, block)) - } - - fun disabled(predicate: () -> Boolean) = apply { - disabled = predicate - } - - fun group(path: List, vararg continuation: NamedEnum) = apply { - groups.add(path + continuation) - } - - fun group(vararg path: NamedEnum) = apply { - groups.add(path.toList()) - } - - fun group(path: NamedEnum?) = apply { - path?.let { groups.add(listOf(it)) } - } - - fun reset(silent: Boolean = false) { - if (!silent && value == defaultValue) { - ConfigCommand.info(notChangedMessage()) - return - } - if (!silent) ConfigCommand.info(resetMessage(value, defaultValue)) - value = defaultValue - } - - open fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { - required(string("value as JSON")) { value -> - executeWithResult { - val valueString = value().value() - val parsed = try { - JsonParser.parseString("\"$valueString\"") - } catch (_: Exception) { - return@executeWithResult failure("$valueString is not a valid JSON string.") - } - val config = Configuration.configurableBySetting(this@AbstractSetting) - ?: return@executeWithResult failure("No config found for $name.") - val previous = this@AbstractSetting.value - try { - loadFromJson(parsed) - } catch (_: Exception) { - return@executeWithResult failure("Failed to load $valueString as a ${type::class.simpleName} for $name in ${config.name}.") - } - ConfigCommand.info(setMessage(previous, this@AbstractSetting.value)) - return@executeWithResult success() - } - } - } - - fun trySetValue(newValue: T) { - if (newValue == value) { - ConfigCommand.info(notChangedMessage()) - } else { - val previous = value - value = newValue - ConfigCommand.info(setMessage(previous, newValue)) - } - } - - private fun setMessage(previousValue: T, newValue: T) = buildText { - literal("Set ") - changedMessage(previousValue, newValue) - val config = Configuration.configurableBySetting(this@AbstractSetting) ?: return@buildText - clickEvent(ClickEvents.suggestCommand("${CommandRegistry.prefix}${ConfigCommand.name} reset ${config.commandName} $commandName")) { - hoverEvent(HoverEvents.showText(buildText { - literal("Click to reset to default value ") - highlighted(defaultValue.toString()) - })) { - highlighted(" [Reset]") - } - } - } - - private fun resetMessage(previousValue: T, newValue: T) = buildText { - literal("Reset ") - changedMessage(previousValue, newValue) - } - - private fun notChangedMessage() = buildText { - literal("No changes made to ") - highlighted(name) - literal(" as it is already set to ") - highlighted(value.toString()) - literal(".") - } - - private fun TextBuilder.changedMessage(previousValue: T, newValue: T) { - val config = Configuration.configurableBySetting(this@AbstractSetting) ?: return - highlighted(config.name) - literal(" > ") - highlighted(name) - literal(" from ") - highlighted(previousValue.toString()) - literal(" to ") - highlighted(newValue.toString()) - literal(".") - clickEvent(ClickEvents.suggestCommand("${CommandRegistry.prefix}${ConfigCommand.name} set ${config.commandName} $commandName $previousValue")) { - hoverEvent(HoverEvents.showText(buildText { - literal("Click to undo to previous value ") - highlighted(previousValue.toString()) - })) { - highlighted(" [Undo]") - } - } - } - - override fun toString() = "Setting $name: $value of type ${type.typeName}" - - override fun equals(other: Any?) = other is AbstractSetting<*> && name == other.name - override fun hashCode() = name.hashCode() -} diff --git a/src/main/kotlin/com/lambda/config/AutomationConfig.kt b/src/main/kotlin/com/lambda/config/AutomationConfig.kt index bd67681ed..4e1039ad5 100644 --- a/src/main/kotlin/com/lambda/config/AutomationConfig.kt +++ b/src/main/kotlin/com/lambda/config/AutomationConfig.kt @@ -59,8 +59,6 @@ open class AutomationConfig( override val hotbarConfig = HotbarSettings(this, Group.Hotbar) override val eatConfig = EatSettings(this, Group.Eat) - val hiddenSettings = mutableSetOf>() - companion object { context(module: Module) fun MutableAutomationConfig.setDefaultAutomationConfig( diff --git a/src/main/kotlin/com/lambda/config/ConfigEditor.kt b/src/main/kotlin/com/lambda/config/ConfigEditor.kt index 856021a7b..4a9e168b9 100644 --- a/src/main/kotlin/com/lambda/config/ConfigEditor.kt +++ b/src/main/kotlin/com/lambda/config/ConfigEditor.kt @@ -17,7 +17,6 @@ package com.lambda.config -import com.lambda.config.AutomationConfig.Companion.DEFAULT.hiddenSettings import com.lambda.util.NamedEnum import kotlin.reflect.KProperty0 import kotlin.reflect.jvm.isAccessible @@ -39,66 +38,60 @@ open class SettingGroupEditor(open val c: T) { throw IllegalStateException("Could not access delegate for property $name", e) } - fun KProperty0<*>.settingDelegate() = - this.delegate as? AbstractSetting - ?: throw IllegalStateException("Setting (${(delegate as AbstractSetting<*>).name}) delegate did not match current value's type") + fun KProperty0.setting() = + this.delegate as? Setting, T> + ?: throw IllegalStateException("Setting delegate did not match current value's type") + + fun KProperty0.settingCore() = setting().core @SettingEditorDsl - inline fun KProperty0.edit(edits: TypedEditBuilder.(AbstractSetting) -> Unit) { - val setting = settingDelegate() - TypedEditBuilder(this@SettingGroupEditor, listOf(setting)).edits(setting) + inline fun KProperty0.edit(edits: TypedEditBuilder.(SettingCore) -> Unit) { + val delegate = setting() + TypedEditBuilder(this@SettingGroupEditor, listOf(delegate)).edits(delegate.core) } @SettingEditorDsl inline fun KProperty0.editWith( other: KProperty0, - edits: TypedEditBuilder.(AbstractSetting) -> Unit - ) { - val setting = settingDelegate() - TypedEditBuilder(this@SettingGroupEditor, listOf(setting)).edits(other.settingDelegate()) - } + edits: TypedEditBuilder.(SettingCore) -> Unit + ) = TypedEditBuilder(this@SettingGroupEditor, listOf(setting())).edits(other.settingCore()) @SettingEditorDsl fun edit( vararg settings: KProperty0<*>, edits: BasicEditBuilder.() -> Unit - ) { BasicEditBuilder(this, settings.map { it.delegate } as List>).apply(edits) } + ) = BasicEditBuilder(this, settings.map { it.setting() }).apply(edits) @SettingEditorDsl inline fun editWith( vararg settings: KProperty0<*>, other: KProperty0, - edits: BasicEditBuilder.(AbstractSetting) -> Unit - ) = BasicEditBuilder(this, settings.map { it.delegate } as List>).edits(other.settingDelegate()) + edits: BasicEditBuilder.(SettingCore) -> Unit + ) = BasicEditBuilder(this, settings.map { it.setting() }).edits(other.settingCore()) @SettingEditorDsl inline fun editTyped( vararg settings: KProperty0, edits: TypedEditBuilder.() -> Unit - ) { TypedEditBuilder(this, settings.map { it.delegate } as List>).apply(edits) } + ) = TypedEditBuilder(this, settings.map { it.setting() }).apply(edits) @SettingEditorDsl inline fun editTypedWith( vararg settings: KProperty0, other: KProperty0, - edits: TypedEditBuilder.(AbstractSetting) -> Unit - ) = TypedEditBuilder(this, settings.map { it.delegate } as List>).edits(other.delegate as AbstractSetting) + edits: TypedEditBuilder.(SettingCore) -> Unit + ) = TypedEditBuilder(this, settings.map { it.setting() }).edits(other.settingCore()) @SettingEditorDsl - fun hide(settings: Collection>) { + fun hide(settings: Collection>) { c.settings.removeAll(settings) - hiddenSettings.addAll(settings) } @SettingEditorDsl fun hide(vararg settings: KProperty0<*>) = - hide((settings.map { it.delegate } as List>)) - - open class BasicEditBuilder(val c: SettingGroupEditor<*>, open val settings: Collection>) { - @SettingEditorDsl - fun visibility(vis: () -> Boolean) = - settings.forEach { it.visibility = vis } + hide(settings.map { it.setting() }) + open class BasicEditBuilder(val c: SettingGroupEditor<*>, open val settings: Collection>) { @SettingEditorDsl fun hide() = c.hide(settings) @@ -113,13 +106,13 @@ open class SettingGroupEditor(open val c: T) { class TypedEditBuilder( c: SettingGroupEditor<*>, - override val settings: Collection> + override val settings: Collection, T>> ) : BasicEditBuilder(c, settings) { @SettingEditorDsl fun defaultValue(value: T) = settings.forEach { - it.defaultValue = value - it.value = value + it.core.defaultValue = value + it.core.value = value } } } diff --git a/src/main/kotlin/com/lambda/config/Configurable.kt b/src/main/kotlin/com/lambda/config/Configurable.kt index ac1cd1389..ba1cb14f7 100644 --- a/src/main/kotlin/com/lambda/config/Configurable.kt +++ b/src/main/kotlin/com/lambda/config/Configurable.kt @@ -21,6 +21,7 @@ import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.reflect.TypeToken import com.lambda.Lambda.LOG +import com.lambda.config.Configuration.Companion.configurables import com.lambda.config.settings.CharSetting import com.lambda.config.settings.FunctionSetting import com.lambda.config.settings.StringSetting @@ -35,7 +36,7 @@ import com.lambda.config.settings.complex.Bind import com.lambda.config.settings.complex.BlockPosSetting import com.lambda.config.settings.complex.BlockSetting import com.lambda.config.settings.complex.ColorSetting -import com.lambda.config.settings.complex.KeybindSetting +import com.lambda.config.settings.complex.KeybindSettingCore import com.lambda.config.settings.complex.Vec3dSetting import com.lambda.config.settings.numeric.DoubleSetting import com.lambda.config.settings.numeric.FloatSetting @@ -44,44 +45,42 @@ import com.lambda.config.settings.numeric.LongSetting import com.lambda.util.Communication.logError import com.lambda.util.KeyCode import com.lambda.util.Nameable -import com.lambda.util.reflections.getInstancesImplementingWithParameters import imgui.flag.ImGuiInputTextFlags -import io.github.classgraph.ClassInfoList import net.minecraft.block.Block import net.minecraft.item.Item -import net.minecraft.network.listener.ClientPlayPacketListener -import net.minecraft.network.packet.Packet import net.minecraft.registry.Registries import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import java.awt.Color -import kotlin.reflect.KClass /** - * Represents a set of [AbstractSetting]s that are associated with the [name] of the [Configurable]. + * Represents a set of [SettingCore]s that are associated with the [name] of the [Configurable]. * The settings are managed by this [Configurable] and are saved and loaded as part of the [Configuration]. * * This class also provides a series of helper methods ([setting]) for creating different types of settings. * - * @property settings A set of [AbstractSetting]s that this configurable manages. + * @property settings A set of [SettingCore]s that this configurable manages. */ abstract class Configurable( - private val configuration: Configuration, + val configuration: Configuration, ) : Jsonable, Nameable { - val settings = mutableListOf>() + val settings = mutableListOf>() val settingGroups = mutableListOf() init { registerConfigurable() } - private fun registerConfigurable() = configuration.configurables.add(this) + private fun registerConfigurable() { + if (configurables.any { it.name == name }) + throw IllegalStateException("Configurable with name $name already exists") + configuration.configurables.add(this) + } - inline fun > T.register(): T { + fun , R : Any> Setting.register() = apply { if (settings.any { it.name == name }) throw IllegalStateException("Setting with name $name already exists for configurable: ${this@Configurable.name}") - settings.add(this) - return this + settings.add(this) } override fun toJson() = @@ -107,7 +106,7 @@ abstract class Configurable( defaultValue: Boolean, description: String = "", visibility: () -> Boolean = { true }, - ) = BooleanSetting(name, defaultValue, description, visibility).register() + ) = Setting(name, description, BooleanSetting(defaultValue), this, visibility).register() inline fun > setting( name: String, @@ -115,14 +114,14 @@ abstract class Configurable( description: String = "", noinline visibility: () -> Boolean = { true }, - ) = EnumSetting(name, defaultValue, description, visibility).register() + ) = Setting(name, description,EnumSetting(defaultValue), this, visibility).register() fun setting( name: String, defaultValue: Char, description: String = "", visibility: () -> Boolean = { true }, - ) = CharSetting(name, defaultValue, description, visibility).register() + ) = Setting(name, description, CharSetting(defaultValue), this, visibility).register() fun setting( name: String, @@ -131,65 +130,53 @@ abstract class Configurable( flags: Int = ImGuiInputTextFlags.None, description: String = "", visibility: () -> Boolean = { true }, - ) = StringSetting(name, defaultValue, multiline, flags, description, visibility).register() - + ) = Setting(name, description, StringSetting(defaultValue, multiline, flags), this, visibility).register() + @JvmName("collectionSetting1") fun setting( name: String, defaultValue: Collection, immutableCollection: Collection = Registries.BLOCK.toList(), description: String = "", visibility: () -> Boolean = { true }, - ) = BlockCollectionSetting( - name, - immutableCollection, - defaultValue.toMutableList(), - description, - visibility, - ).register() + ) = Setting(name, description, BlockCollectionSetting(immutableCollection, defaultValue.toMutableList()), this, visibility).register() + @JvmName("collectionSetting2") fun setting( name: String, defaultValue: Collection, immutableCollection: Collection = Registries.ITEM.toList(), description: String = "", visibility: () -> Boolean = { true }, - ) = ItemCollectionSetting( - name, - immutableCollection, - defaultValue.toMutableList(), - description, - visibility, - ).register() + ) = Setting(name, description, ItemCollectionSetting(immutableCollection, defaultValue.toMutableList()), this, visibility).register() + @JvmName("collectionSetting3") inline fun > setting( name: String, defaultValue: Collection, immutableList: Collection = defaultValue, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = CollectionSetting( - name, - defaultValue.toMutableList(), - immutableList, - TypeToken.getParameterized(Collection::class.java, T::class.java).type, - description, - visibility, - ).register() + ) = Setting( + name, + description, + CollectionSetting( + defaultValue.toMutableList(), + immutableList, + TypeToken.getParameterized(Collection::class.java, T::class.java).type + ), + this, + visibility + ).register() + @JvmName("collectionSetting4") inline fun setting( name: String, defaultValue: Collection, immutableList: Collection = defaultValue, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = ClassCollectionSetting( - name, - immutableList, - defaultValue.toMutableList(), - description, - visibility, - ).register() + ) = Setting(name, description, ClassCollectionSetting(immutableList, defaultValue.toMutableList()), this, visibility).register() // ToDo: Actually implement maps inline fun setting( @@ -197,13 +184,16 @@ abstract class Configurable( defaultValue: Map, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = MapSetting( - name, - defaultValue.toMutableMap(), - TypeToken.getParameterized(MutableMap::class.java, K::class.java, V::class.java).type, - description, - visibility - ).register() + ) = Setting( + name, + description, + MapSetting( + defaultValue.toMutableMap(), + TypeToken.getParameterized(MutableMap::class.java, K::class.java, V::class.java).type + ), + this, + visibility + ).register() fun setting( name: String, @@ -213,7 +203,7 @@ abstract class Configurable( description: String = "", unit: String = "", visibility: () -> Boolean = { true }, - ) = DoubleSetting(name, defaultValue, range, step, description, unit, visibility).register() + ) = Setting(name, description, DoubleSetting(defaultValue, range, step, unit), this, visibility).register() fun setting( name: String, @@ -223,7 +213,7 @@ abstract class Configurable( description: String = "", unit: String = "", visibility: () -> Boolean = { true }, - ) = FloatSetting(name, defaultValue, range, step, description, unit, visibility).register() + ) = Setting(name, description, FloatSetting(defaultValue, range, step, unit), this, visibility).register() fun setting( name: String, @@ -233,7 +223,7 @@ abstract class Configurable( description: String = "", unit: String = "", visibility: () -> Boolean = { true }, - ) = IntegerSetting(name, defaultValue, range, step, description, unit, visibility).register() + ) = Setting(name, description, IntegerSetting(defaultValue, range, step, unit), this, visibility).register() fun setting( name: String, @@ -243,61 +233,61 @@ abstract class Configurable( description: String = "", unit: String = "", visibility: () -> Boolean = { true }, - ) = LongSetting(name, defaultValue, range, step, description, unit, visibility).register() + ) = Setting(name, description, LongSetting(defaultValue, range, step, unit), this, visibility).register() fun setting( name: String, defaultValue: Bind, description: String = "", visibility: () -> Boolean = { true }, - ) = KeybindSetting(name, defaultValue, description, visibility).register() + ) = Setting(name, description, KeybindSettingCore(defaultValue), this, visibility).register() fun setting( name: String, defaultValue: KeyCode, description: String = "", visibility: () -> Boolean = { true }, - ) = KeybindSetting(name, defaultValue, description, visibility).register() + ) = Setting(name, description, KeybindSettingCore(defaultValue), this, visibility).register() fun setting( name: String, defaultValue: Color, description: String = "", visibility: () -> Boolean = { true }, - ) = ColorSetting(name, defaultValue, description, visibility).register() + ) = Setting(name, description, ColorSetting(defaultValue), this, visibility).register() fun setting( name: String, defaultValue: Vec3d, description: String = "", visibility: () -> Boolean = { true }, - ) = Vec3dSetting(name, defaultValue, description, visibility).register() + ) = Setting(name, description, Vec3dSetting(defaultValue), this, visibility).register() fun setting( name: String, defaultValue: BlockPos.Mutable, description: String = "", visibility: () -> Boolean = { true }, - ) = BlockPosSetting(name, defaultValue, description, visibility).register() + ) = Setting(name, description, BlockPosSetting(defaultValue), this, visibility).register() fun setting( name: String, defaultValue: BlockPos, description: String = "", visibility: () -> Boolean = { true }, - ) = BlockPosSetting(name, defaultValue, description, visibility).register() + ) = Setting(name, description, BlockPosSetting(defaultValue), this, visibility).register() fun setting( name: String, defaultValue: Block, description: String = "", visibility: () -> Boolean = { true }, - ) = BlockSetting(name, defaultValue, description, visibility).register() + ) = Setting(name, description, BlockSetting(defaultValue), this, visibility).register() fun setting( name: String, defaultValue: () -> Unit, description: String = "", visibility: () -> Boolean = { true } - ) = FunctionSetting(name, defaultValue, description, visibility).register() -} + ) = Setting(name, description, FunctionSetting(defaultValue), this, visibility).register() +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/config/Configuration.kt b/src/main/kotlin/com/lambda/config/Configuration.kt index 102924ace..c00385c7f 100644 --- a/src/main/kotlin/com/lambda/config/Configuration.kt +++ b/src/main/kotlin/com/lambda/config/Configuration.kt @@ -42,7 +42,7 @@ import kotlin.time.Duration.Companion.minutes /** - * Represents a compound of [Configurable] objects whose [AbstractSetting]s + * Represents a compound of [Configurable] objects whose [SettingCore]s * are saved into a single [Configuration] file ([Configuration.primary]). * * This class also handles the concurrent loading and saving of persisted data on the `Dispatchers.IO` thread. @@ -72,6 +72,9 @@ abstract class Configuration : Jsonable, Loadable { // Avoid context-leaking warning private fun register() { + if (configurations.any { it.configName == configName }) + throw IllegalStateException("Configuration with name $configName already exists") + fixedRateTimer( daemon = true, name = "Scheduler-config-${configName}", @@ -159,12 +162,8 @@ abstract class Configuration : Jsonable, Loadable { val configurations = mutableSetOf() val configurables: Set get() = configurations.flatMapTo(mutableSetOf()) { it.configurables } - val settings: Set> - get() = configurables.flatMapTo(mutableSetOf()) { it.settings } - - //ToDo: Store owner in setting - fun configurableBySetting(setting: AbstractSetting<*>) = - configurables.find { it.settings.contains(setting) } + val settings: List> + get() = configurables.flatMapTo(mutableListOf()) { it.settings } fun configurableByName(name: String) = configurables.find { it.name == name } @@ -172,9 +171,6 @@ abstract class Configuration : Jsonable, Loadable { fun configurableByCommandName(name: String) = configurables.find { it.commandName == name } - fun settingByName(configurable: Configurable, name: String) = - configurable.settings.find { it.name == name } - fun settingByCommandName(configurable: Configurable, name: String) = configurable.settings.find { it.commandName == name } } diff --git a/src/main/kotlin/com/lambda/config/MutableAutomationConfig.kt b/src/main/kotlin/com/lambda/config/MutableAutomationConfig.kt index 58a90eea8..ee9bf5aca 100644 --- a/src/main/kotlin/com/lambda/config/MutableAutomationConfig.kt +++ b/src/main/kotlin/com/lambda/config/MutableAutomationConfig.kt @@ -29,14 +29,43 @@ import com.lambda.interaction.request.rotating.RotationConfig interface MutableAutomationConfig : Automated { var defaultAutomationConfig: AutomationConfig - var automationConfig: AutomationConfig + var backingAutomationConfig: AutomationConfig + var automationConfig: AutomationConfig - override val buildConfig: BuildConfig get() = automationConfig.buildConfig - override val breakConfig: BreakConfig get() = automationConfig.breakConfig - override val placeConfig: PlaceConfig get() = automationConfig.placeConfig - override val interactConfig: InteractConfig get() = automationConfig.interactConfig - override val rotationConfig: RotationConfig get() = automationConfig.rotationConfig - override val inventoryConfig: InventoryConfig get() = automationConfig.inventoryConfig - override val hotbarConfig: HotbarConfig get() = automationConfig.hotbarConfig - override val eatConfig: EatConfig get() = automationConfig.eatConfig + override val buildConfig: BuildConfig get() = automationConfig.buildConfig + override val breakConfig: BreakConfig get() = automationConfig.breakConfig + override val placeConfig: PlaceConfig get() = automationConfig.placeConfig + override val interactConfig: InteractConfig get() = automationConfig.interactConfig + override val rotationConfig: RotationConfig get() = automationConfig.rotationConfig + override val inventoryConfig: InventoryConfig get() = automationConfig.inventoryConfig + override val hotbarConfig: HotbarConfig get() = automationConfig.hotbarConfig + override val eatConfig: EatConfig get() = automationConfig.eatConfig +} + +class MutableAutomationConfigImpl : MutableAutomationConfig { + override var defaultAutomationConfig: AutomationConfig = AutomationConfig.Companion.DEFAULT + set(value) { + field = value + automationConfig = value + } + override var backingAutomationConfig: AutomationConfig = defaultAutomationConfig + override var automationConfig: AutomationConfig = defaultAutomationConfig + set(value) { + if (value === defaultAutomationConfig) { + if (backingAutomationConfig !== defaultAutomationConfig) { + field.settings.forEach(Setting<*, *>::restoreOriginalCore) + } + field = value + } else field.settings.forEach { setting -> + value.settings.forEach { newSetting -> + if (setting.name == newSetting.name) { + if (setting.core.type != newSetting.core.type) + throw IllegalStateException("Settings with the same name do not have the same type.") + @Suppress("UNCHECKED_CAST") + (setting as Setting, Any>).core = newSetting.core as SettingCore + } + } + } + backingAutomationConfig = value + } } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/config/Setting.kt b/src/main/kotlin/com/lambda/config/Setting.kt new file mode 100644 index 000000000..457b74817 --- /dev/null +++ b/src/main/kotlin/com/lambda/config/Setting.kt @@ -0,0 +1,285 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.config + +import com.google.gson.JsonElement +import com.google.gson.JsonParser +import com.lambda.Lambda.LOG +import com.lambda.Lambda.gson +import com.lambda.brigadier.CommandResult.Companion.failure +import com.lambda.brigadier.CommandResult.Companion.success +import com.lambda.brigadier.argument.string +import com.lambda.brigadier.argument.value +import com.lambda.brigadier.executeWithResult +import com.lambda.brigadier.required +import com.lambda.command.CommandRegistry +import com.lambda.command.commands.ConfigCommand +import com.lambda.context.SafeContext +import com.lambda.gui.dsl.ImGuiBuilder +import com.lambda.threading.runSafe +import com.lambda.util.Communication.info +import com.lambda.util.Describable +import com.lambda.util.Nameable +import com.lambda.util.NamedEnum +import com.lambda.util.extension.CommandBuilder +import com.lambda.util.text.ClickEvents +import com.lambda.util.text.HoverEvents +import com.lambda.util.text.TextBuilder +import com.lambda.util.text.buildText +import com.lambda.util.text.clickEvent +import com.lambda.util.text.highlighted +import com.lambda.util.text.hoverEvent +import com.lambda.util.text.literal +import net.minecraft.command.CommandRegistryAccess +import java.lang.reflect.Type +import kotlin.reflect.KProperty + +/** + * Represents a setting with a [defaultValue], [visibility] condition, and [description]. + * This setting is serializable ([Jsonable]) and has a [name]. + * + * When the [value] is modified, all registered [listeners] are notified. + * The [visibility] of the setting can be checked with the [isVisible] property. + * The setting can be [reset] to its [defaultValue]. + * + * Simple Usage: + * ```kotlin + * // this uses the delegate (by) association to access the setting value in the code directly. + * val mode by setting("Mode", Modes.FREEZE, { page == Page.CUSTOM }, "The mode of the module.") + * + * init { + * listener { + * LOG.info("Mode: $mode") // direct access of the value + * } + * } + * ``` + * + * Advanced usage with listeners: + * ```kotlin + * // notice how this does not use the delegate (by) association, to access the setting object to register listeners. + * val mode = setting("Mode", Modes.FREEZE, { page == Page.CUSTOM }, "The mode of the module.") + * + * init { + * mode.listener { from, to -> + * // Do something when the mode changes in a safe context + * } + * mode.unsafeListener { from, to -> + * // Do something when the mode changes in an unsafe context + * } + * + * listener { + * LOG.info("Mode: ${mode.value}") // indirect access of the value + * } + * } + * ``` + * + * @property defaultValue The default value of the setting. + * @property description A description of the setting. + * @property type The type reflection of the setting. + * @property visibility A function that determines whether the setting is visible. + */ +abstract class SettingCore( + var defaultValue: T, + val type: Type +) { + var value = defaultValue + + context(setting: Setting<*, T>) + abstract fun ImGuiBuilder.buildLayout() + + context(setting: Setting<*, T>) + open fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { + required(string("value as JSON")) { value -> + executeWithResult { + val valueString = value().value() + val parsed = try { + JsonParser.parseString("\"$valueString\"") + } catch (_: Exception) { + return@executeWithResult failure("$valueString is not a valid JSON string.") + } + ?: return@executeWithResult failure("No config found for $name.") + val previous = this@SettingCore.value + try { + loadFromJson(parsed) + } catch (_: Exception) { + return@executeWithResult failure("Failed to load $valueString as a ${type::class.simpleName} for $name in ${setting.configurable.name}.") + } + ConfigCommand.info(setting.setMessage(previous, this@SettingCore.value)) + return@executeWithResult success() + } + } + } + + context(setting: Setting<*, T>) + open fun toJson(): JsonElement = + gson.toJsonTree(value, type) + + context(setting: Setting<*, T>) + open fun loadFromJson(serialized: JsonElement) { + runCatching { + value = gson.fromJson(serialized, type) + }.onFailure { + LOG.warn("Failed to load setting ${setting.name} with value $serialized. Resetting to default value $defaultValue") + value = defaultValue + } + } +} + +class Setting, R : Any>( + override val name: String, + override val description: String, + var core: T, + val configurable: Configurable, + val visibility: () -> Boolean, +) : Nameable, Describable { + val originalCore = core + val listeners = mutableListOf>() + var disabled = { false } + var groups: MutableList> = mutableListOf() + + var value by this + + val isModified get() = value != core.defaultValue + + operator fun getValue(thisRef: Any?, property: KProperty<*>) = core.value + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: R) { + val oldValue = core.value + core.value = value + listeners.forEach { + if (it.requiresValueChange && oldValue == value) return@forEach + it.execute(oldValue, value) + } + } + + fun reset(silent: Boolean = false) { + if (!silent && value == core.defaultValue) { + ConfigCommand.info(notChangedMessage()) + return + } + if (!silent) ConfigCommand.info(resetMessage(value, core.defaultValue)) + value = core.defaultValue + } + + fun restoreOriginalCore() { + core = originalCore + } + + fun ImGuiBuilder.buildLayout() = with(core) { buildLayout() } + fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) = with(core) { buildCommand(registry) } + + fun toJson() = originalCore.toJson() + fun loadFromJson(serialized: JsonElement) = originalCore.loadFromJson(serialized) + + class ValueListener(val requiresValueChange: Boolean, val execute: (from: T, to: T) -> Unit) + + /** + * Will only register changes of the variable, not the content of the variable! + * E.g., if the variable is a list, it will only register if the list reference changes, not if the content of the list changes. + */ + fun onValueChange(block: SafeContext.(from: R, to: R) -> Unit) = apply { + listeners.add(ValueListener(true) { from, to -> + runSafe { + block(from, to) + } + }) + } + + fun onValueChangeUnsafe(block: (from: R, to: R) -> Unit) = apply { + listeners.add(ValueListener(true, block)) + } + + fun onValueSet(block: (from: R, to: R) -> Unit) = apply { + listeners.add(ValueListener(false, block)) + } + + fun disabled(predicate: () -> Boolean) = apply { + disabled = predicate + } + + fun group(path: List, vararg continuation: NamedEnum) = apply { + groups.add(path + continuation) + } + + fun group(vararg path: NamedEnum) = apply { + groups.add(path.toList()) + } + + fun group(path: NamedEnum?) = apply { + path?.let { groups.add(listOf(it)) } + } + + fun trySetValue(newValue: R) { + if (newValue == value) { + ConfigCommand.info(notChangedMessage()) + } else { + val previous = value + value = newValue + ConfigCommand.info(setMessage(previous, newValue)) + } + } + + fun setMessage(previousValue: R, newValue: R) = buildText { + literal("Set ") + changedMessage(previousValue, newValue) + clickEvent(ClickEvents.suggestCommand("${CommandRegistry.prefix}${ConfigCommand.name} reset ${configurable.commandName} $commandName")) { + hoverEvent(HoverEvents.showText(buildText { + literal("Click to reset to default value ") + highlighted(core.defaultValue.toString()) + })) { + highlighted(" [Reset]") + } + } + } + + private fun resetMessage(previousValue: R, newValue: R) = buildText { + literal("Reset ") + changedMessage(previousValue, newValue) + } + + private fun notChangedMessage() = buildText { + literal("No changes made to ") + highlighted(name) + literal(" as it is already set to ") + highlighted(value.toString()) + literal(".") + } + + private fun TextBuilder.changedMessage(previousValue: R, newValue: R) { + highlighted(configurable.name) + literal(" > ") + highlighted(name) + literal(" from ") + highlighted(previousValue.toString()) + literal(" to ") + highlighted(newValue.toString()) + literal(".") + clickEvent(ClickEvents.suggestCommand("${CommandRegistry.prefix}${ConfigCommand.name} set ${configurable.commandName} $commandName $previousValue")) { + hoverEvent(HoverEvents.showText(buildText { + literal("Click to undo to previous value ") + highlighted(previousValue.toString()) + })) { + highlighted(" [Undo]") + } + } + } + + override fun toString() = "Setting $name: $value of type ${core.type.typeName}" + + override fun equals(other: Any?) = other is Setting<*, *> && name == other.name + override fun hashCode() = name.hashCode() +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/config/SettingGroup.kt b/src/main/kotlin/com/lambda/config/SettingGroup.kt index 6f45278b7..6ee65aae3 100644 --- a/src/main/kotlin/com/lambda/config/SettingGroup.kt +++ b/src/main/kotlin/com/lambda/config/SettingGroup.kt @@ -18,17 +18,17 @@ package com.lambda.config interface ISettingGroup { - val settings: MutableList> + val settings: MutableList> } abstract class SettingGroup(c: Configurable) : ISettingGroup { - override val settings = mutableListOf>() + override val settings = mutableListOf>() init { c.settingGroups.add(this) } - fun AbstractSetting.index(): AbstractSetting { + fun , R : Any> Setting.index(): Setting { settings.add(this) return this } diff --git a/src/main/kotlin/com/lambda/config/UserAutomationConfig.kt b/src/main/kotlin/com/lambda/config/UserAutomationConfig.kt index e629012e8..d2398c16b 100644 --- a/src/main/kotlin/com/lambda/config/UserAutomationConfig.kt +++ b/src/main/kotlin/com/lambda/config/UserAutomationConfig.kt @@ -18,6 +18,8 @@ package com.lambda.config import com.lambda.config.configurations.UserAutomationConfigs +import com.lambda.config.settings.collections.CollectionSetting.Companion.onDeselect +import com.lambda.config.settings.collections.CollectionSetting.Companion.onSelect import com.lambda.module.Module import com.lambda.module.ModuleRegistry.moduleNameMap @@ -29,7 +31,6 @@ class UserAutomationConfig(override val name: String) : AutomationConfig(name, U it.automationConfig = this@UserAutomationConfig } } - .onDeselect { name -> moduleNameMap[name]?.let { module -> module.automationConfig = module.defaultAutomationConfig @@ -37,6 +38,6 @@ class UserAutomationConfig(override val name: String) : AutomationConfig(name, U } private fun Module.removeLink() { - (automationConfig as UserAutomationConfig).linkedModules.value -= name + (automationConfig as? UserAutomationConfig)?.linkedModules?.value -= name } } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/config/settings/CharSetting.kt b/src/main/kotlin/com/lambda/config/settings/CharSetting.kt index 2e2abaea3..d32c394be 100644 --- a/src/main/kotlin/com/lambda/config/settings/CharSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/CharSetting.kt @@ -24,7 +24,8 @@ import com.lambda.brigadier.argument.value import com.lambda.brigadier.argument.word import com.lambda.brigadier.executeWithResult import com.lambda.brigadier.required -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.extension.CommandBuilder import net.minecraft.command.CommandRegistryAccess @@ -32,27 +33,19 @@ import net.minecraft.command.CommandRegistryAccess /** * @see [com.lambda.config.Configurable] */ -class CharSetting( - override var name: String, - defaultValue: Char, - description: String, - visibility: () -> Boolean, -) : AbstractSetting( - name, - defaultValue, - TypeToken.get(Char::class.java).type, - description, - visibility +class CharSetting(defaultValue: Char) : SettingCore( + defaultValue, + TypeToken.get(Char::class.java).type ) { - override fun ImGuiBuilder.buildLayout() { - - } + context(setting: Setting<*, Char>) + override fun ImGuiBuilder.buildLayout() {} + context(setting: Setting<*, Char>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { - required(word(name)) { parameter -> + required(word(setting.name)) { parameter -> executeWithResult { val char = parameter().value().firstOrNull() ?: return@executeWithResult failure("Cant parse char type") - trySetValue(char) + setting.trySetValue(char) return@executeWithResult success() } } diff --git a/src/main/kotlin/com/lambda/config/settings/FunctionSetting.kt b/src/main/kotlin/com/lambda/config/settings/FunctionSetting.kt index 00f04629b..eb3cef115 100644 --- a/src/main/kotlin/com/lambda/config/settings/FunctionSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/FunctionSetting.kt @@ -20,26 +20,22 @@ package com.lambda.config.settings import com.google.gson.JsonElement import com.google.gson.JsonNull import com.google.gson.reflect.TypeToken -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.gui.dsl.ImGuiBuilder -open class FunctionSetting( - override var name: String, - defaultValue: () -> T, - description: String, - visibility: () -> Boolean, -) : AbstractSetting<() -> T>( - name, - defaultValue, - TypeToken.get(defaultValue::class.java).type, - description, - visibility +open class FunctionSetting(defaultValue: () -> T) : SettingCore<() -> T>( + defaultValue, + TypeToken.get(defaultValue::class.java).type ) { - override fun ImGuiBuilder.buildLayout() { - button(name) { value() } - lambdaTooltip(description) + context(setting: Setting<*, () -> T>) + override fun ImGuiBuilder.buildLayout() { + button(setting.name) { value() } + lambdaTooltip(setting.description) } + context(setting: Setting<*, () -> T>) override fun toJson(): JsonElement = JsonNull.INSTANCE + context(setting: Setting<*, () -> T>) override fun loadFromJson(serialized: JsonElement) { value = defaultValue } } diff --git a/src/main/kotlin/com/lambda/config/settings/NumericSetting.kt b/src/main/kotlin/com/lambda/config/settings/NumericSetting.kt index 162d7fbf1..cdc003227 100644 --- a/src/main/kotlin/com/lambda/config/settings/NumericSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/NumericSetting.kt @@ -18,7 +18,8 @@ package com.lambda.config.settings import com.google.gson.reflect.TypeToken -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.config.SettingEditorDsl import com.lambda.config.SettingGroupEditor import com.lambda.gui.dsl.ImGuiBuilder @@ -34,60 +35,56 @@ import kotlin.reflect.KProperty * @see [com.lambda.config.Configurable] */ abstract class NumericSetting( - override var name: String, value: T, open var range: ClosedRange, open var step: T, - description: String, - var unit: String, - visibility: () -> Boolean -) : AbstractSetting( - name, - value, - TypeToken.get(value::class.java).type, - description, - visibility + var unit: String +) : SettingCore( + value, + TypeToken.get(value::class.java).type ) where T : Number, T : Comparable { private val formatter = NumberFormat.getNumberInstance(Locale.getDefault()) override fun toString() = "${formatter.format(value)}$unit" - override operator fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: T) { + operator fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: T) { value = valueIn.coerceIn(range) } /** * Subclasses must implement this to provide their specific slider widget. */ + context(setting: Setting<*, T>) protected abstract fun ImGuiBuilder.buildSlider() + context(setting: Setting<*, T>) override fun ImGuiBuilder.buildLayout() { - val showReset = isModified + val showReset = setting.isModified val resetButtonText = "R" val valueString = this@NumericSetting.toString() buildSlider() - lambdaTooltip(description) + lambdaTooltip(setting.description) val itemRectMin = ImGui.getItemRectMin() val itemRectMax = ImGui.getItemRectMax() val textHeight = ImGui.getTextLineHeight() val textY = itemRectMin.y + (itemRectMax.y - itemRectMin.y - textHeight) / 2.0f - val labelWidth = calcTextSize(name).x + val labelWidth = calcTextSize(setting.name).x val valueWidth = calcTextSize(valueString).x val labelEndPosX = itemRectMin.x + style.framePadding.x * 2 + labelWidth val valueStartPosX = itemRectMax.x - style.framePadding.x * 2 - valueWidth - windowDrawList.addText(itemRectMin.x + style.framePadding.x * 2, textY, ImGui.getColorU32(ImGuiCol.Text), name) + windowDrawList.addText(itemRectMin.x + style.framePadding.x * 2, textY, ImGui.getColorU32(ImGuiCol.Text), setting.name) if (labelEndPosX < valueStartPosX) { windowDrawList.addText(valueStartPosX, textY, ImGui.getColorU32(ImGuiCol.Text), valueString) } sameLine(0.0f, style.itemSpacing.x) if (showReset) { - button("$resetButtonText##$name") { - reset() + button("$resetButtonText##${setting.name}") { + setting.reset() } onItemHover { tooltip { text("Reset to default") } diff --git a/src/main/kotlin/com/lambda/config/settings/StringSetting.kt b/src/main/kotlin/com/lambda/config/settings/StringSetting.kt index f6caa6c12..fdf569979 100644 --- a/src/main/kotlin/com/lambda/config/settings/StringSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/StringSetting.kt @@ -22,7 +22,8 @@ import com.lambda.brigadier.argument.greedyString import com.lambda.brigadier.argument.value import com.lambda.brigadier.execute import com.lambda.brigadier.required -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.config.SettingEditorDsl import com.lambda.config.SettingGroupEditor import com.lambda.gui.dsl.ImGuiBuilder @@ -34,32 +35,28 @@ import net.minecraft.command.CommandRegistryAccess * @see [com.lambda.config.Configurable] */ class StringSetting( - override var name: String, defaultValue: String, var multiline: Boolean = false, var flags: Int = ImGuiInputTextFlags.None, - description: String, - visibility: () -> Boolean, -) : AbstractSetting( - name, - defaultValue, - TypeToken.get(String::class.java).type, - description, - visibility +) : SettingCore( + defaultValue, + TypeToken.get(String::class.java).type ) { + context(setting: Setting<*, String>) override fun ImGuiBuilder.buildLayout() { if (multiline) { - inputTextMultiline(name, ::value, flags = flags) + inputTextMultiline(setting.name, ::value, flags = flags) } else { - inputText(name, ::value, flags) + inputText(setting.name, ::value, flags) } - lambdaTooltip(description) + lambdaTooltip(setting.description) } + context(setting: Setting<*, String>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { - required(greedyString(name)) { parameter -> + required(greedyString(setting.name)) { parameter -> execute { - trySetValue(parameter().value()) + setting.trySetValue(parameter().value()) } } } diff --git a/src/main/kotlin/com/lambda/config/settings/collections/BlockCollectionSetting.kt b/src/main/kotlin/com/lambda/config/settings/collections/BlockCollectionSetting.kt index da9ad7543..268650544 100644 --- a/src/main/kotlin/com/lambda/config/settings/collections/BlockCollectionSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/collections/BlockCollectionSetting.kt @@ -20,68 +20,25 @@ package com.lambda.config.settings.collections import com.google.gson.JsonElement import com.google.gson.reflect.TypeToken import com.lambda.Lambda.gson -import com.lambda.config.serializer.BlockCodec +import com.lambda.config.Setting import com.lambda.gui.dsl.ImGuiBuilder -import com.lambda.util.StringUtils.levenshteinDistance -import imgui.ImGuiListClipper -import imgui.flag.ImGuiChildFlags -import imgui.flag.ImGuiSelectableFlags.DontClosePopups import net.minecraft.block.Block class BlockCollectionSetting( - override var name: String, - private val immutableCollection: Collection, + immutableCollection: Collection, defaultValue: MutableCollection, - description: String, - visibility: () -> Boolean, ) : CollectionSetting( - name, defaultValue, immutableCollection, - TypeToken.getParameterized(Collection::class.java, Block::class.java).type, - description, - visibility, + TypeToken.getParameterized(Collection::class.java, Block::class.java).type ) { - private var searchFilter = "" - - override fun ImGuiBuilder.buildLayout() { - val text = if (value.size == 1) "block" else "blocks" - - combo("##$name", "$name: ${value.size} $text") { - inputText("##$name-SearchBox", ::searchFilter) - - child( - strId = "##$name-ComboOptionsChild", - childFlags = ImGuiChildFlags.AutoResizeY or ImGuiChildFlags.AlwaysAutoResize, - ) { - val list = immutableCollection - .filter { searchFilter == "" || searchFilter.levenshteinDistance(BlockCodec.stringify(it)) < 3 } - - ImGuiListClipper.forEach { // not actually iterating - it.begin(list.size) - - while (it.step()) { - for (i in it.displayStart..it.displayEnd) { - val v = list.getOrNull(i) ?: continue - val selected = value.contains(v) - - selectable( - label = BlockCodec.stringify(v), - selected = selected, - flags = DontClosePopups - ) { - if (selected) value.remove(v) - else value.add(v) - } - } - } - } - } - } - } + context(setting: Setting<*, MutableCollection>) + override fun ImGuiBuilder.buildLayout() = buildComboBox("block") + context(setting: Setting<*, MutableCollection>) override fun toJson(): JsonElement = gson.toJsonTree(value, type) + context(setting: Setting<*, MutableCollection>) override fun loadFromJson(serialized: JsonElement) { value = gson.fromJson>(serialized, type) .toMutableList() diff --git a/src/main/kotlin/com/lambda/config/settings/collections/ClassCollectionSetting.kt b/src/main/kotlin/com/lambda/config/settings/collections/ClassCollectionSetting.kt index badc04fd5..2047f6169 100644 --- a/src/main/kotlin/com/lambda/config/settings/collections/ClassCollectionSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/collections/ClassCollectionSetting.kt @@ -20,74 +20,32 @@ package com.lambda.config.settings.collections import com.google.gson.JsonElement import com.google.gson.reflect.TypeToken import com.lambda.Lambda.gson +import com.lambda.config.Setting import com.lambda.gui.dsl.ImGuiBuilder -import com.lambda.util.StringUtils.levenshteinDistance import com.lambda.util.reflections.className -import imgui.ImGuiListClipper -import imgui.flag.ImGuiChildFlags -import imgui.flag.ImGuiSelectableFlags.DontClosePopups /** * @see [com.lambda.config.settings.collections.CollectionSetting] * @see [com.lambda.config.Configurable] */ class ClassCollectionSetting( - override var name: String, private val immutableCollection: Collection, - defaultValue: MutableCollection, - description: String, - visibility: () -> Boolean, + defaultValue: MutableCollection ) : CollectionSetting( - name, defaultValue, immutableCollection, - TypeToken.getParameterized(Collection::class.java, Any::class.java).type, - description, - visibility, + TypeToken.getParameterized(Collection::class.java, Any::class.java).type ) { - private var searchFilter = "" - - override fun ImGuiBuilder.buildLayout() { - val text = if (value.size == 1) "item" else "items" - - combo("##$name", "$name: ${value.size} $text") { - inputText("##$name-SearchBox", ::searchFilter) - - child( - strId = "##$name-ComboOptionsChild", - childFlags = ImGuiChildFlags.AutoResizeY or ImGuiChildFlags.AlwaysAutoResize, - ) { - val list = immutableCollection - .filter { searchFilter == "" || searchFilter.levenshteinDistance(it.className) < 3 } - - ImGuiListClipper.forEach { // not actually iterating - it.begin(list.size) - - while (it.step()) { - for (i in it.displayStart..it.displayEnd) { - val v = list.getOrNull(i) ?: continue - val selected = value.contains(v) - - selectable( - label = v.className, - selected = selected, - flags = DontClosePopups - ) { - if (selected) value.remove(v) - else value.add(v) - } - } - } - } - } - } - } + context(setting: Setting<*, MutableCollection>) + override fun ImGuiBuilder.buildLayout() = buildComboBox("item") // When serializing the list to json we do not want to serialize the elements' classes, but their stringified representation. // If we do serialize the classes we'll run into missing type adapters errors by Gson. // This is intended behaviour. If you wish your collection settings to display something else then you must extend this class. + context(setting: Setting<*, MutableCollection>) override fun toJson(): JsonElement = gson.toJsonTree(value.map { it.className }) + context(setting: Setting<*, MutableCollection>) override fun loadFromJson(serialized: JsonElement) { val strList = gson.fromJson>(serialized, type) .mapNotNull { str -> immutableCollection.find { it.className == str } } diff --git a/src/main/kotlin/com/lambda/config/settings/collections/CollectionSetting.kt b/src/main/kotlin/com/lambda/config/settings/collections/CollectionSetting.kt index c2f5b5429..9242f38b6 100644 --- a/src/main/kotlin/com/lambda/config/settings/collections/CollectionSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/collections/CollectionSetting.kt @@ -20,14 +20,15 @@ package com.lambda.config.settings.collections import com.google.gson.JsonElement import com.google.gson.reflect.TypeToken import com.lambda.Lambda.gson -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.config.SettingEditorDsl import com.lambda.config.SettingGroupEditor import com.lambda.context.SafeContext import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.threading.runSafe -import com.lambda.util.StringUtils.levenshteinDistance import imgui.ImGuiListClipper +import imgui.callback.ImListClipperCallback import imgui.flag.ImGuiChildFlags import imgui.flag.ImGuiSelectableFlags.DontClosePopups import java.lang.reflect.Type @@ -42,79 +43,72 @@ import java.lang.reflect.Type * * @see [com.lambda.config.Configurable] */ -open class CollectionSetting( - override var name: String, - defaultValue: MutableCollection, - private var immutableCollection: Collection, - type: Type, - description: String, - visibility: () -> Boolean, -) : AbstractSetting>( - name, - defaultValue, - type, - description, - visibility +open class CollectionSetting( + defaultValue: MutableCollection, + private var immutableCollection: Collection, + type: Type +) : SettingCore>( + defaultValue, + type ) { private var searchFilter = "" private val strListType = TypeToken.getParameterized(Collection::class.java, String::class.java).type - private val selectListeners = mutableListOf Unit>() - private val deselectListeners = mutableListOf Unit>() - - fun onSelect(block: SafeContext.(T) -> Unit) = apply { - selectListeners.add(block) - } - - fun onDeselect(block: SafeContext.(T) -> Unit) = apply { - deselectListeners.add(block) - } - - override fun ImGuiBuilder.buildLayout() { - val text = if (value.size == 1) "item" else "items" - - combo("##$name", "$name: ${value.size} $text") { - inputText("##$name-SearchBox", ::searchFilter) - - child( - strId = "##$name-ComboOptionsChild", - childFlags = ImGuiChildFlags.AutoResizeY or ImGuiChildFlags.AlwaysAutoResize, - ) { - val list = immutableCollection - .filter { searchFilter == "" || searchFilter.levenshteinDistance(it.toString()) < 3 } - - ImGuiListClipper.forEach { // not actually iterating - it.begin(list.size) - - while (it.step()) { - for (i in it.displayStart..it.displayEnd) { - val v = list.getOrNull(i) ?: continue - val selected = value.contains(v) - - selectable( - label = v.toString(), - selected = selected, - flags = DontClosePopups - ) { - if (selected) { - value.remove(v) - runSafe { deselectListeners.forEach { f -> f(v) } } - } else { - value.add(v) - runSafe { selectListeners.forEach { f -> f(v) } } - } - } - } - } - } - } - } - } - + val selectListeners = mutableListOf Unit>() + val deselectListeners = mutableListOf Unit>() + + context(setting: Setting<*, MutableCollection>) + override fun ImGuiBuilder.buildLayout() = buildComboBox("item") + + context(setting: Setting<*, MutableCollection>) + fun ImGuiBuilder.buildComboBox(itemName: String) { + val text = if (value.size == 1) itemName else "${itemName}s" + + combo("##${setting.name}", "${setting.name}: ${value.size} $text") { + inputText("##${setting.name}-SearchBox", ::searchFilter) + + child( + strId = "##${setting.name}-ComboOptionsChild", + childFlags = ImGuiChildFlags.AutoResizeY or ImGuiChildFlags.AlwaysAutoResize, + ) { + val list = immutableCollection + .filter { item -> + val q = searchFilter.trim() + if (q.isEmpty()) true + else item.toString().startsWith(q, ignoreCase = true) + } + + val listClipperCallback = object : ImListClipperCallback() { + override fun accept(index: Int) { + val v = list.getOrNull(index) ?: return + val selected = value.contains(v) + + selectable( + label = v.toString(), + selected = selected, + flags = DontClosePopups + ) { + if (selected) { + value.remove(v) + runSafe { deselectListeners.forEach { listener -> listener(v) } } + } else { + value.add(v) + runSafe { selectListeners.forEach { listener -> listener(v) } } + } + } + } + } + ImGuiListClipper.forEach(list.size, listClipperCallback) + } + } + } + + context(setting: Setting<*, MutableCollection>) override fun toJson(): JsonElement = gson.toJsonTree(value.map { it.toString() }) + context(setting: Setting<*, MutableCollection>) override fun loadFromJson(serialized: JsonElement) { val strList = gson.fromJson>(serialized, strListType) .mapNotNull { str -> immutableCollection.find { it.toString() == str } } @@ -123,7 +117,15 @@ open class CollectionSetting( value = strList } - companion object { + companion object { + fun , R : Any> Setting>.onSelect(block: SafeContext.(R) -> Unit) = apply { + core.selectListeners.add(block) + } + + fun , R : Any> Setting>.onDeselect(block: SafeContext.(R) -> Unit) = apply { + core.deselectListeners.add(block) + } + @SettingEditorDsl @Suppress("unchecked_cast") fun SettingGroupEditor.TypedEditBuilder>.immutableCollection(collection: Collection) { diff --git a/src/main/kotlin/com/lambda/config/settings/collections/ItemCollectionSetting.kt b/src/main/kotlin/com/lambda/config/settings/collections/ItemCollectionSetting.kt index e6f424b3b..8432a8568 100644 --- a/src/main/kotlin/com/lambda/config/settings/collections/ItemCollectionSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/collections/ItemCollectionSetting.kt @@ -20,68 +20,25 @@ package com.lambda.config.settings.collections import com.google.gson.JsonElement import com.google.gson.reflect.TypeToken import com.lambda.Lambda.gson -import com.lambda.config.serializer.ItemCodec +import com.lambda.config.Setting import com.lambda.gui.dsl.ImGuiBuilder -import com.lambda.util.StringUtils.levenshteinDistance -import imgui.ImGuiListClipper -import imgui.flag.ImGuiChildFlags -import imgui.flag.ImGuiSelectableFlags.DontClosePopups import net.minecraft.item.Item class ItemCollectionSetting( - override var name: String, - private val immutableCollection: Collection, - defaultValue: MutableCollection, - description: String, - visibility: () -> Boolean, + immutableCollection: Collection, + defaultValue: MutableCollection ) : CollectionSetting( - name, defaultValue, immutableCollection, - TypeToken.getParameterized(Collection::class.java, Item::class.java).type, - description, - visibility, + TypeToken.getParameterized(Collection::class.java, Item::class.java).type ) { - private var searchFilter = "" - - override fun ImGuiBuilder.buildLayout() { - val text = if (value.size == 1) "item" else "items" - - combo("##$name", "$name: ${value.size} $text") { - inputText("##$name-SearchBox", ::searchFilter) - - child( - strId = "##$name-ComboOptionsChild", - childFlags = ImGuiChildFlags.AutoResizeY or ImGuiChildFlags.AlwaysAutoResize, - ) { - val list = immutableCollection - .filter { searchFilter == "" || searchFilter.levenshteinDistance(ItemCodec.stringify(it)) < 3 } - - ImGuiListClipper.forEach { // not actually iterating - it.begin(list.size) - - while (it.step()) { - for (i in it.displayStart..it.displayEnd) { - val v = list.getOrNull(i) ?: continue - val selected = value.contains(v) - - selectable( - label = ItemCodec.stringify(v), - selected = selected, - flags = DontClosePopups - ) { - if (selected) value.remove(v) - else value.add(v) - } - } - } - } - } - } - } + context(setting: Setting<*, MutableCollection>) + override fun ImGuiBuilder.buildLayout() = buildComboBox("item") + context(setting: Setting<*, MutableCollection>) override fun toJson(): JsonElement = gson.toJsonTree(value, type) + context(setting: Setting<*, MutableCollection>) override fun loadFromJson(serialized: JsonElement) { value = gson.fromJson>(serialized, type) .toMutableList() diff --git a/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt b/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt index cdf49cb6e..3e0fba2ac 100644 --- a/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt @@ -17,7 +17,8 @@ package com.lambda.config.settings.collections -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.gui.dsl.ImGuiBuilder import java.lang.reflect.Type @@ -25,17 +26,12 @@ import java.lang.reflect.Type * @see [com.lambda.config.Configurable] */ class MapSetting( - override var name: String, - defaultValue: MutableMap, - type: Type, - description: String, - visibility: () -> Boolean, -) : AbstractSetting>( - name, - defaultValue, - type, - description, - visibility + defaultValue: MutableMap, + type: Type +) : SettingCore>( + defaultValue, + type ) { - override fun ImGuiBuilder.buildLayout() {} + context(setting: Setting<*, MutableMap>) + override fun ImGuiBuilder.buildLayout() {} } diff --git a/src/main/kotlin/com/lambda/config/settings/comparable/BooleanSetting.kt b/src/main/kotlin/com/lambda/config/settings/comparable/BooleanSetting.kt index 44845c99f..bf08e9c49 100644 --- a/src/main/kotlin/com/lambda/config/settings/comparable/BooleanSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/comparable/BooleanSetting.kt @@ -22,7 +22,8 @@ import com.lambda.brigadier.argument.boolean import com.lambda.brigadier.argument.value import com.lambda.brigadier.execute import com.lambda.brigadier.required -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.extension.CommandBuilder import net.minecraft.command.CommandRegistryAccess @@ -30,27 +31,21 @@ import net.minecraft.command.CommandRegistryAccess /** * @see [com.lambda.config.Configurable] */ -class BooleanSetting( - override var name: String, - defaultValue: Boolean, - description: String, - visibility: () -> Boolean, -) : AbstractSetting( - name, - defaultValue, - TypeToken.get(Boolean::class.java).type, - description, - visibility +class BooleanSetting(defaultValue: Boolean) : SettingCore( + defaultValue, + TypeToken.get(Boolean::class.java).type ) { - override fun ImGuiBuilder.buildLayout() { - checkbox(name, ::value) - lambdaTooltip(description) + context(setting: Setting<*, Boolean>) + override fun ImGuiBuilder.buildLayout() { + checkbox(setting.name, ::value) + lambdaTooltip(setting.description) } + context(setting: Setting<*, Boolean>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { - required(boolean(name)) { parameter -> + required(boolean(setting.name)) { parameter -> execute { - trySetValue(parameter().value()) + setting.trySetValue(parameter().value()) } } } diff --git a/src/main/kotlin/com/lambda/config/settings/comparable/EnumSetting.kt b/src/main/kotlin/com/lambda/config/settings/comparable/EnumSetting.kt index e54080ae1..182f145c4 100644 --- a/src/main/kotlin/com/lambda/config/settings/comparable/EnumSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/comparable/EnumSetting.kt @@ -25,7 +25,8 @@ import com.lambda.brigadier.argument.value import com.lambda.brigadier.argument.word import com.lambda.brigadier.executeWithResult import com.lambda.brigadier.required -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.Describable import com.lambda.util.StringUtils.capitalize @@ -37,32 +38,26 @@ import kotlin.properties.Delegates /** * @see [com.lambda.config.Configurable] */ -class EnumSetting>( - override var name: String, - defaultValue: T, - description: String, - visibility: () -> Boolean, -) : AbstractSetting( - name, - defaultValue, - TypeToken.get(defaultValue.declaringJavaClass).type, - description, - visibility, +class EnumSetting>(defaultValue: T) : SettingCore( + defaultValue, + TypeToken.get(defaultValue.declaringJavaClass).type ) { var index by Delegates.observable(value.ordinal) { _, _, to -> value = value.enumValues[to % value.enumValues.size] } + context(setting: Setting<*, T>) override fun loadFromJson(serialized: JsonElement) { super.loadFromJson(serialized) index = value.ordinal // super bug fix for imgui } + context(setting: Setting<*, T>) override fun ImGuiBuilder.buildLayout() { val values = value.enumValues val currentDisplay = value.displayValue - combo("##$name", preview = "$name: $currentDisplay") { + combo("##${setting.name}", preview = "${setting.name}: $currentDisplay") { values.forEachIndexed { idx, v -> val isSelected = idx == index @@ -74,11 +69,12 @@ class EnumSetting>( } } - lambdaTooltip(description) + lambdaTooltip(setting.description) } + context(setting: Setting<*, T>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { - required(word(name)) { parameter -> + required(word(setting.name)) { parameter -> suggests { _, builder -> value.enumValues.forEach { builder.suggest(it.name.capitalize()) } builder.buildFuture() @@ -86,7 +82,7 @@ class EnumSetting>( executeWithResult { val newValue = value.enumValues.find { it.name.equals(parameter().value(), true) } ?: return@executeWithResult failure("Invalid value") - trySetValue(newValue) + setting.trySetValue(newValue) return@executeWithResult success() } } diff --git a/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt b/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt index 35c7b1e6c..907b74d96 100644 --- a/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt @@ -22,7 +22,8 @@ import com.lambda.brigadier.argument.integer import com.lambda.brigadier.argument.value import com.lambda.brigadier.execute import com.lambda.brigadier.required -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.BlockUtils.blockPos import com.lambda.util.extension.CommandBuilder @@ -32,29 +33,23 @@ import net.minecraft.util.math.BlockPos /** * @see [com.lambda.config.Configurable] */ -class BlockPosSetting( - override var name: String, - defaultValue: BlockPos, - description: String, - visibility: () -> Boolean, -) : AbstractSetting( - name, - defaultValue, - TypeToken.get(BlockPos::class.java).type, - description, - visibility +class BlockPosSetting(defaultValue: BlockPos) : SettingCore( + defaultValue, + TypeToken.get(BlockPos::class.java).type ) { + context(setting: Setting<*, BlockPos>) override fun ImGuiBuilder.buildLayout() { - inputVec3i(name, value) { value = it.blockPos } - lambdaTooltip(description) + inputVec3i(setting.name, value) { value = it.blockPos } + lambdaTooltip(setting.description) } + context(setting: Setting<*, BlockPos>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { required(integer("X", -30000000, 30000000)) { x -> required(integer("Y", -64, 255)) { y -> required(integer("Z", -30000000, 30000000)) { z -> execute { - trySetValue(BlockPos(x().value(), y().value(), z().value())) + setting.trySetValue(BlockPos(x().value(), y().value(), z().value())) } } } diff --git a/src/main/kotlin/com/lambda/config/settings/complex/BlockSetting.kt b/src/main/kotlin/com/lambda/config/settings/complex/BlockSetting.kt index 7833fe4cd..87ca679f2 100644 --- a/src/main/kotlin/com/lambda/config/settings/complex/BlockSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/complex/BlockSetting.kt @@ -22,7 +22,8 @@ import com.lambda.brigadier.argument.blockState import com.lambda.brigadier.argument.value import com.lambda.brigadier.execute import com.lambda.brigadier.required -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.extension.CommandBuilder import net.minecraft.block.Block @@ -31,24 +32,18 @@ import net.minecraft.command.CommandRegistryAccess /** * @see [com.lambda.config.Configurable] */ -class BlockSetting( - override var name: String, - defaultValue: Block, - description: String, - visibility: () -> Boolean, -) : AbstractSetting( - name, - defaultValue, - TypeToken.get(Block::class.java).type, - description, - visibility +class BlockSetting(defaultValue: Block) : SettingCore( + defaultValue, + TypeToken.get(Block::class.java).type ) { + context(setting: Setting<*, Block>) override fun ImGuiBuilder.buildLayout() {} + context(setting: Setting<*, Block>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { - required(blockState(name, registry)) { argument -> + required(blockState(setting.name, registry)) { argument -> execute { - trySetValue(argument().value().blockState.block) + setting.trySetValue(argument().value().blockState.block) } } } diff --git a/src/main/kotlin/com/lambda/config/settings/complex/ColorSetting.kt b/src/main/kotlin/com/lambda/config/settings/complex/ColorSetting.kt index 553b5eb72..1621da90e 100644 --- a/src/main/kotlin/com/lambda/config/settings/complex/ColorSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/complex/ColorSetting.kt @@ -23,7 +23,8 @@ import com.lambda.brigadier.argument.value import com.lambda.brigadier.execute import com.lambda.brigadier.optional import com.lambda.brigadier.required -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.extension.CommandBuilder import net.minecraft.command.CommandRegistryAccess @@ -32,23 +33,17 @@ import java.awt.Color /** * @see [com.lambda.config.Configurable] */ -class ColorSetting( - override var name: String, - defaultValue: Color, - description: String, - visibility: () -> Boolean, -) : AbstractSetting( - name, - defaultValue, - TypeToken.get(Color::class.java).type, - description, - visibility +class ColorSetting(defaultValue: Color) : SettingCore( + defaultValue, + TypeToken.get(Color::class.java).type ) { - override fun ImGuiBuilder.buildLayout() { - colorEdit(name, ::value) - lambdaTooltip(description) + context(setting: Setting<*, Color>) + override fun ImGuiBuilder.buildLayout() { + colorEdit(setting.name, ::value) + lambdaTooltip(setting.description) } + context(setting: Setting<*, Color>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { required(integer("Red", 0, 255)) { red -> required(integer("Green", 0, 255)) { green -> @@ -56,7 +51,7 @@ class ColorSetting( optional(integer("Alpha", 0, 255)) { alpha -> execute { val alphaValue = alpha?.let { it().value() } ?: 255 - trySetValue(Color(red().value(), green().value(), blue().value(), alphaValue)) + setting.trySetValue(Color(red().value(), green().value(), blue().value(), alphaValue)) } } } diff --git a/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt b/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt index e4639ae2a..eee1f5546 100644 --- a/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt @@ -26,7 +26,8 @@ import com.lambda.brigadier.argument.word import com.lambda.brigadier.executeWithResult import com.lambda.brigadier.optional import com.lambda.brigadier.required -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.InputUtils import com.lambda.util.KeyCode @@ -47,25 +48,17 @@ import org.lwjgl.glfw.GLFW.GLFW_MOD_NUM_LOCK import org.lwjgl.glfw.GLFW.GLFW_MOD_SHIFT import org.lwjgl.glfw.GLFW.GLFW_MOD_SUPER -class KeybindSetting( - override var name: String, - defaultValue: Bind, - description: String, - visibility: () -> Boolean, -) : AbstractSetting( - name, - defaultValue, - TypeToken.get(Bind::class.java).type, - description, - visibility +class KeybindSettingCore(defaultValue: Bind) : SettingCore( + defaultValue, + TypeToken.get(Bind::class.java).type ) { - constructor(name: String, defaultValue: KeyCode, description: String, visibility: () -> Boolean) - : this(name, Bind(defaultValue.code, 0, -1), description, visibility) + constructor(defaultValue: KeyCode) : this(Bind(defaultValue.code, 0, -1)) private var listening = false + context(setting: Setting<*, Bind>) override fun ImGuiBuilder.buildLayout() { - text(name) + text(setting.name) sameLine() val bind = value @@ -86,7 +79,7 @@ class KeybindSetting( } lambdaTooltip { - if (!listening) description.ifBlank { "Click to set. Esc cancels. Backspace/Delete unbinds." } + if (!listening) setting.description.ifBlank { "Click to set. Esc cancels. Backspace/Delete unbinds." } else "Listening… Press a key to bind. Esc to cancel. Backspace/Delete to unbind." } @@ -131,8 +124,9 @@ class KeybindSetting( } } + context(setting: Setting<*, Bind>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { - required(word(name)) { name -> + required(word(setting.name)) { name -> suggests { _, builder -> KeyCode.entries.forEach { builder.suggest(it.name.capitalize()) } (1..10).forEach { builder.suggest(it) } @@ -157,7 +151,7 @@ class KeybindSetting( } } - trySetValue(bind) + setting.trySetValue(bind) return@executeWithResult success() } } diff --git a/src/main/kotlin/com/lambda/config/settings/complex/Vec3dSetting.kt b/src/main/kotlin/com/lambda/config/settings/complex/Vec3dSetting.kt index fd33a860a..bc7224b4a 100644 --- a/src/main/kotlin/com/lambda/config/settings/complex/Vec3dSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/complex/Vec3dSetting.kt @@ -22,35 +22,30 @@ import com.lambda.brigadier.argument.double import com.lambda.brigadier.argument.value import com.lambda.brigadier.execute import com.lambda.brigadier.required -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting +import com.lambda.config.SettingCore import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.extension.CommandBuilder import net.minecraft.command.CommandRegistryAccess import net.minecraft.util.math.Vec3d -class Vec3dSetting( - override var name: String, - defaultValue: Vec3d, - description: String, - visibility: () -> Boolean, -) : AbstractSetting( - name, - defaultValue, - TypeToken.get(Vec3d::class.java).type, - description, - visibility +class Vec3dSetting(defaultValue: Vec3d) : SettingCore( + defaultValue, + TypeToken.get(Vec3d::class.java).type ) { - override fun ImGuiBuilder.buildLayout() { - inputVec3d(name, ::value as Vec3d) // FixMe: what the fuck - lambdaTooltip(description) + context(setting: Setting<*, Vec3d>) + override fun ImGuiBuilder.buildLayout() { + inputVec3d(setting.name, ::value as Vec3d) // FixMe: what the fuck + lambdaTooltip(setting.description) } + context(setting: Setting<*, Vec3d>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { required(double("X", -30000000.0, 30000000.0)) { x -> required(double("Y", -64.0, 255.0)) { y -> required(double("Z", -30000000.0, 30000000.0)) { z -> execute { - trySetValue(Vec3d(x().value(), y().value(), z().value())) + setting.trySetValue(Vec3d(x().value(), y().value(), z().value())) } } } diff --git a/src/main/kotlin/com/lambda/config/settings/numeric/DoubleSetting.kt b/src/main/kotlin/com/lambda/config/settings/numeric/DoubleSetting.kt index 0856ee2f1..e6577b751 100644 --- a/src/main/kotlin/com/lambda/config/settings/numeric/DoubleSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/numeric/DoubleSetting.kt @@ -22,6 +22,7 @@ import com.lambda.brigadier.argument.double import com.lambda.brigadier.argument.value import com.lambda.brigadier.execute import com.lambda.brigadier.required +import com.lambda.config.Setting import com.lambda.config.settings.NumericSetting import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.extension.CommandBuilder @@ -32,21 +33,15 @@ import kotlin.math.roundToInt * @see [com.lambda.config.Configurable] */ class DoubleSetting( - override var name: String, defaultValue: Double, override var range: ClosedRange, override var step: Double, - description: String, - unit: String, - visibility: () -> Boolean + unit: String ) : NumericSetting( - name, defaultValue, range, step, - description, - unit, - visibility + unit ) { private var valueIndex: Int get() = ((value - range.start) / step).roundToInt() @@ -54,15 +49,17 @@ class DoubleSetting( value = (range.start + index * step).coerceIn(range) } + context(setting: Setting<*, Double>) override fun ImGuiBuilder.buildSlider() { val maxIndex = ((range.endInclusive - range.start) / step).toInt() - slider("##$name", ::valueIndex, 0, maxIndex, "") + slider("##${setting.name}", ::valueIndex, 0, maxIndex, "") } + context(setting: Setting<*, Double>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { - required(double(name, range.start, range.endInclusive)) { parameter -> + required(double(setting.name, range.start, range.endInclusive)) { parameter -> execute { - trySetValue(parameter().value()) + setting.trySetValue(parameter().value()) } } } diff --git a/src/main/kotlin/com/lambda/config/settings/numeric/FloatSetting.kt b/src/main/kotlin/com/lambda/config/settings/numeric/FloatSetting.kt index ad3e5a830..469655a71 100644 --- a/src/main/kotlin/com/lambda/config/settings/numeric/FloatSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/numeric/FloatSetting.kt @@ -21,6 +21,7 @@ import com.lambda.brigadier.argument.float import com.lambda.brigadier.argument.value import com.lambda.brigadier.execute import com.lambda.brigadier.required +import com.lambda.config.Setting import com.lambda.config.settings.NumericSetting import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.extension.CommandBuilder @@ -31,21 +32,15 @@ import kotlin.math.roundToInt * @see [com.lambda.config.Configurable] */ class FloatSetting( - override var name: String, defaultValue: Float, override var range: ClosedRange, override var step: Float = 1f, - description: String, unit: String, - visibility: () -> Boolean ) : NumericSetting( - name, defaultValue, range, step, - description, - unit, - visibility + unit ) { private var valueIndex: Int get() = ((value - range.start) / step).roundToInt() @@ -53,15 +48,17 @@ class FloatSetting( value = (range.start + index * step).coerceIn(range) } + context(setting: Setting<*, Float>) override fun ImGuiBuilder.buildSlider() { val maxIndex = ((range.endInclusive - range.start) / step).toInt() - slider("##$name", ::valueIndex, 0, maxIndex, "") + slider("##${setting.name}", ::valueIndex, 0, maxIndex, "") } + context(setting: Setting<*, Float>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { - required(float(name, range.start, range.endInclusive)) { parameter -> + required(float(setting.name, range.start, range.endInclusive)) { parameter -> execute { - trySetValue(parameter().value()) + setting.trySetValue(parameter().value()) } } } diff --git a/src/main/kotlin/com/lambda/config/settings/numeric/IntegerSetting.kt b/src/main/kotlin/com/lambda/config/settings/numeric/IntegerSetting.kt index 7eb0eb056..2df3b50ad 100644 --- a/src/main/kotlin/com/lambda/config/settings/numeric/IntegerSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/numeric/IntegerSetting.kt @@ -21,6 +21,7 @@ import com.lambda.brigadier.argument.integer import com.lambda.brigadier.argument.value import com.lambda.brigadier.execute import com.lambda.brigadier.required +import com.lambda.config.Setting import com.lambda.config.settings.NumericSetting import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.extension.CommandBuilder @@ -30,30 +31,26 @@ import net.minecraft.command.CommandRegistryAccess * @see [com.lambda.config.Configurable] */ class IntegerSetting( - override var name: String, defaultValue: Int, override var range: ClosedRange, override var step: Int = 1, - description: String, - unit: String, - visibility: () -> Boolean + unit: String ) : NumericSetting( - name, defaultValue, range, step, - description, - unit, - visibility + unit ) { + context(setting: Setting<*, Int>) override fun ImGuiBuilder.buildSlider() { - slider("##$name", ::value, range.start, range.endInclusive, "") + slider("##${setting.name}", ::value, range.start, range.endInclusive, "") } + context(setting: Setting<*, Int>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { - required(integer(name, range.start, range.endInclusive)) { parameter -> + required(integer(setting.name, range.start, range.endInclusive)) { parameter -> execute { - trySetValue(parameter().value()) + setting.trySetValue(parameter().value()) } } } diff --git a/src/main/kotlin/com/lambda/config/settings/numeric/LongSetting.kt b/src/main/kotlin/com/lambda/config/settings/numeric/LongSetting.kt index 00a2d8d61..2198a0f23 100644 --- a/src/main/kotlin/com/lambda/config/settings/numeric/LongSetting.kt +++ b/src/main/kotlin/com/lambda/config/settings/numeric/LongSetting.kt @@ -21,6 +21,7 @@ import com.lambda.brigadier.argument.long import com.lambda.brigadier.argument.value import com.lambda.brigadier.execute import com.lambda.brigadier.required +import com.lambda.config.Setting import com.lambda.config.settings.NumericSetting import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.util.extension.CommandBuilder @@ -30,21 +31,15 @@ import net.minecraft.command.CommandRegistryAccess * @see [com.lambda.config.Configurable] */ class LongSetting( - override var name: String, defaultValue: Long, override var range: ClosedRange, override var step: Long = 1, - description: String, - unit: String, - visibility: () -> Boolean + unit: String ) : NumericSetting( - name, defaultValue, range, step, - description, - unit, - visibility + unit ) { // ToDo: No worky for super large numbers private var valueIndex: Int @@ -53,15 +48,17 @@ class LongSetting( value = (range.start + index * step).coerceIn(range) } + context(setting: Setting<*, Long>) override fun ImGuiBuilder.buildSlider() { val maxIndex = ((range.endInclusive - range.start) / step).toInt() - slider("##$name", ::valueIndex, 0, maxIndex, "") + slider("##${setting.name}", ::valueIndex, 0, maxIndex, "") } + context(setting: Setting<*, Long>) override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { - required(long(name, range.start, range.endInclusive)) { parameter -> + required(long(setting.name, range.start, range.endInclusive)) { parameter -> execute { - trySetValue(parameter().value()) + setting.trySetValue(parameter().value()) } } } diff --git a/src/main/kotlin/com/lambda/gui/MenuBar.kt b/src/main/kotlin/com/lambda/gui/MenuBar.kt index d636bae70..4cf3ef7f9 100644 --- a/src/main/kotlin/com/lambda/gui/MenuBar.kt +++ b/src/main/kotlin/com/lambda/gui/MenuBar.kt @@ -75,7 +75,7 @@ object MenuBar { menu("HUD") { buildHudMenu() } menu("GUI") { buildGuiMenu() } menu("Modules") { buildModulesMenu() } - menu("Automation Configs") { buildConfigPresetsMenu() } + menu("Automation Configs") { buildAutomationConfigsMenu() } menu("Minecraft") { buildMinecraftMenu() } menu("Help") { buildHelpMenu() } buildGitHubReference() @@ -284,7 +284,7 @@ object MenuBar { } } - private fun ImGuiBuilder.buildConfigPresetsMenu() { + private fun ImGuiBuilder.buildAutomationConfigsMenu() { button("New Config") { ImGui.openPopup("##new-config") } popupContextWindow("##new-config") { diff --git a/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt b/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt index f5544aa56..0868117cc 100644 --- a/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt +++ b/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt @@ -20,9 +20,9 @@ package com.lambda.gui.components import com.lambda.Lambda.mc import com.lambda.command.CommandRegistry import com.lambda.command.LambdaCommand -import com.lambda.config.AbstractSetting import com.lambda.config.Configurable import com.lambda.config.Configuration +import com.lambda.config.Setting import com.lambda.event.events.KeyboardEvent import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.gui.LambdaScreen @@ -100,7 +100,7 @@ object QuickSearch { } } - private class SettingResult(val setting: AbstractSetting<*>, val configurable: Configurable) : SearchResult { + private class SettingResult(val setting: Setting<*, *>, val configurable: Configurable) : SearchResult { override val breadcrumb: String by lazy { buildSettingBreadcrumb(configurable.name, setting) } override fun ImGuiBuilder.buildLayout() { @@ -295,7 +295,7 @@ object QuickSearch { } } - private fun buildSettingBreadcrumb(configurableName: String, setting: AbstractSetting<*>): String { + private fun buildSettingBreadcrumb(configurableName: String, setting: Setting<*, *>): String { val group = setting.groups .minByOrNull { it.size } ?.joinToString(" » ") { it.displayName } diff --git a/src/main/kotlin/com/lambda/gui/components/SettingsWidget.kt b/src/main/kotlin/com/lambda/gui/components/SettingsWidget.kt index bc016631c..103b8baa4 100644 --- a/src/main/kotlin/com/lambda/gui/components/SettingsWidget.kt +++ b/src/main/kotlin/com/lambda/gui/components/SettingsWidget.kt @@ -17,10 +17,10 @@ package com.lambda.gui.components -import com.lambda.config.AbstractSetting import com.lambda.config.AutomationConfig import com.lambda.config.Configurable import com.lambda.config.MutableAutomationConfig +import com.lambda.config.Setting import com.lambda.config.UserAutomationConfig import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.module.Module @@ -33,7 +33,7 @@ object SettingsWidget { /** * Builds the settings context popup content for a given configurable. */ - fun ImGuiBuilder.buildConfigSettingsContext(config: Configurable, hiddenSettings: Set> = emptySet()) { + fun ImGuiBuilder.buildConfigSettingsContext(config: Configurable) { group { if (config is Module) { with(config.keybindSetting) { buildLayout() } @@ -49,39 +49,40 @@ object SettingsWidget { } ImGui.setNextWindowSizeConstraints(0f, 0f, Float.MAX_VALUE, io.displaySize.y * 0.5f) popupContextItem("##automation-config-popup-${config.name}", ImGuiPopupFlags.None) { - buildConfigSettingsContext(config.automationConfig, config.defaultAutomationConfig.hiddenSettings) + buildConfigSettingsContext(config.automationConfig) } - if (config.automationConfig !== config.defaultAutomationConfig) { + if (config.backingAutomationConfig !== config.defaultAutomationConfig) { sameLine() - text("(${config.automationConfig.name})") + text("(${config.backingAutomationConfig.name})") } } } - separator() val toIgnoreSettings = when (config) { is Module -> setOf(config.keybindSetting, config.disableOnReleaseSetting) is UserAutomationConfig -> setOf(config.linkedModules) else -> emptySet() } - val visibleSettings = config.settings.filter { it.visibility() } - toIgnoreSettings - hiddenSettings + val visibleSettings = config.settings.filter { it.visibility() } - toIgnoreSettings + if (visibleSettings.isEmpty()) return + else separator() val (grouped, ungrouped) = visibleSettings.partition { it.groups.isNotEmpty() } - ungrouped.forEach { + ungrouped.forEach { it.withDisabled { buildLayout() } } renderGroup(grouped, emptyList(), config) } - private fun AbstractSetting<*>.withDisabled(block: AbstractSetting<*>.() -> Unit) { + private fun Setting<*, *>.withDisabled(block: Setting<*, *>.() -> Unit) { if (disabled()) ImGui.beginDisabled() block() if (disabled()) ImGui.endDisabled() } private fun ImGuiBuilder.renderGroup( - settings: List>, - parentPath: List, - config: Configurable + settings: List>, + parentPath: List, + config: Configurable ) { settings.filter { it.groups.contains(parentPath) }.forEach { it.withDisabled { buildLayout() } diff --git a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt index 75cca282f..9e48d60ad 100644 --- a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt +++ b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt @@ -17,8 +17,8 @@ package com.lambda.interaction.request.rotating -import com.lambda.config.AbstractSetting import com.lambda.config.ISettingGroup +import com.lambda.config.Setting import com.lambda.event.events.TickEvent interface RotationConfig : ISettingGroup { @@ -50,7 +50,7 @@ interface RotationConfig : ISettingGroup { val rotate: Boolean get() = rotationMode != RotationMode.None open class Instant(mode: RotationMode) : RotationConfig { - override val settings = mutableListOf>() + override val settings = mutableListOf>() override val rotationMode = mode override val keepTicks = 1 override val decayTicks = 1 diff --git a/src/main/kotlin/com/lambda/module/Module.kt b/src/main/kotlin/com/lambda/module/Module.kt index 2c6dd6625..5b6721d66 100644 --- a/src/main/kotlin/com/lambda/module/Module.kt +++ b/src/main/kotlin/com/lambda/module/Module.kt @@ -19,11 +19,11 @@ package com.lambda.module import com.lambda.Lambda import com.lambda.command.LambdaCommand -import com.lambda.config.AbstractSetting -import com.lambda.config.AutomationConfig import com.lambda.config.Configurable import com.lambda.config.Configuration import com.lambda.config.MutableAutomationConfig +import com.lambda.config.MutableAutomationConfigImpl +import com.lambda.config.SettingCore import com.lambda.config.configurations.ModuleConfigs import com.lambda.config.settings.complex.Bind import com.lambda.context.SafeContext @@ -59,7 +59,7 @@ import com.lambda.util.Nameable * If a module does not need to be activated by a key (like [ClickGui]), * the default [keybind] should not be set (using [KeyCode.Unbound]). * - * [Module]s are [Configurable]s with [settings] (see [AbstractSetting] for all setting types). + * [Module]s are [Configurable]s with [settings] (see [SettingCore] for all setting types). * Example: * ``` * private val foo by setting("Foo", true) @@ -120,13 +120,7 @@ abstract class Module( enabledByDefault: Boolean = false, defaultKeybind: Bind = Bind.EMPTY, autoDisable: Boolean = false -) : Nameable, Muteable, Configurable(ModuleConfigs), MutableAutomationConfig { - final override var defaultAutomationConfig: AutomationConfig = AutomationConfig.Companion.DEFAULT - set(value) { - field = value - automationConfig = value - } - final override var automationConfig = defaultAutomationConfig +) : Nameable, Muteable, Configurable(ModuleConfigs), MutableAutomationConfig by MutableAutomationConfigImpl() { private val isEnabledSetting = setting("Enabled", enabledByDefault) { false } val keybindSetting = setting("Keybind", defaultKeybind) { false } val disableOnReleaseSetting = setting("Disable On Release", false) { false } diff --git a/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 677bd3443..b63f5871f 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -99,6 +99,7 @@ object PacketMine : Module( init { setDefaultAutomationConfig { applyEdits { + hideAllGroupsExcept(breakConfig, rotationConfig, hotbarConfig) breakConfig.apply { editTyped( ::avoidLiquids, @@ -111,7 +112,6 @@ object PacketMine : Module( hotbarConfig.apply { ::keepTicks.edit { defaultValue(0) } } - hideGroups(buildConfig, placeConfig, interactConfig, inventoryConfig, eatConfig) } } diff --git a/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index c6cb41e19..282c6fcd1 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -18,7 +18,8 @@ package com.lambda.module.modules.render import com.lambda.Lambda.mc -import com.lambda.config.serializer.BlockCodec +import com.lambda.config.settings.collections.CollectionSetting.Companion.onDeselect +import com.lambda.config.settings.collections.CollectionSetting.Companion.onSelect import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.ChunkedESP.Companion.newChunkedESP import com.lambda.graphics.renderer.esp.DirectionMask diff --git a/src/main/kotlin/com/lambda/util/Formatting.kt b/src/main/kotlin/com/lambda/util/Formatting.kt index 4217f551d..bd37d3e3e 100644 --- a/src/main/kotlin/com/lambda/util/Formatting.kt +++ b/src/main/kotlin/com/lambda/util/Formatting.kt @@ -17,7 +17,7 @@ package com.lambda.util -import com.lambda.config.AbstractSetting +import com.lambda.config.Setting import com.lambda.config.groups.FormatterConfig import com.lambda.util.math.Vec2d import net.minecraft.util.math.Vec2f @@ -124,7 +124,7 @@ object Formatting { } object Default : FormatterConfig { - override val settings = mutableListOf>() + override val settings = mutableListOf>() override val locale: Locale = Locale.US override val separator: String = "," override val prefix: String = "("