Documentation

Learn how to install and use LiquidBounce with our comprehensive guides

Events

The client raises events for what happens in the game, and event listeners react to them. Modules, modes, toggleable groups, HUD components and the add-on itself are listeners, so they can declare handlers directly.

When handlers run

A handler runs only while its listener's running is true:

ListenerRunning while
ClientModulethe module is enabled and the player is in a world
Modeit is the selected mode and its parent is running
ToggleableValueGroupit is enabled and its parent is running
HUD componentit is enabled and the HUD module is running
LiquidBounceAddonthe add-on's state is LOADED
any other EventListeneralways, unless running is overridden

Handlers are registered once and stay registered. They are skipped while their listener is not running, they are not added and removed on every toggle.

Handlers

object ModuleNoSwear : ClientModule("NoSwear", ModuleCategories.MISC) {

    private val word by text("Word", "heck")

    @Suppress("unused")
    private val chatHandler = handler<ChatSendEvent> { event ->
        if (event.message.contains(word, ignoreCase = true)) {
            event.cancelEvent()
        }
    }

}

handler<T> registers the handler when the property is initialized. @Suppress("unused") silences the warning that the property is never read.

Events that extend CancellableEvent can be cancelled with cancelEvent(), which usually stops what the event announced: the chat message is not sent, the packet is dropped, the jump does not happen. isCancelled tells whether an earlier handler cancelled it.

Handlers run on the thread that raises the event. That is the render thread for most events, PacketEvent can arrive on the network thread.

Priority

Handlers with a higher priority run first, the default is 0. EventPriorityConvention names the values the client uses:

ConstantValue
FIRST_PRIORITY1000
CRITICAL_MODIFICATION500
MODEL_STATE-10
SAFETY_FEATURE-50
OBJECTION_AGAINST_EVERYTHING-100
FINAL_DECISION-500
READ_FINAL_STATE-1000
@Suppress("unused")
private val packetHandler = handler<PacketEvent>(priority = EventPriorityConvention.FIRST_PRIORITY) { event ->
    if (event.origin == TransferOrigin.INCOMING && event.packet is ClientboundSetTimePacket) {
        event.cancelEvent()
    }
}

Sequences

A sequence is a handler that can wait. tickHandler runs its block on every tick, but not again until the previous run has finished, so waiting inside it spaces the runs out:

object ModuleReminder : ClientModule("Reminder", ModuleCategories.MISC) {

    private val interval by int("Interval", 1200, 20..12000, "ticks")

    @Suppress("unused")
    private val tickHandler = tickHandler {
        waitTicks(interval)
        chat("Drink some water.")
    }

}

sequenceHandler<T> starts a sequence for every event of type T, each running on its own:

@Suppress("unused")
private val attackHandler = sequenceHandler<AttackEntityEvent> { event ->
    val target = event.entity
    val ticks = tickUntil { !target.isAlive || it >= 40 }
    if (!target.isAlive) {
        chat("Took ${target.name.string} down in $ticks ticks.")
    }
}

Handling the event itself is over once the sequence first waits, so cancelling or changing the event only works before that.

FunctionWaits
waitTicks(ticks)the given number of ticks
waitSeconds(seconds)seconds * 20 ticks
tickUntil { ticks -> done }until the condition is true, checked once per tick; returns the ticks waited
waitMatches<T> { event -> matches }until an event of type T matches, and returns it

waitTicks, waitSeconds and tickUntil resume on the render thread, waitMatches on the thread that raised the matching event. When the listener stops running, for example because the module was disabled, its sequences are cancelled at their next wait.

Without Kotlin

handler, tickHandler and sequenceHandler are Kotlin extensions. Every listener also has plain methods, which work from Java and Kotlin alike and return an AutoCloseable that unregisters again:

MethodDescription
on(type, handler)Calls handler for every event of type while the listener is running.
on(type, priority, handler)The same with a priority.
onTick(task)Runs task on every tick while the listener is running.
after(ticks, task)Runs task once, ticks ticks from now.
every(ticks, task)Runs task every ticks ticks, the first time after ticks.

after and every start counting right away and are cancelled for good as soon as the listener is not running. Call them while it runs, for example from a handler, not from a constructor. See Using Java.

Listeners of your own

Anything can implement EventListener. Handlers of a plain listener run as long as the client does, so register it with registerListeners to have it unregistered when the add-on fails:

object ChatLogger : EventListener {

    @Suppress("unused")
    private val chatHandler = handler<ChatReceiveEvent> { event ->
        println(event.message)
    }

}

Override running to pause it, parent() to tie it to another listener, and call unregister() to remove its handlers for good.

Events of your own

Subclass Event, or CancellableEvent, and raise it with EventManager.callEvent. It returns the event after every handler ran.

class GreetingEvent(val name: String) : Event()

fun greet(name: String) {
    EventManager.callEvent(GreetingEvent(name))
}

Stable events

These events are part of the stable API. The client raises many more, see the event classes, but those may change between releases.

EventRaised
GameTickEventOn every client tick, at its start.
PlayerTickEventBefore the local player ticks. Cancel to skip the tick.
PlayerPostTickEventAfter the local player ticked.
PlayerNetworkMovementTickEventWhen the player's position is sent, PRE before and POST after. In PRE, x, y, z and ground change what is sent, cancelling sends nothing.
PlayerMoveEventWhen the local player moves. movement can be changed.
PlayerJumpEventWhen the local player jumps. motion and yaw can be changed, cancel to not jump.
MovementInputEventWhen the movement keys are read. directionalInput, jump and sneak can be changed.
AttackEntityEventBefore the player attacks an entity. Cancel to not attack.
TagEntityEventWhen the client decides how to treat an entity: dontTarget(), ignore(), assumeFriend(), color(...).
PacketEventFor every packet, OUTGOING or INCOMING. Cancel to drop it.
ChatSendEventWhen the player sends a chat message. Cancel to not send it.
ChatReceiveEventWhen a chat or game message arrives. Cancel to hide it.
ScreenEventWhen a screen is opened, with null when screens are closed. Cancel to keep the current one.
KeyboardKeyEventWhen a key is pressed, repeated or released.
MouseButtonEventWhen a mouse button is pressed or released.
OverlayRenderEventWhile the HUD is drawn, see Rendering.
WorldRenderEventWhile the world is drawn, see Rendering.
WorldChangeEventWhen the client's world changes, with null when leaving it.
ChunkLoadEventWhen the server sends a chunk, with its chunk coordinates.
BlockChangeEventWhen a block in the client's world changes.
WorldEntityRemoveEventWhen an entity is removed from the client's world, with the reason.
DisconnectEventWhen the player leaves a server or world.
ModuleToggleEventWhen a module is enabled or disabled.
RefreshArrayListEventRaise it to make the HUD's module list read names and tags again.
FriendChangeEventWhen a friend is added or removed.
NotificationEventWhen a notification is shown.