From 7e1300a9773bd2f84ecbcb3d548208dcfbd7218c Mon Sep 17 00:00:00 2001 From: RestartFU Date: Fri, 5 Dec 2025 18:31:55 -0500 Subject: [PATCH 1/2] chore: improve readme --- README.md | 278 +++++++----------------------------------------------- 1 file changed, 34 insertions(+), 244 deletions(-) diff --git a/README.md b/README.md index c21b0ca..4d93673 100644 --- a/README.md +++ b/README.md @@ -1,272 +1,62 @@ # Dragonfly Plugin System -A powerful, language-agnostic plugin system for [Dragonfly](https://github.com/df-mc/dragonfly) Minecraft Bedrock servers using gRPC and Protocol Buffers. +This repository hosts the gRPC bridge that lets [Dragonfly](https://github.com/df-mc/dragonfly) talk to plugins written in any language. The host ships as a normal Dragonfly process (`cmd/main.go`), while plugins live in their own processes and communicate over protobuf streams. -## Why Dragonfly's Plugin System? +## Highlights -| Benefit | Description | Use Case | -|---------|-------------|----------| -| 🌍 **Any Language** | Write plugins in JavaScript, TypeScript, PHP, Python, Rust, C++, or any language with gRPC support | Use the language your team knows best | -| πŸ’° **Sell Plugins** | Compile to binary (Rust, Go, C++) and distribute without source code | Create commercial plugins | -| πŸ”₯ **Hot Reload** | Edit JS/TS/PHP plugins and see changes instantly - no server restart needed | Develop and debug plugins in real-time | -| πŸ“± **Remote Control** | Plugins connect over gRPC - run them anywhere (phone app, cloud service, discord bot) | Build mobile admin apps | -| πŸ“¦ **Use Any Library** | Import npm packages on a Go server, use Python ML libraries, etc. | Leverage entire ecosystems | -| ⚑ **Zero Performance Impact** | Plugins run in separate processes - slow/heavy plugin code doesn't affect server TPS | Run intensive tasks without lag | -| πŸš€ **High Performance (COMING SOON)** | Optimized protobuf protocol with optional batching for low latency | Handle 100+ players with movement events | -| πŸ”’ **Sandboxing** | Control what plugins can access via gRPC permissions | Host untrusted plugins safely | +- Works with any language that has a gRPC client (TypeScript, PHP, Rust, Go, Python, C++, etc.). +- Plugins stay isolated from the server process, so bad code cannot tank TPS. +- Hot‑reload JS/TS/PHP examples for tight feedback loops, or ship compiled binaries when you need to distribute closed-source plugins. +- Uses a strict protobuf schema with generated types under `proto/` for consistent APIs across languages. +- Ships with ready‑to‑run samples plus automation (`make proto`, `make run`) for day-one productivity. -### Real-World Examples +## Prerequisites -```bash -# Hot reload: Edit plugin code while server is running -vim plugins/my-plugin.js # Make changes -# Changes apply immediately - no restart! - -# Remote plugin: Control server from your phone -# Plugin runs on your phone, connects to server over internet -phone-app β†’ [gRPC] β†’ Dragonfly Server - -# Binary plugin: Sell without source code -rustc plugin.rs --release # Compile to binary -# Distribute the binary - customers can't see your code -``` - -## Features - -- **Multi-Language Support**: Write plugins in JavaScript, TypeScript, PHP, Python, Rust, C++, or any language with gRPC support -- **Event-Driven Architecture**: Subscribe to specific events (player join, chat, block break, etc.) -- **Type Safety**: Generated types for TypeScript and other statically typed languages +- Go 1.22+ with `GOBIN` on your `PATH`. +- [buf](https://buf.build/docs/cli/installation/) and `protoc-gen-go` (`go install google.golang.org/protobuf/cmd/protoc-gen-go@latest`). -## Quick Start - -### 1. Clone the Repository +## Setup ```bash git clone https://github.com/secmc/plugin.git cd plugin +go mod download +make proto # generates Go + TS stubs via buf and the post-gen script ``` -### 2. Install Dependencies +## Running the Host ```bash -go mod download +make run ``` -### 3. Configure Plugins - -Edit `plugins/plugins.yaml`: +`make run` boots the host pointed at `cmd/plugins/plugins.yaml`. Each entry launches a plugin command, sets the handshake metadata, and defines how the plugin connects back (Unix socket or TCP loopback). Minimal entry: ```yaml plugins: - - id: my-plugin - name: My First Plugin - command: "node" - args: ["examples/plugins/node/hello.js"] - address: "127.0.0.1:50051" -``` - -### 4. Run the Server - -```bash -go run main.go -``` - -Your plugin will automatically connect and start receiving events! - -## Example Plugins - -We provide complete working examples in multiple languages: - -- **[TypeScript](examples/plugins/typescript/)** - Type-safe plugin with generated types (recommended for production) -- **[Node.js](examples/plugins/node/)** - Simple JavaScript plugin -- **[PHP](examples/plugins/php/)** - PHP plugin using gRPC extension - -See [examples/plugins/README.md](examples/plugins/README.md) for detailed documentation and more examples. - -## Creating Your First Plugin - -### Minimal Example (PHP) - -```php - ChannelCredentials::createInsecure(), -]); - -$stream = $client->EventStream(); - -try { - foreach ($stream->responses() as $message) { - // Handle handshake - if ($message->hasHello()) { - $hello = new \DF\Plugin\PluginToHost(); - $hello->setPluginId($pluginId); - $pluginHello = new \DF\Plugin\PluginHello(); - $pluginHello->setName('My Plugin'); - $pluginHello->setVersion('1.0.0'); - $pluginHello->setApiVersion($message->getHello()->getApiVersion()); - - // Register /mine command - $command = new \DF\Plugin\CommandSpec(); - $command->setName('/mine'); - $command->setDescription('Get mining boost'); - $pluginHello->setCommands([$command]); - $hello->setHello($pluginHello); - $stream->write($hello); - - // Subscribe to events - $sub = new \DF\Plugin\PluginToHost(); - $sub->setPluginId($pluginId); - $subscribe = new \DF\Plugin\EventSubscribe(); - $subscribe->setEvents([ - \DF\Plugin\EventType::PLAYER_JOIN, - \DF\Plugin\EventType::COMMAND, - \DF\Plugin\EventType::PLAYER_BLOCK_BREAK, - ]); - $sub->setSubscribe($subscribe); - $stream->write($sub); - continue; - } - - if ($message->hasEvent()) { - $event = $message->getEvent(); - - // Handle /mine command - if ($event->getType() === \DF\Plugin\EventType::COMMAND && $event->hasCommand()) { - $cmd = $event->getCommand(); - if ($cmd->getCommand() === 'mine') { - // Send message to player - $action = new \DF\Plugin\Action(); - $send = new \DF\Plugin\SendChatAction(); - $send->setTargetUuid($cmd->getPlayerUuid()); - $send->setMessage('Β§6⛏️ Mining boost activated! Break blocks for double drops!'); - $action->setSendChat($send); - $batch = new \DF\Plugin\ActionBatch(); - $batch->setActions([$action]); - $resp = new \DF\Plugin\PluginToHost(); - $resp->setPluginId($pluginId); - $resp->setActions($batch); - $stream->write($resp); - } - - // Acknowledge event - $result = new \DF\Plugin\EventResult(); - $result->setEventId($event->getEventId()); - $result->setCancel(false); - $resp = new \DF\Plugin\PluginToHost(); - $resp->setPluginId($pluginId); - $resp->setEventResult($result); - $stream->write($resp); - } - - // Handle block break with double drops - if ($event->getType() === 'BLOCK_BREAK' && $event->hasBlockBreak()) { - $blockBreak = $event->getBlockBreak(); - echo "[php] {$blockBreak->getName()} broke block at "; - echo "{$blockBreak->getX()},{$blockBreak->getY()},{$blockBreak->getZ()}\n"; - - // Give double drops for every 10th block (X coordinate % 10 == 0) - if ($blockBreak->getX() % 10 === 0) { - $drop = new \DF\Plugin\ItemStack(); - $drop->setName('minecraft:diamond'); - $drop->setCount(2); - $drop->setMeta(0); - - $mutation = new \DF\Plugin\BlockBreakMutation(); - $mutation->setDrops([$drop]); - $mutation->setXp(10); - - $result = new \DF\Plugin\EventResult(); - $result->setEventId($event->getEventId()); - $result->setBlockBreak($mutation); - $resp = new \DF\Plugin\PluginToHost(); - $resp->setPluginId($pluginId); - $resp->setEventResult($result); - $stream->write($resp); - } else { - // Acknowledge normally - $result = new \DF\Plugin\EventResult(); - $result->setEventId($event->getEventId()); - $result->setCancel(false); - $resp = new \DF\Plugin\PluginToHost(); - $resp->setPluginId($pluginId); - $resp->setEventResult($result); - $stream->write($resp); - } - } - } - } -} catch (Exception $e) { - echo "[php] Error: " . $e->getMessage() . "\n"; -} finally { - $stream->writesDone(); -} - -echo "[php] plugin connected to {$address}\n"; -``` - -## Project Structure - -``` -dragonfly-plugins/ -β”œβ”€β”€ dragonfly/ # Modified Dragonfly server with plugin support -β”œβ”€β”€ plugin/ # Plugin system core -β”‚ β”œβ”€β”€ proto/ # Protocol Buffer definitions -β”‚ β”œβ”€β”€ manager.go # Plugin lifecycle management -β”‚ └── README.md # Plugin system documentation -β”œβ”€β”€ examples/ -β”‚ └── plugins/ # Example plugins in various languages -β”œβ”€β”€ plugins/ -β”‚ └── plugins.yaml # Plugin configuration -└── main.go # Server entry point -``` - -## How It Works - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” gRPC Stream β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ β”‚ ←──────────────────────────→ β”‚ β”‚ -β”‚ Dragonfly β”‚ Events: JOIN, CHAT, etc. β”‚ Your Plugin β”‚ -β”‚ Server β”‚ Actions: TELEPORT, etc. β”‚ (Any Language) β”‚ -β”‚ (Go) β”‚ β”‚ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + - id: example-typescript + name: Example TypeScript Plugin + command: "npm" + args: ["run", "dev", "--prefix", "examples/plugins/typescript"] + address: "unix:///tmp/dragonfly_plugin.sock" ``` -1. **Server starts** and loads plugin configuration from `plugins/plugins.yaml` -2. **Plugin process launches** via configured command (e.g., `node plugin.js`) -3. **Handshake** occurs where plugin registers capabilities -4. **Plugin subscribes** to events it wants to receive -5. **Events flow** from server to plugin in real-time -6. **Plugin executes actions** by sending messages back to server +Keep plugin IDs kebab-case and match directory names so the examples and config stay in sync. -## Documentation +## Building Plugins -- **[Plugin Examples](examples/plugins/README.md)** - Complete guide to example plugins -- **[Plugin System](plugin/README.md)** - Core plugin system documentation -- **[Protocol Buffer Definitions](plugin/proto/types/plugin.proto)** - API reference -- **[Plugin Architecture](docs/plugin-architecture.md)** - Design documentation +1. Start from one of the language samples in `examples/plugins/`. +2. Generate protobuf types as needed (`make proto` already covers TS/Go; other languages can run `buf generate` with their own templates). +3. Point your plugin command + args at the new entry in `cmd/plugins/plugins.yaml`. +4. Implement the handshake: respond to the `Hello` message with plugin metadata, then send an `EventSubscribe` specifying which events you care about. +5. React to streamed events and reply with `ActionBatch` or `EventResult` messages. Because everything flows over gRPC, you can deploy the plugin on the same machine or talk over loopback TCP for remote control tools. +## Development Workflow -## Protobuf generation -to generate our protobuf types, you will need to install [buf](https://buf.build/docs/cli/installation/) and protoc-gen-go: - -```bash -# Install buf -# Follow instructions at https://buf.build/docs/cli/installation/ - -# Install protoc-gen-go -go install google.golang.org/protobuf/cmd/protoc-gen-go@latest -``` - -Then run: ```bash -make proto +make proto # regenerate protobuf artifacts + lint through buf +go test ./... # run all host-side suites +make run # launch Dragonfly with the sample plugin config +npm run dev --prefix examples/plugins/typescript # live dev loop for TS sample +examples/plugins/php/bin/php7/bin/php examples/plugins/php/src/HelloPlugin.php # PHP sample ``` From daeefaae0d3c057ca594c336c9d05ed0526978c2 Mon Sep 17 00:00:00 2001 From: RestartFU Date: Sat, 6 Dec 2025 09:23:41 -0500 Subject: [PATCH 2/2] Update README.md --- README.md | 119 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 80 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 4d93673..bde6de6 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,103 @@ # Dragonfly Plugin System -This repository hosts the gRPC bridge that lets [Dragonfly](https://github.com/df-mc/dragonfly) talk to plugins written in any language. The host ships as a normal Dragonfly process (`cmd/main.go`), while plugins live in their own processes and communicate over protobuf streams. +Write plugins for your Minecraft Bedrock server in whatever language you love. This gRPC bridge sits on top of the [Dragonfly](https://github.com/df-mc/dragonfly) server and lets external processes stream protobuf events and actions without touching the core runtime. -## Highlights +## Why Dragonfly Plugins? -- Works with any language that has a gRPC client (TypeScript, PHP, Rust, Go, Python, C++, etc.). -- Plugins stay isolated from the server process, so bad code cannot tank TPS. -- Hot‑reload JS/TS/PHP examples for tight feedback loops, or ship compiled binaries when you need to distribute closed-source plugins. -- Uses a strict protobuf schema with generated types under `proto/` for consistent APIs across languages. -- Ships with ready‑to‑run samples plus automation (`make proto`, `make run`) for day-one productivity. +| Benefit | Description | +| --- | --- | +| 🌍 **Any Language** | JavaScript, TypeScript, PHP, Python, Rust, C++, Goβ€”if it can speak gRPC, it can be a plugin. | +| πŸ’° **Sell Plugins** | Compile to a binary (Rust, Go, C++) and ship closed-source builds. | +| πŸ”₯ **Hot Reload** | Edit JS/TS/PHP plugins while the server runs; changes apply immediately. | +| πŸ“± **Remote Control** | Plugins connect over gRPC, so you can run them on your phone, a web app, or a remote service. | +| πŸ“¦ **Use Any Library** | Mix npm packages, Python ML libs, or anything else your runtime supports. | +| ⚑ **Zero Performance Impact** | Plugins live in separate processes, so heavy work never blocks Dragonfly’s TPS. | +| πŸš€ **High Performance (SOON)** | The protocol is optimized protobuf with room for batching. | +| πŸ”’ **Sandboxing** | Grant only the permissions each plugin needs over the gRPC interface. | -## Prerequisites - -- Go 1.22+ with `GOBIN` on your `PATH`. -- [buf](https://buf.build/docs/cli/installation/) and `protoc-gen-go` (`go install google.golang.org/protobuf/cmd/protoc-gen-go@latest`). - -## Setup +### Real-World Examples ```bash -git clone https://github.com/secmc/plugin.git -cd plugin -go mod download -make proto # generates Go + TS stubs via buf and the post-gen script -``` +# Hot reload: Edit plugin code while server is running +vim plugins/my-plugin.js # Make changes +# Changes apply immediately - no restart! -## Running the Host +# Remote plugin: Control server from your phone +# Plugin runs on your phone, connects to server over internet +phone-app β†’ [gRPC] β†’ Dragonfly Server -```bash -make run +# Binary plugin: Sell without source code +rustc plugin.rs --release # Compile to binary +# Distribute the binary - customers can't see your code ``` -`make run` boots the host pointed at `cmd/plugins/plugins.yaml`. Each entry launches a plugin command, sets the handshake metadata, and defines how the plugin connects back (Unix socket or TCP loopback). Minimal entry: +## Key Features + +- **Event-driven API**: Subscribe to joins, chat, commands, block events, and more. +- **Generated types**: Proto definitions live in `proto/types/` with generated Go + TypeScript stubs under `proto/generated/`. +- **Language samples**: TypeScript, Node, PHP, and more under `examples/plugins/` to kick-start new plugins. +- **Automation ready**: `make proto` (buf + scripts) and `make run` wire up the host for you. + +## Quick Start + +1. **Clone & bootstrap** + ```bash + git clone https://github.com/secmc/plugin.git + cd plugin + go mod download + make proto + ``` +2. **Configure a plugin** in `cmd/plugins/plugins.yaml`: + ```yaml + plugins: + - id: example-typescript + name: Example TypeScript Plugin + command: "npm" + args: ["run", "dev", "--prefix", "examples/plugins/typescript"] + address: "unix:///tmp/dragonfly_plugin.sock" + ``` +3. **Run the host** + ```bash + make run + ``` +4. **Iterate in your language** – edit the example plugin, or point the config at your own command/binary. + +## How It Works -```yaml -plugins: - - id: example-typescript - name: Example TypeScript Plugin - command: "npm" - args: ["run", "dev", "--prefix", "examples/plugins/typescript"] - address: "unix:///tmp/dragonfly_plugin.sock" +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” gRPC Stream β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ β”‚ ←──────────────────────────→ β”‚ β”‚ +β”‚ Dragonfly β”‚ Events: JOIN, CHAT, etc. β”‚ Your Plugin β”‚ +β”‚ Server (Go) β”‚ Actions: TELEPORT, etc. β”‚ (Any Language) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` -Keep plugin IDs kebab-case and match directory names so the examples and config stay in sync. +1. **Server starts** and loads plugin configuration from `cmd/plugins/plugins.yaml`. +2. **Plugin process launches** via the configured command (for example `node plugin.js`). +3. **Handshake** occurs where the plugin registers its metadata and commands. +4. **Plugin subscribes** to the events it wants. +5. **Events flow** from Dragonfly to the plugin in real time. +6. **Plugin executes actions** by sending protobuf messages back to the host. ## Building Plugins -1. Start from one of the language samples in `examples/plugins/`. -2. Generate protobuf types as needed (`make proto` already covers TS/Go; other languages can run `buf generate` with their own templates). -3. Point your plugin command + args at the new entry in `cmd/plugins/plugins.yaml`. -4. Implement the handshake: respond to the `Hello` message with plugin metadata, then send an `EventSubscribe` specifying which events you care about. -5. React to streamed events and reply with `ActionBatch` or `EventResult` messages. Because everything flows over gRPC, you can deploy the plugin on the same machine or talk over loopback TCP for remote control tools. +1. Copy an example from `examples/plugins/` or start fresh with `proto/types/plugin.proto`. +2. Run `make proto` (or `buf generate` with your template) to refresh client stubs. +3. Add your command + args + socket info to `cmd/plugins/plugins.yaml`. +4. Implement the handshake: reply to `PluginHello`, register commands, then send `EventSubscribe`. +5. Handle streamed events and reply with `ActionBatch` or `EventResult` messages. Because plugins speak gRPC, they can run locally, over loopback TCP, or on a remote machine. ## Development Workflow ```bash -make proto # regenerate protobuf artifacts + lint through buf -go test ./... # run all host-side suites -make run # launch Dragonfly with the sample plugin config -npm run dev --prefix examples/plugins/typescript # live dev loop for TS sample +make proto # regenerate protobuf artifacts + post-gen scripts +go test ./... # run all Go suites +make run # launch Dragonfly host with sample config +npm run dev --prefix examples/plugins/typescript # TypeScript live dev examples/plugins/php/bin/php7/bin/php examples/plugins/php/src/HelloPlugin.php # PHP sample ``` + +## Prerequisites + +- Go 1.22+ with `GOBIN` on your `PATH`. +- [buf](https://buf.build/docs/cli/installation/) and `protoc-gen-go` (`go install google.golang.org/protobuf/cmd/protoc-gen-go@latest`).