Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/com/lambda/mixin/MinecraftClientMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ void onNetwork(ClientPlayerInteractionManager instance, Operation<Void> original

@Definition(id = "overlay", field = "Lnet/minecraft/client/MinecraftClient;overlay:Lnet/minecraft/client/gui/screen/Overlay;")
@Expression("this.overlay == null")
@ModifyExpressionValue(method = "tick", at = @At("MIXINEXTRAS:EXPRESSION"))
@ModifyExpressionValue(method = "tick", at = @At(value = "MIXINEXTRAS:EXPRESSION", ordinal = 1))
private boolean modifyCurrentScreenNullCheck(boolean original) {
if (!original || this.currentScreen != null) {
EventFlow.post(TickEvent.Input.Pre.INSTANCE);
Expand Down
5 changes: 2 additions & 3 deletions src/main/kotlin/com/lambda/Lambda.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ package com.lambda

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.lambda.config.serializer.BlockPosCodec
import com.lambda.config.serializer.BlockCodec
import com.lambda.config.serializer.BlockPosCodec
import com.lambda.config.serializer.ColorSerializer
import com.lambda.config.serializer.GameProfileCodec
import com.lambda.config.serializer.ItemCodec
Expand All @@ -45,7 +45,6 @@ import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.PotionItem
import net.minecraft.item.RangedWeaponItem
import net.minecraft.registry.DynamicRegistryManager
import net.minecraft.text.Text
import net.minecraft.util.math.BlockPos
import org.apache.logging.log4j.LogManager
Expand Down Expand Up @@ -92,7 +91,7 @@ object Lambda : ClientModInitializer {

init {
// We want the opengl context to be created
listenOnceUnsafe<ClientEvent.Startup>(priority = Int.MAX_VALUE) {
listenOnceUnsafe<ClientEvent.Startup>({ Int.MAX_VALUE }) {
LOG.info("$MOD_NAME $VERSION initialized in ${Loader.initialize()} ms\n")
if (ClickGuiLayout.setLambdaWindowIcon) setLambdaWindowIcon()
true
Expand Down
12 changes: 6 additions & 6 deletions src/main/kotlin/com/lambda/config/AutomationConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ open class AutomationConfig(

companion object {
context(module: Module)
fun MutableAutomationConfig.setDefaultAutomationConfig(
name: String = module.name,
edits: (AutomationConfig.() -> Unit)? = null
fun IMutableAutomationConfig.setDefaultAutomationConfig(
name: String = module.name,
edits: (AutomationConfig.() -> Unit)? = null
) {
this.defaultAutomationConfig = AutomationConfig("$name Automation Config").apply { edits?.invoke(this) }
}

fun MutableAutomationConfig.setDefaultAutomationConfig(
name: String,
edits: (AutomationConfig.() -> Unit)? = null
fun IMutableAutomationConfig.setDefaultAutomationConfig(
name: String,
edits: (AutomationConfig.() -> Unit)? = null
) {
defaultAutomationConfig = AutomationConfig("$name Automation Config").apply { edits?.invoke(this) }
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/lambda/config/Configuration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ abstract class Configuration : Jsonable, Loadable {
get() = File("${primary.parent}/${primary.nameWithoutExtension}-backup.${primary.extension}")

override fun load(): String {
listenUnsafe<ClientEvent.Shutdown>(Int.MIN_VALUE) { trySave() }
listenUnsafe<ClientEvent.Shutdown>({ Int.MIN_VALUE }) { trySave() }
register()
return super.load()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import com.lambda.interaction.managers.interacting.InteractConfig
import com.lambda.interaction.managers.inventory.InventoryConfig
import com.lambda.interaction.managers.rotating.RotationConfig

interface MutableAutomationConfig : Automated {
interface IMutableAutomationConfig : Automated {
var defaultAutomationConfig: AutomationConfig
var backingAutomationConfig: AutomationConfig
var automationConfig: AutomationConfig
Expand All @@ -40,7 +40,7 @@ interface MutableAutomationConfig : Automated {
override val eatConfig: EatConfig get() = automationConfig.eatConfig
}

class MutableAutomationConfigImpl : MutableAutomationConfig {
class MutableAutomationConfig : IMutableAutomationConfig {
override var defaultAutomationConfig: AutomationConfig = AutomationConfig.Companion.DEFAULT
set(value) {
field = value
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/lambda/config/UserAutomationConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import com.lambda.module.Module
import com.lambda.module.ModuleRegistry.moduleNameMap

class UserAutomationConfig(override val name: String) : AutomationConfig(name, UserAutomationConfigs) {
val linkedModules = setting<String>("Linked Modules", emptySet(), moduleNameMap.filter { it.value.defaultAutomationConfig != Companion.DEFAULT }.keys)
val linkedModules = setting<String>("Linked Modules", emptySet(), moduleNameMap.filter { it.value.defaultAutomationConfig != Companion.DEFAULT }.keys) { false }
.onSelect { name ->
moduleNameMap[name]?.let {
it.removeLink()
Expand Down
11 changes: 11 additions & 0 deletions src/main/kotlin/com/lambda/event/EventFlow.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ object EventFlow {
concurrentListeners.unsubscribe(this)
}

fun Any.updateListenerSorting() {
syncListeners.updateListenerSorting(this)
concurrentListeners.updateListenerSorting(this)
}

private fun Subscriber.updateListenerSorting(owner: Any) =
values.forEach { listeners ->
val matching = listeners.filter { it.owner === owner }
matching.forEach { listeners.remove(it); it.priority.update(); listeners.add(it) }
}

init {
// parallel event execution on dedicated threads
runConcurrent {
Expand Down
22 changes: 22 additions & 0 deletions src/main/kotlin/com/lambda/event/OwnerPriority.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2026 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 <http://www.gnu.org/licenses/>.
*/

package com.lambda.event

interface OwnerPriority {
val ownerPriority: Int
}
9 changes: 7 additions & 2 deletions src/main/kotlin/com/lambda/event/listener/Listener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ package com.lambda.event.listener
import com.lambda.event.Event
import com.lambda.event.EventFlow
import com.lambda.event.Muteable
import com.lambda.event.OwnerPriority
import com.lambda.module.Module
import com.lambda.util.collections.Updatable

/**
* An abstract class representing a [Listener] in the [Event] system ([EventFlow]).
Expand All @@ -45,7 +47,7 @@ import com.lambda.module.Module
* @property alwaysListen If true, the [Listener] will always be triggered, even if the [owner] is [Muteable.isMuted].
*/
abstract class Listener<T : Event> : Comparable<Listener<T>> {
abstract val priority: Int
abstract val priority: Updatable<Int>
abstract val owner: Any
abstract val alwaysListen: Boolean

Expand All @@ -61,10 +63,13 @@ abstract class Listener<T : Event> : Comparable<Listener<T>> {

companion object {
val comparator = compareBy<Listener<out Event>> {
it.priority
it.priority.value
}.thenBy {
// Hashcode is needed because ConcurrentSkipListSet handles insertion based on compareTo
it.hashCode()
}

val Any.ownerPriorityOr0Getter
get() = (this as? OwnerPriority)?.let { { ownerPriority } } ?: { 0 }
}
}
13 changes: 8 additions & 5 deletions src/main/kotlin/com/lambda/event/listener/SafeListener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.lambda.threading.runConcurrent
import com.lambda.threading.runGameScheduled
import com.lambda.threading.runSafe
import com.lambda.util.Pointer
import com.lambda.util.collections.updatable
import com.lambda.util.selfReference
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -62,11 +63,13 @@ import kotlin.reflect.KProperty
* @property function The function to be executed when the event occurs. This function operates within a [SafeContext].
*/
class SafeListener<T : Event>(
override val priority: Int = 0,
priorityProvider: () -> Int,
override val owner: Any,
override val alwaysListen: Boolean = false,
val function: SafeContext.(T) -> Unit
) : Listener<T>(), ReadOnlyProperty<Any?, T?> {
override val priority = updatable(priorityProvider)

/**
* The last processed event signal.
*/
Expand Down Expand Up @@ -118,7 +121,7 @@ class SafeListener<T : Event>(
* @return The newly created and registered [SafeListener].
*/
inline fun <reified T : Event> Any.listen(
priority: Int = 0,
noinline priority: () -> Int = ownerPriorityOr0Getter,
alwaysListen: Boolean = false,
noinline function: SafeContext.(T) -> Unit = {}
): SafeListener<T> {
Expand Down Expand Up @@ -163,7 +166,7 @@ class SafeListener<T : Event>(
*/
fun <T : Event> Any.listen(
kClass: KClass<out T>,
priority: Int = 0,
priority: () -> Int = ownerPriorityOr0Getter,
alwaysListen: Boolean = false,
function: SafeContext.(T) -> Unit = {},
): SafeListener<T> {
Expand Down Expand Up @@ -205,7 +208,7 @@ class SafeListener<T : Event>(
* @return The newly created and registered [SafeListener].
*/
inline fun <reified T : Event> Any.listenOnce(
priority: Int = 0,
noinline priority: () -> Int = ownerPriorityOr0Getter,
alwaysListen: Boolean = false,
noinline predicate: SafeContext.(T) -> Boolean = { true },
): ReadWriteProperty<Any?, T?> {
Expand Down Expand Up @@ -253,7 +256,7 @@ class SafeListener<T : Event>(
* @return The newly created and registered [SafeListener].
*/
inline fun <reified T : Event> Any.listenConcurrently(
priority: Int = 0,
noinline priority: () -> Int = ownerPriorityOr0Getter,
alwaysListen: Boolean = false,
scheduler: CoroutineDispatcher = Dispatchers.Default,
noinline function: suspend SafeContext.(T) -> Unit = {},
Expand Down
11 changes: 7 additions & 4 deletions src/main/kotlin/com/lambda/event/listener/UnsafeListener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.lambda.event.listener.SafeListener.Companion.listenConcurrently
import com.lambda.event.listener.SafeListener.Companion.listenOnce
import com.lambda.threading.runConcurrent
import com.lambda.util.Pointer
import com.lambda.util.collections.updatable
import com.lambda.util.selfReference
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -60,11 +61,13 @@ import kotlin.reflect.KProperty
* @property function The function to be executed when the event occurs. This function operates without a [SafeContext].
*/
class UnsafeListener<T : Event>(
override val priority: Int,
priorityProvider: () -> Int,
override val owner: Any,
override val alwaysListen: Boolean = false,
val function: (T) -> Unit,
) : Listener<T>(), ReadOnlyProperty<Any?, T?> {
override val priority = updatable(priorityProvider)

/**
* The last processed event signal.
*/
Expand Down Expand Up @@ -107,7 +110,7 @@ class UnsafeListener<T : Event>(
* @return The newly created and registered [UnsafeListener].
*/
inline fun <reified T : Event> Any.listenUnsafe(
priority: Int = 0,
noinline priority: () -> Int = ownerPriorityOr0Getter,
alwaysListen: Boolean = false,
noinline function: (T) -> Unit = {},
): UnsafeListener<T> {
Expand Down Expand Up @@ -144,7 +147,7 @@ class UnsafeListener<T : Event>(
* @return The newly created and registered [UnsafeListener].
*/
inline fun <reified T : Event> Any.listenOnceUnsafe(
priority: Int = 0,
noinline priority: () -> Int = ownerPriorityOr0Getter,
alwaysListen: Boolean = false,
noinline function: (T) -> Boolean = { true },
): ReadWriteProperty<Any?, T?> {
Expand Down Expand Up @@ -193,7 +196,7 @@ class UnsafeListener<T : Event>(
* @return The newly created and registered [UnsafeListener].
*/
inline fun <reified T : Event> Any.listenConcurrentlyUnsafe(
priority: Int = 0,
noinline priority: () -> Int = ownerPriorityOr0Getter,
alwaysListen: Boolean = false,
scheduler: CoroutineDispatcher = Dispatchers.Default,
noinline function: suspend (T) -> Unit = {},
Expand Down
39 changes: 20 additions & 19 deletions src/main/kotlin/com/lambda/gui/components/SettingsWidget.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package com.lambda.gui.components

import com.lambda.config.AutomationConfig
import com.lambda.config.Configurable
import com.lambda.config.MutableAutomationConfig
import com.lambda.config.IMutableAutomationConfig
import com.lambda.config.Setting
import com.lambda.config.UserAutomationConfig
import com.lambda.config.configurations.UserAutomationConfigs
Expand All @@ -38,18 +38,25 @@ object SettingsWidget {
fun ImGuiBuilder.buildConfigSettingsContext(config: Configurable) {
group {
if (config is Module) {
with(config.keybindSetting) { buildLayout() }
with(config.disableOnReleaseSetting) { buildLayout() }
with(config.drawSetting) { buildLayout() }
}
if (config is HudModule) {
with(config.backgroundColor) { buildLayout() }
}
smallButton("Reset") {
config.settings.forEach { it.reset(silent = true) }
button("Module Settings") {
ImGui.openPopup("##module-settings-popup-${config.name}")
}
ImGui.setNextWindowSizeConstraints(0f, 0f, Float.MAX_VALUE, io.displaySize.y * 0.5f)
popupContextItem("##module-settings-popup-${config.name}", ImGuiPopupFlags.None) {
with(config.keybindSetting) { buildLayout() }
with(config.prioritySetting) { buildLayout() }
with(config.disableOnReleaseSetting) { buildLayout() }
with(config.drawSetting) { buildLayout() }
if (config is HudModule) {
with(config.backgroundColor) { buildLayout() }
}
smallButton("Reset") {
config.settings.forEach { it.reset(silent = true) }
}
}
}
lambdaTooltip("Resets all settings for this module to their default values")
if (config is MutableAutomationConfig && config.automationConfig !== AutomationConfig.Companion.DEFAULT) {
if (config is IMutableAutomationConfig && config.automationConfig !== AutomationConfig.Companion.DEFAULT) {
button("Automation Config") {
ImGui.openPopup("##automation-config-popup-${config.name}")
}
Expand Down Expand Up @@ -78,14 +85,8 @@ object SettingsWidget {
}
}
}
val toIgnoreSettings =
when (config) {
is HudModule -> setOf(config.backgroundColor)
is Module -> setOf(config.keybindSetting, config.disableOnReleaseSetting, config.drawSetting)
is UserAutomationConfig -> setOf(config.linkedModules)
else -> emptySet()
}
val visibleSettings = config.settings.filter { it.visibility() } - toIgnoreSettings

val visibleSettings = config.settings.filter { it.visibility() }
if (visibleSettings.isEmpty()) return
else separator()
val (grouped, ungrouped) = visibleSettings.partition { it.groups.isNotEmpty() }
Expand Down
Loading