Skip to content

Commit e8a5c6f

Browse files
authored
Feature: User created automation configs with module linking (166)
1 parent eccfd4f commit e8a5c6f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+940
-723
lines changed

src/main/kotlin/com/lambda/command/commands/BuildCommand.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import com.lambda.brigadier.argument.value
2525
import com.lambda.brigadier.executeWithResult
2626
import com.lambda.brigadier.required
2727
import com.lambda.command.LambdaCommand
28-
import com.lambda.context.AutomationConfig
28+
import com.lambda.config.AutomationConfig
2929
import com.lambda.interaction.construction.StructureRegistry
3030
import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure
3131
import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint
@@ -62,7 +62,7 @@ object BuildCommand : LambdaCommand(
6262
.loadStructureByRelativePath(Path.of(pathString))
6363
.let { template ->
6464
info("Building structure $pathString with dimensions ${template.size.toShortString()} created by ${template.author}")
65-
lastBuildTask = with(AutomationConfig) {
65+
lastBuildTask = with(AutomationConfig.Companion.DEFAULT) {
6666
template.toStructure()
6767
.move(player.blockPos)
6868
.toBlueprint()

src/main/kotlin/com/lambda/command/commands/ModuleCommand.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import com.lambda.threading.runSafe
3232
import com.lambda.util.Communication.info
3333
import com.lambda.util.Communication.joinToText
3434
import com.lambda.util.Communication.warn
35-
import com.lambda.util.StringUtils
3635
import com.lambda.util.StringUtils.findSimilarStrings
3736
import com.lambda.util.extension.CommandBuilder
3837
import com.lambda.util.text.ClickEvents.suggestCommand
@@ -76,7 +75,7 @@ object ModuleCommand : LambdaCommand(
7675

7776
required(string("module name")) { moduleName ->
7877
suggests { _, builder ->
79-
ModuleRegistry.moduleNames.forEach {
78+
ModuleRegistry.moduleNameMap.keys.forEach {
8079
builder.suggest(it)
8180
}
8281
builder.buildFuture()
@@ -95,7 +94,7 @@ object ModuleCommand : LambdaCommand(
9594
literal("not found!")
9695
}
9796
val similarModules = name.findSimilarStrings(
98-
ModuleRegistry.moduleNames,
97+
ModuleRegistry.moduleNameMap.keys,
9998
3
10099
)
101100
if (similarModules.isEmpty()) return@buildText

src/main/kotlin/com/lambda/command/commands/TransferCommand.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import com.lambda.brigadier.argument.value
2727
import com.lambda.brigadier.executeWithResult
2828
import com.lambda.brigadier.required
2929
import com.lambda.command.LambdaCommand
30-
import com.lambda.context.AutomationConfig
30+
import com.lambda.config.AutomationConfig
3131
import com.lambda.interaction.material.StackSelection.Companion.selectStack
3232
import com.lambda.interaction.material.container.ContainerManager
3333
import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial
@@ -53,7 +53,7 @@ object TransferCommand : LambdaCommand(
5353
val selection = selectStack(count) {
5454
isItem(stack(ctx).value().item)
5555
}
56-
with(AutomationConfig) {
56+
with(AutomationConfig.Companion.DEFAULT) {
5757
selection.containerWithMaterial().forEachIndexed { i, container ->
5858
builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection))
5959
}
@@ -65,7 +65,7 @@ object TransferCommand : LambdaCommand(
6565
val selection = selectStack(amount(ctx).value()) {
6666
isItem(stack(ctx).value().item)
6767
}
68-
with(AutomationConfig) {
68+
with(AutomationConfig.Companion.DEFAULT) {
6969
containerWithSpace(selection).forEachIndexed { i, container ->
7070
builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection))
7171
}
@@ -84,7 +84,7 @@ object TransferCommand : LambdaCommand(
8484
it.name == to().value().split(".").last().trim()
8585
} ?: return@executeWithResult failure("To container not found")
8686

87-
with(AutomationConfig) {
87+
with(AutomationConfig.Companion.DEFAULT) {
8888
when (val transaction = fromContainer.transfer(selection, toContainer)) {
8989
is TransferResult.ContainerTransfer -> {
9090
info("${transaction.name} started.")

src/main/kotlin/com/lambda/config/AbstractSetting.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ abstract class AbstractSetting<T : Any>(
102102
var visibility: () -> Boolean,
103103
) : Jsonable, Nameable, Describable, Layout {
104104
private val listeners = mutableListOf<ValueListener<T>>()
105+
var disabled = { false }
105106
var groups: MutableList<List<NamedEnum>> = mutableListOf()
106107

107108
var value by Delegates.observable(defaultValue) { _, from, to ->
@@ -152,6 +153,10 @@ abstract class AbstractSetting<T : Any>(
152153
listeners.add(ValueListener(false, block))
153154
}
154155

156+
fun disabled(predicate: () -> Boolean) = apply {
157+
disabled = predicate
158+
}
159+
155160
fun group(path: List<NamedEnum>, vararg continuation: NamedEnum) = apply {
156161
groups.add(path + continuation)
157162
}
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.config
19+
20+
import com.lambda.config.configurations.AutomationConfigs
21+
import com.lambda.config.groups.BreakSettings
22+
import com.lambda.config.groups.BuildSettings
23+
import com.lambda.config.groups.EatSettings
24+
import com.lambda.config.groups.HotbarSettings
25+
import com.lambda.config.groups.InteractSettings
26+
import com.lambda.config.groups.InventorySettings
27+
import com.lambda.config.groups.PlaceSettings
28+
import com.lambda.config.groups.RotationSettings
29+
import com.lambda.context.Automated
30+
import com.lambda.event.events.onStaticRender
31+
import com.lambda.interaction.construction.result.Drawable
32+
import com.lambda.module.Module
33+
import com.lambda.util.NamedEnum
34+
import kotlin.reflect.KProperty0
35+
import kotlin.reflect.jvm.isAccessible
36+
37+
@Suppress("unchecked_cast", "unused")
38+
open class AutomationConfig(
39+
override val name: String,
40+
configuration: Configuration = AutomationConfigs
41+
) : Configurable(configuration), Automated {
42+
enum class Group(override val displayName: String) : NamedEnum {
43+
Build("Build"),
44+
Break("Break"),
45+
Place("Place"),
46+
Interact("Interact"),
47+
Rotation("Rotation"),
48+
Interaction("Interaction"),
49+
Inventory("Inventory"),
50+
Hotbar("Hotbar"),
51+
Eat("Eat"),
52+
Render("Render"),
53+
Debug("Debug")
54+
}
55+
56+
override val buildConfig = BuildSettings(this, Group.Build)
57+
override val breakConfig = BreakSettings(this, Group.Break)
58+
override val placeConfig = PlaceSettings(this, Group.Place)
59+
override val interactConfig = InteractSettings(this, Group.Interact)
60+
override val rotationConfig = RotationSettings(this, Group.Rotation)
61+
override val inventoryConfig = InventorySettings(this, Group.Inventory)
62+
override val hotbarConfig = HotbarSettings(this, Group.Hotbar)
63+
override val eatConfig = EatSettings(this, Group.Eat)
64+
65+
val hiddenSettings = mutableSetOf<AbstractSetting<*>>()
66+
67+
companion object {
68+
context(module: Module)
69+
fun automationConfig(name: String = module.name, edits: (AutomationConfig.() -> Unit)? = null): AutomationConfig =
70+
AutomationConfig("Default $name Automation Config").apply { edits?.invoke(this) }
71+
72+
fun automationConfig(name: String, edits: (AutomationConfig.() -> Unit)? = null): AutomationConfig =
73+
AutomationConfig("Default $name Automation Config").apply { edits?.invoke(this) }
74+
75+
object DEFAULT : AutomationConfig("Default Automation Config") {
76+
val renders by setting("Render", false).group(Group.Render)
77+
val avoidDesync by setting("Avoid Desync", true, "Cancels incoming inventory update packets if they match previous actions").group(Group.Debug)
78+
val desyncTimeout by setting("Desync Timeout", 30, 1..30, 1, unit = " ticks", description = "Time to store previous inventory actions before dropping the cache") { avoidDesync }.group(Group.Debug)
79+
val showAllEntries by setting("Show All Entries", false, "Show all entries in the task tree").group(Group.Debug)
80+
val shrinkFactor by setting("Shrink Factor", 0.001, 0.0..1.0, 0.001).group(Group.Debug)
81+
val ignoreItemDropWarnings by setting("Ignore Drop Warnings", false, "Hides the item drop warnings from the break manager").group(Group.Debug)
82+
val maxSimDependencies by setting("Max Sim Dependencies", 3, 0..10, 1, "Maximum dependency build results").group(Group.Debug)
83+
84+
@Volatile
85+
var drawables = listOf<Drawable>()
86+
87+
init {
88+
onStaticRender {
89+
if (renders)
90+
with(it) { drawables.forEach { with(it) { buildRenderer() } } }
91+
}
92+
}
93+
}
94+
}
95+
96+
@DslMarker
97+
annotation class SettingEditorDsl
98+
99+
private val KProperty0<*>.delegate
100+
get() = try {
101+
apply { isAccessible = true }.getDelegate()
102+
} catch (e: Exception) {
103+
throw IllegalStateException("Could not access delegate for property $name", e)
104+
}
105+
106+
@SettingEditorDsl
107+
internal inline fun <T : Any> KProperty0<T>.edit(edits: TypedEditBuilder<T>.(AbstractSetting<T>) -> Unit) {
108+
val setting = delegate as? AbstractSetting<T> ?: throw IllegalStateException("Setting delegate did not match current value's type")
109+
TypedEditBuilder(this@AutomationConfig, listOf(setting)).edits(setting)
110+
}
111+
112+
@SettingEditorDsl
113+
internal inline fun <T : Any, R : Any> KProperty0<T>.editWith(
114+
other: KProperty0<R>,
115+
edits: TypedEditBuilder<T>.(AbstractSetting<R>) -> Unit
116+
) {
117+
val setting = delegate as? AbstractSetting<T> ?: throw IllegalStateException("Setting delegate did not match current value's type")
118+
TypedEditBuilder(this@AutomationConfig, listOf(setting)).edits(other.delegate as AbstractSetting<R>)
119+
}
120+
121+
@SettingEditorDsl
122+
fun edit(
123+
vararg settings: KProperty0<*>,
124+
edits: BasicEditBuilder.() -> Unit
125+
) { BasicEditBuilder(this@AutomationConfig, settings.map { it.delegate } as List<AbstractSetting<*>>).apply(edits) }
126+
127+
@SettingEditorDsl
128+
internal inline fun <T : Any> editWith(
129+
vararg settings: KProperty0<*>,
130+
other: KProperty0<T>,
131+
edits: BasicEditBuilder.(AbstractSetting<T>) -> Unit
132+
) { BasicEditBuilder(this@AutomationConfig, settings.map { it.delegate } as List<AbstractSetting<*>>).edits(other.delegate as AbstractSetting<T>) }
133+
134+
@SettingEditorDsl
135+
internal inline fun <T : Any> editTyped(
136+
vararg settings: KProperty0<T>,
137+
edits: TypedEditBuilder<T>.() -> Unit
138+
) { TypedEditBuilder(this@AutomationConfig, settings.map { it.delegate } as List<AbstractSetting<T>>).apply(edits) }
139+
140+
@SettingEditorDsl
141+
internal inline fun <T : Any, R : Any> editTypedWith(
142+
vararg settings: KProperty0<T>,
143+
other: KProperty0<R>,
144+
edits: TypedEditBuilder<T>.(AbstractSetting<R>) -> Unit
145+
) = TypedEditBuilder(this@AutomationConfig, settings.map { it.delegate } as List<AbstractSetting<T>>).edits(other.delegate as AbstractSetting<R>)
146+
147+
@SettingEditorDsl
148+
fun hide(vararg settings: KProperty0<*>) {
149+
hideAll((settings.map { it.delegate } as List<AbstractSetting<*>>))
150+
}
151+
152+
@SettingEditorDsl
153+
fun hideAll(settingGroup: SettingGroup) = hideAll(settingGroup.settings)
154+
155+
@SettingEditorDsl
156+
fun hideAll(settings: Collection<AbstractSetting<*>>) {
157+
this@AutomationConfig.settings.removeAll(settings)
158+
hiddenSettings.addAll(settings)
159+
}
160+
161+
@SettingEditorDsl
162+
fun hideAll(vararg settingGroups: SettingGroup) {
163+
settingGroups.forEach { hideAll(it.settings) }
164+
}
165+
166+
@SettingEditorDsl
167+
fun hideAllExcept(settingGroup: SettingGroup, vararg settings: KProperty0<*>) {
168+
this@AutomationConfig.settings.removeIf {
169+
return@removeIf if (it in settingGroup.settings && it !in (settings.toList() as List<AbstractSetting<*>>)) {
170+
hiddenSettings.add(it)
171+
true
172+
} else false
173+
}
174+
}
175+
176+
open class BasicEditBuilder(val c: AutomationConfig, open val settings: Collection<AbstractSetting<*>>) {
177+
@SettingEditorDsl
178+
fun visibility(vis: () -> Boolean) =
179+
settings.forEach { it.visibility = vis }
180+
181+
@SettingEditorDsl
182+
fun hide() = c.hideAll(settings)
183+
184+
@SettingEditorDsl
185+
fun groups(vararg groups: NamedEnum) =
186+
settings.forEach { it.groups = mutableListOf(groups.toList()) }
187+
188+
@SettingEditorDsl
189+
fun groups(groups: MutableList<List<NamedEnum>>) =
190+
settings.forEach { it.groups = groups }
191+
}
192+
193+
open class TypedEditBuilder<T : Any>(
194+
c: AutomationConfig,
195+
override val settings: Collection<AbstractSetting<T>>
196+
) : BasicEditBuilder(c, settings) {
197+
@SettingEditorDsl
198+
fun defaultValue(value: T) =
199+
settings.forEach {
200+
it.defaultValue = value
201+
it.value = value
202+
}
203+
}
204+
205+
enum class InsertMode {
206+
Above,
207+
Below
208+
}
209+
}

src/main/kotlin/com/lambda/config/Configurable.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ package com.lambda.config
2020
import com.google.gson.JsonElement
2121
import com.google.gson.JsonObject
2222
import com.google.gson.reflect.TypeToken
23-
import com.lambda.Lambda
2423
import com.lambda.Lambda.LOG
2524
import com.lambda.config.settings.CharSetting
2625
import com.lambda.config.settings.FunctionSetting
@@ -69,7 +68,9 @@ abstract class Configurable(
6968
private fun registerConfigurable() = configuration.configurables.add(this)
7069

7170
inline fun <reified T : AbstractSetting<*>> T.register(): T {
72-
check(settings.add(this)) { "Setting with name $name already exists for configurable: ${this@Configurable.name}" }
71+
if (settings.any { it.name == name })
72+
throw IllegalStateException("Setting with name $name already exists for configurable: ${this@Configurable.name}")
73+
settings.add(this)
7374
return this
7475
}
7576

0 commit comments

Comments
 (0)