Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add guide for AsyncChatEvent #264

Merged
merged 6 commits into from
Jan 27, 2024
Merged
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
1 change: 1 addition & 0 deletions config/sidebar.paper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const paper: SidebarsConfig = {
"dev/api/event-api/event-listeners",
"dev/api/event-api/custom-events",
"dev/api/event-api/handler-lists",
"dev/api/event-api/chat-event",
],
},
{
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
133 changes: 133 additions & 0 deletions docs/paper/dev/api/event-api/chat-event.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
slug: /dev/chat-events
description: An outline on the AsyncChatEvent and how to handle it
---

# Chat Events

The Chat Event has evolved a few times over the years.
This guide will explain how to properly use the new <VersionedJavaDocLink target={"/io/papermc/paper/event/player/AsyncChatEvent.html"}>`AsyncChatEvent`</VersionedJavaDocLink> and its <VersionedJavaDocLink target={"/io/papermc/paper/chat/ChatRenderer.html"}>`ChatRenderer`</VersionedJavaDocLink>.
The <VersionedJavaDocLink target={"/io/papermc/paper/event/player/AsyncChatEvent.html"}>`AsyncChatEvent`</VersionedJavaDocLink> is an improved version of the old <VersionedJavaDocLink target={"/org/bukkit/event/player/AsyncPlayerChatEvent.html"}>`AsyncPlayerChatEvent`</VersionedJavaDocLink> that allows you to render chat messages individually for each player.

:::note

#### AsyncChatEvent vs ChatEvent

The key difference between the <VersionedJavaDocLink target={"/io/papermc/paper/event/player/AsyncChatEvent.html"}>`AsyncChatEvent`</VersionedJavaDocLink> and the <VersionedJavaDocLink target={"/io/papermc/paper/event/player/ChatEvent.html"}>`ChatEvent`</VersionedJavaDocLink> is that the <VersionedJavaDocLink target={"/io/papermc/paper/event/player/AsyncChatEvent.html"}>`AsyncChatEvent`</VersionedJavaDocLink> is fired asynchronously.
This means that it does not block the main thread and sends the chat message when the listener has completed.
Be aware that using the Bukkit API in the event might cause errors.
If you need to use the Bukkit API, you can use the <VersionedJavaDocLink target={"/io/papermc/paper/event/player/ChatEvent.html"}>`ChatEvent`</VersionedJavaDocLink>.
However, we recommend using the [`BukkitScheduler`](../scheduler.md).

:::

## Understanding the renderer

Before we can start using the new chat event, we need to understand how the new renderer works.
The renderer is Paper's way of allowing plugins to modify the chat message before it is sent to the player.
This is done by using the <VersionedJavaDocLink target={"/io/papermc/paper/chat/ChatRenderer.html"}>`ChatRenderer`</VersionedJavaDocLink> interface with its <VersionedJavaDocLink target={"/io/papermc/paper/chat/ChatRenderer.html#render(org.bukkit.entity.Player,net.kyori.adventure.text.Component,net.kyori.adventure.text.Component,net.kyori.adventure.audience.Audience)"}>`render`</VersionedJavaDocLink> method. Previously, this was done by using the <VersionedJavaDocLink target={"/org/bukkit/event/player/AsyncPlayerChatEvent.html"}>`AsyncPlayerChatEvent`</VersionedJavaDocLink> with its <VersionedJavaDocLink target={"/org/bukkit/event/player/AsyncPlayerChatEvent.html#setFormat(java.lang.String)"}>`setFormat`</VersionedJavaDocLink> method.

```java title="ChatRenderer#render"
public @NotNull Component render(@NotNull Player source, @NotNull Component sourceDisplayName, @NotNull Component message, @NotNull Audience viewer) {
// ...
}
```

- The <VersionedJavaDocLink target={"/io/papermc/paper/chat/ChatRenderer.html#render(org.bukkit.entity.Player,net.kyori.adventure.text.Component,net.kyori.adventure.text.Component,net.kyori.adventure.audience.Audience)"}>`render`</VersionedJavaDocLink> method is called when a chat message is sent to the player.
- The `source` parameter is the player that sent the message.
- The `sourceDisplayName` parameter is the display name of the player that sent the message.
- The `message` parameter is the message that was sent.
- The `viewer` parameter is the player that is receiving the message.

:::tip[ChatRenderer.ViewerUnaware]

If your renderer does not need to know about the viewer, you can use the <VersionedJavaDocLink target={"/io/papermc/paper/chat/ChatRenderer.ViewerUnaware.html"}>`ChatRenderer.ViewerUnaware`</VersionedJavaDocLink> interface instead of the <VersionedJavaDocLink target={"/io/papermc/paper/chat/ChatRenderer.html"}>`ChatRenderer`</VersionedJavaDocLink> interface.
This will benefit performance as the message will only be rendered once instead of each individual player.

:::

## Using the renderer

There are two ways to use the renderer.
1. Implementing the <VersionedJavaDocLink target={"/io/papermc/paper/chat/ChatRenderer.html"}>`ChatRenderer`</VersionedJavaDocLink> interface in a class.
2. Using a lambda expression.

Depending on the complexity of your renderer you may want to use one or the other.

### Implementing the ChatRenderer interface

The first way of using the renderer is by implementing the <VersionedJavaDocLink target={"/io/papermc/paper/chat/ChatRenderer.html"}>`ChatRenderer`</VersionedJavaDocLink> interface in a class.
In this example, we will be using our ChatListener class.
Next, we need to tell the event to use the renderer by using the <VersionedJavaDocLink target={"/1.20/io/papermc/paper/event/player/AbstractChatEvent.html#renderer()"}>`renderer`</VersionedJavaDocLink> method.

```java title="ChatListener.java"
public class ChatListener implements Listener, ChatRenderer { // Implement the ChatRenderer and Listener interface

// Listen for the AsyncChatEvent
@EventHandler
public void onChat(AsyncChatEvent event) {
event.renderer(this); // Tell the event to use our renderer
}

// Override the render method
@Override
public @NotNull Component render(@NotNull Player source, @NotNull Component sourceDisplayName, @NotNull Component message, @NotNull Audience viewer) {
// ...
}
}
```

:::note

If you decide to create a separate class for your renderer, it is important to know that you don't need to instantiate the class every time the event is called.
In this case you can use the singleton pattern to create a single instance of the class.

:::

### Using a lambda expression

Another way of using the renderer is by using a lambda expression.

```java title="ChatListener.java"
public class ChatListener implements Listener {

@EventHandler
public void onChat(AsyncChatEvent event) {
event.renderer((source, sourceDisplayName, message, viewer) -> {
// ...
});
}
}
```

## Rendering the message

Now that we have our renderer, we can start rendering the message.

Let's say we want to render our chat to look like this:

![](./assets/plain-message-rendering.png)

To do this, we need to return a new `Component` that contains the message we want to send.

```java title="ChatListener.java"
public class ChatListener implements Listener, ChatRenderer {

// Listener logic

@Override
public @NotNull Component render(@NotNull Player source, @NotNull Component sourceDisplayName, @NotNull Component message, @NotNull Audience viewer) {
return sourceDisplayName
.append(Component.text(": "))
.append(message);
}
}
```

Now you can see that the message is rendered as we wanted it to be.

## Conclusion

That is all you need to know about the new chat event and its renderer.
Of course there are many more things you can do with Components in general.
If you want to learn more about Components, you can read the [Component Documentation](https://docs.advntr.dev/text.html).