From d1315d20e8a14109011179dab729d617d5a7e9ab Mon Sep 17 00:00:00 2001 From: oblongboot <81798709+oblongboot@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:14:48 +0100 Subject: [PATCH] chore: cleanup, feat: registerLambda --- src/main/kotlin/org/cobalt/event/EventBus.kt | 48 +++++++++++++++++-- .../cobalt/event/annotation/SubscribeEvent.kt | 9 ++++ .../cobalt/pathfinder/calculate/PathNode.kt | 12 +++-- .../ui/component/setting/impl/ModeSetting.kt | 5 ++ .../kotlin/org/cobalt/util/PlayerUtils.kt | 16 ++++--- 5 files changed, 74 insertions(+), 16 deletions(-) diff --git a/src/main/kotlin/org/cobalt/event/EventBus.kt b/src/main/kotlin/org/cobalt/event/EventBus.kt index 087da7b..fd897fd 100644 --- a/src/main/kotlin/org/cobalt/event/EventBus.kt +++ b/src/main/kotlin/org/cobalt/event/EventBus.kt @@ -32,6 +32,50 @@ object EventBus { handlers.addAll(toAdd) dispatchCache.clear() } + /** + * Alternative to the [SubscribeEvent] annotation for people that are either too lazy to subscribe their events to the EventBus + * or for people that just prefer lambdas to annotation based methods. + * + * @param priority the priority this handler runs at relative to other handlers for the same event type + * @param receiveCancelled whether this handler should still be invoked if the event has already been canceled + * @param once if true, this handler is automatically unregistered after it runs once + * @param handler the lambda invoked when an event of type [T] is posted + * @return a token that can be passed to [unregister] to remove this handler + **/ + @JvmStatic + inline fun registerLambda( + priority: Event.Priority = Event.Priority.MEDIUM, + receiveCancelled: Boolean = false, + once: Boolean = false, + noinline handler: (T) -> Unit, + ): Any = registerLambdaInternal(T::class.java, priority, receiveCancelled, once, handler) + + @JvmStatic + @PublishedApi + internal fun registerLambdaInternal( + eventType: Class, + priority: Event.Priority, + receiveCancelled: Boolean, + once: Boolean, + handler: (T) -> Unit, + ): Any { + val token = Any() + val h = Handler( + listener = token, + eventType = eventType, + priority = priority, + receiveCancelled = receiveCancelled, + once = once, + methodName = "", + invoker = { event -> + @Suppress("UNCHECKED_CAST") + handler(event as T) + }, + ) + handlers.add(h) + dispatchCache.clear() + return token + } private fun scanClassForHandlers(listenerClass: Class<*>): List { return listenerClass.declaredMethods.mapNotNull { method -> @@ -54,10 +98,6 @@ object EventBus { "EventBus: could not access ${listenerClass.name}#${method.name}" } - require(Event::class.java.isAssignableFrom(params[0])) { - "EventBus: ${listenerClass.name}#${method.name} parameter ${params[0].name} is not an Event" - } - MethodMetadata( method = method, eventType = params[0].asSubclass(Event::class.java), diff --git a/src/main/kotlin/org/cobalt/event/annotation/SubscribeEvent.kt b/src/main/kotlin/org/cobalt/event/annotation/SubscribeEvent.kt index 24069e0..d56707f 100644 --- a/src/main/kotlin/org/cobalt/event/annotation/SubscribeEvent.kt +++ b/src/main/kotlin/org/cobalt/event/annotation/SubscribeEvent.kt @@ -2,6 +2,15 @@ package org.cobalt.event.annotation import org.cobalt.event.Event +/** + * Marks a function to be called when the specified event is fired + * Must be registered with [org.cobalt.event.EventBus.register] in your class/object + * If you dislike like annotation style methods like this or prefer lambdas, use [org.cobalt.event.EventBus.registerLambda], there are docs on that too + * + * @property ignoreCancelled whether this function should still be called if the event has been canceled + * @property priority the priority this runs at + * @property once should this event be unregistered after it has been run once + */ @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) annotation class SubscribeEvent( diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt index a114174..36499c4 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt @@ -23,15 +23,17 @@ data class PathNode( val centerVec: Vec3 = Vec3(x + 0.5, y + 0.5, z + 0.5) override fun equals(other: Any?): Boolean { - val otherNode = other as PathNode + if (this === other) return true + if (other !is PathNode) return false - return otherNode.x == x && - otherNode.y == y && - otherNode.z == z + return other.x == x && + other.y == y && + other.z == z } override fun hashCode(): Int { - return longHash(x, y, z).toInt() + val hash = longHash(x, y, z) + return (hash xor (hash ushr 32)).toInt() // IntelliJ is saying ushr isn't a thing, but it is wrong } companion object { diff --git a/src/main/kotlin/org/cobalt/ui/component/setting/impl/ModeSetting.kt b/src/main/kotlin/org/cobalt/ui/component/setting/impl/ModeSetting.kt index 5473d0d..a91e12a 100644 --- a/src/main/kotlin/org/cobalt/ui/component/setting/impl/ModeSetting.kt +++ b/src/main/kotlin/org/cobalt/ui/component/setting/impl/ModeSetting.kt @@ -13,6 +13,9 @@ class ModeSetting( val options: Array, ) : Setting(name, description, defaultValue) { + init { + require(options.isNotEmpty()) { "ModeSetting: '$name' must have at least one option" } + } override fun read(element: JsonElement) { this.value = element.asInt } @@ -51,6 +54,8 @@ class ModeSetting( } override fun mouseClicked(button: Int): Boolean { + if (options.isEmpty()) return false + val display = options.getOrNull(value) ?: "" val buttonWidth = Skia.textWidth(Skia.regularFont, display, FONT_SIZE) + 30f val startX = xPos + width - buttonWidth - PADDING diff --git a/src/main/kotlin/org/cobalt/util/PlayerUtils.kt b/src/main/kotlin/org/cobalt/util/PlayerUtils.kt index f6cbf51..659c7ff 100644 --- a/src/main/kotlin/org/cobalt/util/PlayerUtils.kt +++ b/src/main/kotlin/org/cobalt/util/PlayerUtils.kt @@ -7,7 +7,6 @@ import org.cobalt.Cobalt.minecraft import org.cobalt.util.rotation.Rotation object PlayerUtils { - @JvmStatic val player: LocalPlayer? get() = minecraft.player @@ -43,17 +42,20 @@ object PlayerUtils { @JvmStatic val rotation: Rotation get() { - val player = minecraft.player!! + val player = minecraft.player ?: return Rotation(0f, 0f, true) return Rotation(player.yRot, player.xRot, true) } @JvmStatic val position: BlockPos - get() = BlockPos( - floor(player!!.x).toInt(), - player!!.blockPosition().y, - floor(player!!.z).toInt() - ) + get() { + val player = player ?: return BlockPos(0, 0, 0) + return BlockPos( + floor(player.x).toInt(), + player.blockPosition().y, + floor(player.z).toInt() + ) + } @JvmStatic val isSuffocating: Boolean