dragonfly-plugin 0.2.0

Dragonfly gRPC plugin SDK for Rust
Documentation
# Dragonfly Rust Plugin API

Welcome to the Rust Plugin API for Dragonfly server software. This library provides the tools to build high-performance, safe, and asynchronous plugins using native Rust.

The API is designed to be simple and explicit. You define your plugin's logic by implementing the `PluginEventHandler` trait and register your event subscriptions using the `#[derive(Handler)]` macro.

## Features

- **Asynchronous by Default:** Built on `tokio` and native Rust `async/await`.
- **Type-Safe:** All events and actions are strongly typed, catching bugs at compile time.
- **Simple Subscriptions:** A clean `#[derive(Handler)]` macro handles all event subscription logic.

---

## Quick Start Guide

Here is the complete process for creating a "Hello, World\!" plugin.

### 1\. Create a New Plugin

First, create a new binary crate for your plugin:

```sh
cargo new my_plugin --bin
```

### 2\. Update `Cargo.toml`

Next, add `dragonfly-plugin` and `tokio` to your dependencies.

```toml
[package]
name = "my_plugin"
version = "0.1.0"
edition = "2021"

[dependencies]
# This is the main API library
dragonfly-plugin = "0.1" # Or use a version number

# Tokio is required for the async runtime
tokio = { version = "1", features = ["full"] }
```

### 3\. Write Your Plugin (`src/main.rs`)

This is the complete code for a simple plugin that greets players on join and adds a prefix to their chat messages.

```rust,no_run
// --- Import all the necessary items ---
use dragonfly_plugin::{
    event::{EventContext, EventHandler},
    types, // Contains all event data structs
    Plugin, // The derive macro
    event_handler,
    PluginRunner,  // The runner struct.
    Server,
};

// make sure to derive Plugin, (Default isn't required but is used in this example code only.)
// when deriving Plugin you must include plugin attribute:
#[derive(Plugin, Default)]
#[plugin(
    id = "example-rust",        // A unique ID for your plugin (matches plugins.yaml)
    name = "Example Rust Plugin", // A human-readable name
    version = "1.0.0",               // Your plugin's version
    api = "1.0.0",               // The API version you're built against
)]
struct MyPlugin;

// --- 2. Implement the Event Handlers ---
//
// This is where all your plugin's logic lives.
// You only need to implement the `async fn` handlers
// for the events you subscribed to.
// note your LSP will probably fill them in as fn on_xx() -> Future<()>
// just delete the Future<()> + ... and put the keyword async before fn.
//
// #[event_handler] is a proc macro that detects which ever events you
// are overriding and thus setups a list of events to compile to
// as soon as your plugin is ran then it subscribes to them.
#[event_handler]
impl EventHandler for MyPlugin {
    /// This handler runs when a player joins the server.
    async fn on_player_join(
        &self,
        server: &Server,
        event: &mut EventContext<'_, types::PlayerJoinEvent>,
    ) {
        let player_name = &event.data.name;
        println!("Player '{}' has joined.", player_name);

        let welcome_message = format!(
            "Welcome, {}! This server is running a Rust plugin.",
            player_name
        );

        // Use the `server` handle to send actions
        server.send_chat(event.data.player_uuid.clone(), welcome_message).await.ok();
    }

    /// This handler runs when a player sends a chat message.
    async fn on_chat(
        &self,
        _server: &Server, // We don't need the server for this
        event: &mut EventContext<'_, types::ChatEvent>,
    ) {
        let new_message = format!("[Plugin] {}", event.data.message);

        // Use helper methods on the `event` to mutate it
        event.set_message(new_message);
    }
}

// --- 3. Start the Plugin ---
//
// This is the entry point that connects to the server.
#[tokio::main]
async fn main() {
    println!("Starting my-plugin...");

    // Here we default construct our Plugin.
    // note you can use it almost like a Context variable as long
    // as its Send / Sync.
    // so you can not impl default and have it hold like a PgPool or etc.
    PluginRunner::run(MyPlugin, "127.0.0.1:50051")
        .await
        .expect("Plugin failed to run");
}
```

---

## Writing Event Handlers

All plugin logic is built by implementing functions from the `PluginEventHandler` trait.

### `async fn` and Lifetimes

Because this API uses native `async fn` in traits, you **must** include the anonymous lifetime (`'_`) annotation in the `EventContext` type:

- **Correct:** `event: &mut EventContext<'_, types::ChatEvent>`
- **Incorrect:** `event: &mut EventContext<types::ChatEvent>`

### Reading Event Data

You can read immutable data directly from the event:

```rust,ignore
let player_name = &event.data.name;
println!("Player name: {}", player_name);
```

### Mutating Events

Some events are mutable. The `EventContext` provides helper methods (like `set_message`) to modify the event before the server processes it:

```rust,ignore
event.set_message(format!("New message: {}", event.data.message));
```

### Cancelling Events

You can also cancel compatible events to stop them from happening:

```rust,ignore
event.cancel();
```

## Available Events

The `#[subscriptions(...)]` macro accepts any variant from the `types::EventType` enum.

You can find a complete list of all available event names (e.g., `PlayerJoin`, `Chat`, `BlockBreak`) and their corresponding data structs (e.g., `types::PlayerJoinEvent`) by looking in the `./src/types.rs` file or by consulting the API documentation.