Escrevendo um Gerenciador de Evento
Gerenciadores de eventos são uma das funções principais dos plugins. Eles permitem que um plugin acesse o funcionamento interno do servidor e altere seu comportamento para realizar alguma outra ação. Como exemplo simples, vamos implementar um manipulador para o evento player_join
.
O sistema de eventos do Pumpkin tenta imitar o sistema de eventos do Bukkit/Spigot, para que desenvolvedores que vêm de lá tenham uma experiência mais fácil ao aprender o Pumpkin. No entanto, Rust tem concepções e regras diferentes, por isso nem tudo é como no Bukkit/Spigot. Rust não tem herança; em vez disso, ele só tem composição.
O sistema de eventos usa traits para gerenciar dinamicamente alguns eventos: Event
, Cancellable
, PlayerEvent
e etc. Cancellable
também pode ser um evento, porque é uma trait. (TODO: verificar isso)
Implementando o Evento de Entrada
Gerenciadores de eventos individuais são apenas structs que implementam a trait EventHandler<T>
(onde T
é uma implementação específica de evento).
O que são eventos bloqueantes?
O sistema de eventos do Pumpkin diferencia dois tipos de eventos: bloqueantes e não bloqueantes. Cada um tem seus benefícios:
Eventos bloqueantes
Prós:
+ Podem modificar o evento (como editar a mensagem de entrada)
+ Podem cancelar o evento
+ Têm um sistema de prioridade
Contras:
- São executados em sequência
- Podem diminuir a performance do servidor se não forem bem implementados
Eventos não bloqueantes
Prós:
+ São executados simultaneamente
+ São executados depois de todos os eventos bloqueantes terminarem
+ Ainda podem fazer algumas modificações (qualquer coisa que esteja atrás de um Mutex ou RwLock)
Contras:
- Não podem cancelar o evento
- Não têm sistema de prioridade
- Oferecem menos controle sobre o evento
Escrevendo um gerenciador
Como nosso objetivo principal aqui é mudar a mensagem de boas-vindas que o jogador vê quando entra no servidor, vamos escolher o tipo de evento bloqueante com uma prioridade baixa.
Adicione este código acima do método on_load
:
use async_trait::async_trait; // [!código ++:4]
use pumpkin_api_macros::with_runtime;
use pumpkin::plugin::{player::PlayerJoinEvent, Context, EventHandler};
use pumpkin_util::text::{color::NamedColor, TextComponent};
struct MyJoinHandler; // [!código ++:12]
#[with_runtime(global)]
#[async_trait]
impl EventHandler<PlayerJoinEvent> for MyJoinHandler {
async fn handle_blocking(&self, event: &mut PlayerJoinEvent) {
event.join_message =
TextComponent::text(format!("Bem-vindo, {}!", event.player.gameprofile.name))
.color_named(NamedColor::Green);
}
}
Explicação:
struct MyJoinHandler;
: A struct para nosso gerenciador de evento.#[with_runtime(global)]
: Pumpkin usa o runtime assíncrono tokio, que age de maneira estranha além dos limites do plugin. Embora não seja necessário neste exemplo específico, é uma boa prática envolver todos osimpl
s assíncronos que interagem com código assíncrono com essa macro.#[async_trait]
: Rust não tem suporte completo para traits com métodos assíncronos. Então, usamos o crateasync_trait
para permitir isso.async fn handle_blocking()
: Como escolhemos que este evento será bloqueante, é necessário implementar o métodohandle_blocking()
ao invés do métodohandle()
.
IMPORTANTE
É importante que a macro #[with_runtime(global)]
esteja acima da macro #[async_trait]
. Se estiverem na ordem oposta, a macro #[with_runtime(global)]
pode não funcionar.
Registrando o gerenciador
Agora que escrevemos o gerenciador de evento, precisamos informar ao plugin para usá-lo. Podemos fazer isso adicionando uma única linha no método on_load
:
use pumpkin::plugin::{player::PlayerJoinEvent, Context, EventHandler, EventPriority}; // [!código ++]
use pumpkin::plugin::{player::PlayerJoinEvent, Context, EventHandler}; // [!código --]
#[plugin_method]
async fn on_load(&mut self, server: &Context) -> Result<(), String> {
pumpkin::init_log!();
log::info!("Olá, Pumpkin!");
server.register_event(Arc::new(MyJoinHandler), EventPriority::Lowest, true).await; // [!código ++]
Ok(())
}
Agora, se compilarmos o plugin e entrarmos no servidor, devemos ver uma mensagem de boas-vindas verde com o nosso nome de usuário!