# Maintaining the Plugin API
This document is for developers who need to update, extend, or maintain the `dragonfly-plugin` API. This is **not** for plugin _users_; that documentation is in the main `README.md`.
## The Golden Rule: Do Not Edit Generated Files
This entire API is code-generated from a "Single Source of Truth." **You must not edit the generated Rust files by hand.** Any manual changes will be overwritten.
The _only_ files you will ever edit manually are:
1. The `.proto` files (Phase 1).
2. The `xtask` generator code (Phase 3's _generator_).
3. The `dragonfly-plugin-macros` crate code.
4. The `src/*` code in case of BC changes or etc.
## The 3-Phase Generation Pipeline
Our architecture is a 3-phase pipeline. Understanding this flow is critical to maintaining the API.
---
### Phase 1: The Protocol (Source of Truth)
- **What it is:** The `.proto` files that define all our gRPC services, messages, and events.
- **Location:** `../proto` directory.
- **How to change:** This is the **only place** you should start a change. To add a new event (e.g., `PlayerSleepEvent`), you add it to the `EventEnvelope` message in the `.proto` file.
---
### Phase 2: The Raw Bindings (Intermediate)
- **What it is:** The "raw" Rust code (structs, enums, and gRPC clients) generated directly from the `.proto` files. This code is often ugly, un-idiomatic, and not user-friendly.
- **How it's made:** This code is generated by `buf` which uses `prost` (via the makefile).
- **How to update:** When you have made changes to the proto buf files run the buf tool via `make proto`
- **Warning:** **NEVER EDIT THESE FILES BY HAND.**
---
### Phase 3: The Friendly API (The `xtask` Generator)
- **What it is:** This is the **public-facing, idiomatic Rust API** that our users love. It includes:
- The `EventHandler` trait and its `async fn on_*` methods.
- The `Server` struct with its friendly helper methods (e.g., `server.send_chat(...)`).
- The `EventContext` struct and its helpers (`.cancel()`, mutation helpers like `.set_message()`).
- The `types::EventType` enum.
- **How it's made:** The `xtask` crate is a custom Rust program that **parses the output of Phase 2** (the prost Rust code in `src/generated/df.plugin.rs`) and generates this API via:
- `generate_actions.rs` → `server/helpers.rs`
- `generate_handlers.rs` → `event/handler.rs`
- `generate_mutations.rs` → `event/mutations.rs`
- **How to update:** You run the `xtask` crate from the terminal.
- **Warning:** **NEVER EDIT THESE FILES BY HAND.**
---
## How to add a new event
This is the most common maintenance task. Here is the complete workflow.
**Goal:** Add a new `PlayerSleepEvent`.
1. **Phase 1: Edit the Protocol**
- Open the relevant `.proto` file (e.g., `events.proto`).
- Add the `PlayerSleepEvent` message.
- Add `PlayerSleepEvent player_sleep = 15;` (or the next available ID) to your main `EventEnvelope`'s `payload` oneof.
2. **Phase 2: Generate the Raw Bindings**
- Run `make proto` to use the buf tool to generate all the needed `generated/*` files.
3. **Phase 3: Generate the Friendly API**
- Now that the raw types exist, we can generate the friendly API for them.
- `cd` into the `xtask` directory.
- Run `cargo run`.
- The `xtask` generator will see the new `PlayerSleep` variant and automatically:
- Add `async fn on_player_sleep(...)` to the `PluginEventHandler` trait.
- Add the `case PlayerSleep` arm to the `dispatch_event` function.
4. **Review and commit**
- You are done. Review the changes in `git`.
- You should see changes in:
1. The `.proto` file you edited.
2. The Phase 2 raw bindings file.
3. The Phase 3 friendly API files (e.g., `event_handler.rs`, `types.rs`).
- Commit all of them.
- **What xtask generates for events**
- Adds `async fn on_player_sleep(...)` (and similar) to the `EventHandler` trait in `event/handler.rs`.
- Adds the `PlayerSleep` arm to the `dispatch_event` function.
- Adds any required mutation helpers in `event/mutations.rs`.
## The `event_handler`, `Plugin`, and `Command` proc-macros
- **Crate:** `dragonfly-plugin-macro`
### `#[event_handler]`
- **Purpose:** Attached to an `impl EventHandler for MyPlugin` block.
- **How it works:** Scans the methods you define (`on_player_join`, `on_chat`, etc.) and generates an `impl EventSubscriptions for MyPlugin` that returns a `Vec<types::EventType>` with the corresponding variants.
- **Maintenance:** This macro is intentionally simple: it only cares about method names and does not need to change when new events are added. If a user types a non-existent event method, the trait mismatch causes a normal Rust compiler error.
### `#[derive(Plugin)]` + `#[plugin(...)]`
- **Purpose:** Implements the `Plugin` trait and wires the plugin metadata into the runtime.
- **Attributes:** `#[plugin(id = \"...\", name = \"...\", version = \"...\", api = \"...\", commands(Foo, Bar))]`.
- **How it works:**
- Generates `Plugin` trait methods based on the literals.
- Generates a `CommandRegistry` implementation that:
- Collects command specs from the listed command types.
- Dispatches `types::CommandEvent` into those command types.
### `#[derive(Command)]` + `#[subcommand(...)]`
- **Purpose:** Generates a strongly-typed command parser and handler trait for a struct/enum.
- **How it works:**
- Produces a `spec()` function returning `types::CommandSpec`.
- Implements `TryFrom<&types::CommandEvent>` using the SDK’s `parse_required_arg` / `parse_optional_arg` helpers.
- Generates an `XxxHandler` trait and an `__execute` method that calls into the appropriate handler method on the plugin.
- **Maintenance:** When adding new `ParamType` support (e.g., enums) or changing how arguments map from `String` → Rust types, update the command macro implementation in `macro/src/command/` (parse/model/codegen) to keep codegen consistent with the runtime helpers in `src/command.rs`.
## Testing xtask and macros
- **xtask tests:** The `xtask` crate has:
- Unit tests in `utils.rs` for AST helpers.
- Snapshot tests in the generator modules (`generate_actions.rs`, `generate_handlers.rs`, `generate_mutations.rs`) using `insta`, with snapshots under `xtask/src/snapshots/`.
- Edge-case tests that verify generators fail with clear error messages when prost structures are inconsistent (e.g., missing structs or payloads).
- **Macro tests:** The macro crate uses `trybuild` fixtures under `macro/tests/fixtures` to ensure:
- Basic usage of `#[derive(Plugin)]`, `#[event_handler]`, and `#[derive(Command)]` compiles against the public SDK.
- Any breaking change to macro signatures or expectations shows up as a compile error in these tests.