> For the complete documentation index, see [llms.txt](https://docs.quickclient.cc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.quickclient.cc/en/events/basics.md).

# Subscribing to events

How to subscribe to an event, cancel it and read its data.

A script subscribes to events in `activate()`. A subscription is active while the script is enabled. When the script is disabled, Quick removes it, and the next time the script is enabled `activate()` subscribes again.

There are two ways to subscribe: by event name, in which case the handler receives a generic `ScriptEvent`, or to a typed event with its own fields.

```java
@Override
public void activate() {
    on("attack", event -> log().info("attack"));

    on(KeyEvent.class, event -> {
        if (event.pressed() && event.key() == 'F' && !event.screen().open()) {
            chat("F pressed");
        }
    });

    on(ChatEvent.class, event -> {
        if (event.server() && event.contains("joined")) event.cancel();
    });
}
```

## How it works

* On every attack a line is written to the console.
* When you press F with no screen open, a message appears in chat.
* Server messages about players joining are hidden.

## Methods

| Method                           | Type       | Description                                                |
| -------------------------------- | ---------- | ---------------------------------------------------------- |
| `on(eventName, handler)`         | `boolean`  | subscribe by name; `false` if Quick does not know the name |
| `on(EventClass.class, listener)` | `boolean`  | subscribe to a typed event                                 |
| `off(eventName)`                 | `void`     | remove the script's subscriptions to one event             |
| `eventNames()`                   | `String[]` | which names your Quick build knows                         |

The handler runs on the game's main thread. If it throws an exception, the script is disabled and the reason is shown in the console.

## ScriptEvent

When you subscribe by name, the handler receives a `ScriptEvent`. Quick uses the same object for all calls, so you cannot store it in a field: outside the handler it is empty.

| Method          | Type      | Description                                                     |
| --------------- | --------- | --------------------------------------------------------------- |
| `name()`        | `String`  | event name                                                      |
| `payload()`     | `Object`  | event data or `null`, see [Event list](/en/events/reference.md) |
| `cancellable()` | `boolean` | whether it can be cancelled                                     |
| `cancelled()`   | `boolean` | already cancelled, by this script or another one                |
| `cancel()`      | `void`    | cancel: Quick will not perform the action the event belongs to  |

Cast the data to the type you need: `int slot = (Integer) event.payload()`, `PacketInfo packet = (PacketInfo) event.payload()`.

## Typed events

The classes are in `hex.script.api.event`. They all extend `Event`, so each of them has `cancellable()`, `cancelled()`, `cancel()` and `name()`. The object is reused as well, so read the values you need right away.

| Class         | When                                          | Own fields                                                                                                     |
| ------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `TickEvent`   | client tick while you are in a world          | none                                                                                                           |
| `KeyEvent`    | a key was pressed or released                 | `key()`, `scancode()`, `modifiers()`, `pressed()`, `released()`, `shift()`, `control()`, `alt()`, `screen()`   |
| `MouseEvent`  | mouse button, including when a screen is open | `button()`, `left()`, `right()`, `middle()`, `pressed()`, `released()`, `x()`, `y()`, `screen()`               |
| `ScrollEvent` | the mouse wheel was scrolled                  | `amount()` (positive is up), `horizontal()`, `up()`, `screen()`                                                |
| `ScreenEvent` | a screen was opened or closed                 | `opened()`, `closed()`, `screen()`; on close this is the screen that was closed                                |
| `ChatEvent`   | chat message                                  | `message()`, `text()`, `source()`, `player()`, `server()`, `client()`, `contains()`, `startsWith()`, `after()` |
| `SoundEvent`  | a sound started playing                       | `id()`, `is()`, `source()`, `volume()`, `pitch()`, `x()`, `y()`, `z()`; cannot be cancelled                    |

If you cancel a `MouseEvent`, the click does not reach the game. Cancelling a `ChatEvent` hides the message only on your side, and the server does not know about it. `ChatEvent.after("Balance: ")` returns the rest of the line after the prefix, which makes it easier to pull numbers out of chat.

### The screen in an event

For input events, `screen()` returns the screen that was open at the moment of the event, with its data at that moment. You can get the same object anywhere with `client().screen()`.

| Method                                           | Type      | Description                                                      |
| ------------------------------------------------ | --------- | ---------------------------------------------------------------- |
| `open()`                                         | `boolean` | whether any screen is open                                       |
| `kind()`                                         | `int`     | `Screen.NONE`, `CHAT`, `INVENTORY`, `CONTAINER`, `MENU`, `OTHER` |
| `chat()`, `inventory()`, `container()`, `menu()` | `boolean` | quick checks                                                     |
| `type()`                                         | `String`  | short class name of the screen, for example `"ChatScreen"`       |
| `title()`                                        | `String`  | title without formatting                                         |
| `is(name)`                                       | `boolean` | whether the class name contains the string, case insensitive     |

## Common mistakes

* Subscribing in the constructor instead of `activate()`. The subscription is removed the first time the script is disabled and does not come back.
* Storing `event` in a field. After the handler returns, the object is empty or already used by the next event.
* Heavy work in `tick` or `packetReceive`. This is the main thread, and the game waits until the handler finishes.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.quickclient.cc/en/events/basics.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
