| Crates.io | dragonfly-plugin |
| lib.rs | dragonfly-plugin |
| version | 0.3.0 |
| created_at | 2025-11-25 05:14:20.347732+00 |
| updated_at | 2025-11-27 22:38:19.291583+00 |
| description | Dragonfly gRPC plugin SDK for Rust |
| homepage | https://github.com/secmc/dragonfly-plugins |
| repository | https://github.com/secmc/dragonfly-plugins |
| max_upload_size | |
| id | 1949212 |
| size | 323,559 |
The dragonfly-plugin crate is the Rust SDK for Dragonfly gRPC plugins. It gives you:
#[derive(Plugin)]) and commands (#[derive(Command)]).EventHandler trait and an #[event_handler] macro.Server handle with high‑level helpers (like send_chat, teleport, world_set_block, …).PluginRunner that connects your process to the Dragonfly host and runs the event loop.The Rust SDK lives under packages/rust as a small workspace:
dragonfly-plugin (this crate): Public SDK surface used by plugin authors.
src/lib.rs: Re-exports core modules and pulls in this README as crate-level docs.src/command.rs: Command context (Ctx), parsing helpers, and CommandRegistry trait.src/event/: Event system (EventContext, EventHandler, mutation helpers).src/server/: Server handle and generated helpers for sending actions to the host.src/generated/df.plugin.rs: Prost/tonic types generated from proto/types/*.proto (do not edit).macro/ (dragonfly-plugin-macro): Procedural macros for #[derive(Plugin)], #[derive(Command)],
and #[event_handler]. This crate is re-exported by dragonfly-plugin and is not used directly by plugins.xtask/: Internal code generation tooling that reads df.plugin.rs and regenerates
event/handler.rs, event/mutations.rs, and server/helpers.rs. It is not published.example/: A minimal example plugin crate showing recommended usage patterns for the SDK.tests/: Integration tests covering command derivation, event dispatch, server helpers,
and the interaction between the runtime and macros.All APIs in this README reflect the 0.3.x line. Within 0.3.x we intend to keep:
Plugin, EventHandler, EventSubscriptions, and CommandRegistry trait shapes.event_handler, Plugin, and Command macros and their attribute syntax.Server helpers and event::EventContext semantics (including cancel and mutation helpers).Breaking changes may still happen in a future 0.4.0, but not within 0.3.x.
cargo new my_plugin --bin
[package]
name = "my_plugin"
version = "0.1.0"
edition = "2021"
[dependencies]
dragonfly-plugin = "0.3"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite"] } # optional, for DB-backed examples
Only dragonfly-plugin and tokio are required; other crates (like sqlx) are up to your plugin.
use dragonfly_plugin::{
event::{EventContext, EventHandler},
event_handler,
types,
Plugin,
PluginRunner,
Server,
};
#[derive(Plugin, Default)]
#[plugin(
id = "example-rust", // must match plugins.yaml
name = "Example Rust Plugin",
version = "0.3.0",
api = "1.0.0"
)]
struct MyPlugin;
#[event_handler]
impl EventHandler for MyPlugin {
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 = format!(
"Welcome, {}! This server is running a Rust plugin.",
player_name
);
// Ignore send errors; they usually mean the host shut down.
let _ = server
.send_chat(event.data.player_uuid.clone(), welcome)
.await;
}
async fn on_chat(
&self,
_server: &Server,
event: &mut EventContext<'_, types::ChatEvent>,
) {
let new_message = format!("[Plugin] {}", event.data.message);
event.set_message(new_message);
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Starting example-rust plugin...");
PluginRunner::run(MyPlugin, "tcp://127.0.0.1:50050").await
}
The #[event_handler] macro:
on_* methods you implement.EventSubscriptions impl that subscribes to the corresponding types::EventType variants.event::dispatch_event.The 0.3.x series introduces a first‑class command system.
use dragonfly_plugin::{command::Ctx, Command};
#[derive(Command)]
#[command(
name = "eco",
description = "Economy commands.",
aliases("economy", "rustic_eco")
)]
pub enum Eco {
#[subcommand(aliases("donate"))]
Pay { amount: f64 },
#[subcommand(aliases("balance", "money"))]
Bal,
}
This generates:
Eco::spec() -> types::CommandSpec.TryFrom<&types::CommandEvent> impl that parses args into Eco.EcoHandler trait with async methods (pay, bal) and an __execute helper.Add the command type to your plugin’s #[plugin] attribute, and implement the generated handler trait for your plugin type:
use dragonfly_plugin::{command::Ctx, Command, Plugin};
#[derive(Plugin)]
#[plugin(
id = "rustic-economy",
name = "Rustic Economy",
version = "0.1.0",
api = "1.0.0",
commands(Eco)
)]
struct RusticEconomy {
// your state here, e.g. DB pools
}
impl EcoHandler for RusticEconomy {
async fn pay(&self, ctx: Ctx<'_>, amount: f64) {
// ...
let _ = ctx
.reply(format!("You paid yourself ${:.2}.", amount))
.await;
}
async fn bal(&self, ctx: Ctx<'_>) {
// ...
let _ = ctx.reply("Your balance is $0.00".to_string()).await;
}
}
The #[derive(Plugin)] macro then:
CommandRegistry impl that:
CommandEvents into your command types.EcoHandler implementation.Within 0.3.x the shape of the command API (Ctx, CommandRegistry, CommandParseError, and the Command derive attributes) is considered stable.
event::EventContext<'_, T> wraps each incoming event:
data: &T gives read‑only access.cancel().await marks the event as cancelled and immediately sends a response.set_message for ChatEvent) live in generated extensions.event::EventHandler is a trait with an async method per event type; you usually never write impl EventHandler by hand except inside an #[event_handler] block.You generally do not construct EventContext yourself; the runtime does it for you.
Use PluginRunner::run(plugin, addr) from your main function:
"tcp://127.0.0.1:50050" or "127.0.0.1:50050"."unix:///tmp/dragonfly_plugin.sock" or"/tmp/dragonfly_plugin.sock").On non‑Unix platforms, Unix socket addresses will return an error.
PluginRunner:
PluginHello) with your plugin ID, name, version, API version and commands.EventSubscriptions.Within the 0.3.x series we aim to keep:
Plugin, EventHandler, EventSubscriptions, CommandRegistry.#[plugin(...)], #[event_handler], #[derive(Command)], #[subcommand(...)]).Server helper method names and argument shapes.EventContext behavior for cancel, mutation helpers, and double‑send (panic in debug, log in release).We may still:
For details on how the code is generated and how to maintain it, see MAINTAINING.md.