Skip to content

Commit 173f10a

Browse files
RestartFURestartFU
andauthored
chore: improve readme (#32)
Co-authored-by: RestartFU <me@restartfu.com>
1 parent 342251e commit 173f10a

1 file changed

Lines changed: 63 additions & 232 deletions

File tree

README.md

Lines changed: 63 additions & 232 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
# Dragonfly Plugin System
22

3-
A powerful, language-agnostic plugin system for [Dragonfly](https://github.com/df-mc/dragonfly) Minecraft Bedrock servers using gRPC and Protocol Buffers.
3+
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.
44

5-
## Why Dragonfly's Plugin System?
5+
## Why Dragonfly Plugins?
66

7-
| Benefit | Description | Use Case |
8-
|---------|-------------|----------|
9-
| 🌍 **Any Language** | Write plugins in JavaScript, TypeScript, PHP, Python, Rust, C++, or any language with gRPC support | Use the language your team knows best |
10-
| 💰 **Sell Plugins** | Compile to binary (Rust, Go, C++) and distribute without source code | Create commercial plugins |
11-
| 🔥 **Hot Reload** | Edit JS/TS/PHP plugins and see changes instantly - no server restart needed | Develop and debug plugins in real-time |
12-
| 📱 **Remote Control** | Plugins connect over gRPC - run them anywhere (phone app, cloud service, discord bot) | Build mobile admin apps |
13-
| 📦 **Use Any Library** | Import npm packages on a Go server, use Python ML libraries, etc. | Leverage entire ecosystems |
14-
|**Zero Performance Impact** | Plugins run in separate processes - slow/heavy plugin code doesn't affect server TPS | Run intensive tasks without lag |
15-
| 🚀 **High Performance (COMING SOON)** | Optimized protobuf protocol with optional batching for low latency | Handle 100+ players with movement events |
16-
| 🔒 **Sandboxing** | Control what plugins can access via gRPC permissions | Host untrusted plugins safely |
7+
| Benefit | Description |
8+
| --- | --- |
9+
| 🌍 **Any Language** | JavaScript, TypeScript, PHP, Python, Rust, C++, Go—if it can speak gRPC, it can be a plugin. |
10+
| 💰 **Sell Plugins** | Compile to a binary (Rust, Go, C++) and ship closed-source builds. |
11+
| 🔥 **Hot Reload** | Edit JS/TS/PHP plugins while the server runs; changes apply immediately. |
12+
| 📱 **Remote Control** | Plugins connect over gRPC, so you can run them on your phone, a web app, or a remote service. |
13+
| 📦 **Use Any Library** | Mix npm packages, Python ML libs, or anything else your runtime supports. |
14+
|**Zero Performance Impact** | Plugins live in separate processes, so heavy work never blocks Dragonfly’s TPS. |
15+
| 🚀 **High Performance (SOON)** | The protocol is optimized protobuf with room for batching. |
16+
| 🔒 **Sandboxing** | Grant only the permissions each plugin needs over the gRPC interface. |
1717

1818
### Real-World Examples
1919

@@ -31,242 +31,73 @@ rustc plugin.rs --release # Compile to binary
3131
# Distribute the binary - customers can't see your code
3232
```
3333

34-
## Features
34+
## Key Features
3535

36-
- **Multi-Language Support**: Write plugins in JavaScript, TypeScript, PHP, Python, Rust, C++, or any language with gRPC support
37-
- **Event-Driven Architecture**: Subscribe to specific events (player join, chat, block break, etc.)
38-
- **Type Safety**: Generated types for TypeScript and other statically typed languages
36+
- **Event-driven API**: Subscribe to joins, chat, commands, block events, and more.
37+
- **Generated types**: Proto definitions live in `proto/types/` with generated Go + TypeScript stubs under `proto/generated/`.
38+
- **Language samples**: TypeScript, Node, PHP, and more under `examples/plugins/` to kick-start new plugins.
39+
- **Automation ready**: `make proto` (buf + scripts) and `make run` wire up the host for you.
3940

4041
## Quick Start
4142

42-
### 1. Clone the Repository
43-
44-
```bash
45-
git clone https://github.com/secmc/plugin.git
46-
cd plugin
47-
```
48-
49-
### 2. Install Dependencies
50-
51-
```bash
52-
go mod download
53-
```
54-
55-
### 3. Configure Plugins
56-
57-
Edit `plugins/plugins.yaml`:
58-
59-
```yaml
60-
plugins:
61-
- id: my-plugin
62-
name: My First Plugin
63-
command: "node"
64-
args: ["examples/plugins/node/hello.js"]
65-
address: "127.0.0.1:50051"
66-
```
67-
68-
### 4. Run the Server
69-
70-
```bash
71-
go run main.go
72-
```
73-
74-
Your plugin will automatically connect and start receiving events!
75-
76-
## Example Plugins
77-
78-
We provide complete working examples in multiple languages:
79-
80-
- **[TypeScript](examples/plugins/typescript/)** - Type-safe plugin with generated types (recommended for production)
81-
- **[Node.js](examples/plugins/node/)** - Simple JavaScript plugin
82-
- **[PHP](examples/plugins/php/)** - PHP plugin using gRPC extension
83-
84-
See [examples/plugins/README.md](examples/plugins/README.md) for detailed documentation and more examples.
85-
86-
## Creating Your First Plugin
87-
88-
### Minimal Example (PHP)
89-
90-
```php
91-
<?php
92-
// Example plugin showing command handling and block break event modification
93-
require_once __DIR__ . '/vendor/autoload.php';
94-
95-
use Grpc\ChannelCredentials;
96-
97-
$pluginId = 'my-plugin';
98-
$address = '127.0.0.1:50051';
99-
100-
$client = new \Df\Plugin\PluginClient($address, [
101-
'credentials' => ChannelCredentials::createInsecure(),
102-
]);
103-
104-
$stream = $client->EventStream();
105-
106-
try {
107-
foreach ($stream->responses() as $message) {
108-
// Handle handshake
109-
if ($message->hasHello()) {
110-
$hello = new \DF\Plugin\PluginToHost();
111-
$hello->setPluginId($pluginId);
112-
$pluginHello = new \DF\Plugin\PluginHello();
113-
$pluginHello->setName('My Plugin');
114-
$pluginHello->setVersion('1.0.0');
115-
$pluginHello->setApiVersion($message->getHello()->getApiVersion());
116-
117-
// Register /mine command
118-
$command = new \DF\Plugin\CommandSpec();
119-
$command->setName('/mine');
120-
$command->setDescription('Get mining boost');
121-
$pluginHello->setCommands([$command]);
122-
$hello->setHello($pluginHello);
123-
$stream->write($hello);
124-
125-
// Subscribe to events
126-
$sub = new \DF\Plugin\PluginToHost();
127-
$sub->setPluginId($pluginId);
128-
$subscribe = new \DF\Plugin\EventSubscribe();
129-
$subscribe->setEvents([
130-
\DF\Plugin\EventType::PLAYER_JOIN,
131-
\DF\Plugin\EventType::COMMAND,
132-
\DF\Plugin\EventType::PLAYER_BLOCK_BREAK,
133-
]);
134-
$sub->setSubscribe($subscribe);
135-
$stream->write($sub);
136-
continue;
137-
}
138-
139-
if ($message->hasEvent()) {
140-
$event = $message->getEvent();
141-
142-
// Handle /mine command
143-
if ($event->getType() === \DF\Plugin\EventType::COMMAND && $event->hasCommand()) {
144-
$cmd = $event->getCommand();
145-
if ($cmd->getCommand() === 'mine') {
146-
// Send message to player
147-
$action = new \DF\Plugin\Action();
148-
$send = new \DF\Plugin\SendChatAction();
149-
$send->setTargetUuid($cmd->getPlayerUuid());
150-
$send->setMessage('§6⛏️ Mining boost activated! Break blocks for double drops!');
151-
$action->setSendChat($send);
152-
$batch = new \DF\Plugin\ActionBatch();
153-
$batch->setActions([$action]);
154-
$resp = new \DF\Plugin\PluginToHost();
155-
$resp->setPluginId($pluginId);
156-
$resp->setActions($batch);
157-
$stream->write($resp);
158-
}
159-
160-
// Acknowledge event
161-
$result = new \DF\Plugin\EventResult();
162-
$result->setEventId($event->getEventId());
163-
$result->setCancel(false);
164-
$resp = new \DF\Plugin\PluginToHost();
165-
$resp->setPluginId($pluginId);
166-
$resp->setEventResult($result);
167-
$stream->write($resp);
168-
}
169-
170-
// Handle block break with double drops
171-
if ($event->getType() === 'BLOCK_BREAK' && $event->hasBlockBreak()) {
172-
$blockBreak = $event->getBlockBreak();
173-
echo "[php] {$blockBreak->getName()} broke block at ";
174-
echo "{$blockBreak->getX()},{$blockBreak->getY()},{$blockBreak->getZ()}\n";
175-
176-
// Give double drops for every 10th block (X coordinate % 10 == 0)
177-
if ($blockBreak->getX() % 10 === 0) {
178-
$drop = new \DF\Plugin\ItemStack();
179-
$drop->setName('minecraft:diamond');
180-
$drop->setCount(2);
181-
$drop->setMeta(0);
182-
183-
$mutation = new \DF\Plugin\BlockBreakMutation();
184-
$mutation->setDrops([$drop]);
185-
$mutation->setXp(10);
186-
187-
$result = new \DF\Plugin\EventResult();
188-
$result->setEventId($event->getEventId());
189-
$result->setBlockBreak($mutation);
190-
$resp = new \DF\Plugin\PluginToHost();
191-
$resp->setPluginId($pluginId);
192-
$resp->setEventResult($result);
193-
$stream->write($resp);
194-
} else {
195-
// Acknowledge normally
196-
$result = new \DF\Plugin\EventResult();
197-
$result->setEventId($event->getEventId());
198-
$result->setCancel(false);
199-
$resp = new \DF\Plugin\PluginToHost();
200-
$resp->setPluginId($pluginId);
201-
$resp->setEventResult($result);
202-
$stream->write($resp);
203-
}
204-
}
205-
}
206-
}
207-
} catch (Exception $e) {
208-
echo "[php] Error: " . $e->getMessage() . "\n";
209-
} finally {
210-
$stream->writesDone();
211-
}
212-
213-
echo "[php] plugin connected to {$address}\n";
214-
```
215-
216-
## Project Structure
217-
218-
```
219-
dragonfly-plugins/
220-
├── dragonfly/ # Modified Dragonfly server with plugin support
221-
├── plugin/ # Plugin system core
222-
│ ├── proto/ # Protocol Buffer definitions
223-
│ ├── manager.go # Plugin lifecycle management
224-
│ └── README.md # Plugin system documentation
225-
├── examples/
226-
│ └── plugins/ # Example plugins in various languages
227-
├── plugins/
228-
│ └── plugins.yaml # Plugin configuration
229-
└── main.go # Server entry point
230-
```
43+
1. **Clone & bootstrap**
44+
```bash
45+
git clone https://github.com/secmc/plugin.git
46+
cd plugin
47+
go mod download
48+
make proto
49+
```
50+
2. **Configure a plugin** in `cmd/plugins/plugins.yaml`:
51+
```yaml
52+
plugins:
53+
- id: example-typescript
54+
name: Example TypeScript Plugin
55+
command: "npm"
56+
args: ["run", "dev", "--prefix", "examples/plugins/typescript"]
57+
address: "unix:///tmp/dragonfly_plugin.sock"
58+
```
59+
3. **Run the host**
60+
```bash
61+
make run
62+
```
63+
4. **Iterate in your language** – edit the example plugin, or point the config at your own command/binary.
23164

23265
## How It Works
23366

23467
```
23568
┌─────────────────┐ gRPC Stream ┌──────────────────┐
236-
│ │ ←──────────────────────────→ │ │
69+
│ │ ←──────────────────────────→ │ │
23770
│ Dragonfly │ Events: JOIN, CHAT, etc. │ Your Plugin │
238-
│ Server │ Actions: TELEPORT, etc. │ (Any Language) │
239-
│ (Go) │ │ │
240-
└─────────────────┘ └──────────────────┘
71+
│ Server (Go) │ Actions: TELEPORT, etc. │ (Any Language) │
72+
└─────────────────┘ └──────────────────┘
24173
```
24274

243-
1. **Server starts** and loads plugin configuration from `plugins/plugins.yaml`
244-
2. **Plugin process launches** via configured command (e.g., `node plugin.js`)
245-
3. **Handshake** occurs where plugin registers capabilities
246-
4. **Plugin subscribes** to events it wants to receive
247-
5. **Events flow** from server to plugin in real-time
248-
6. **Plugin executes actions** by sending messages back to server
75+
1. **Server starts** and loads plugin configuration from `cmd/plugins/plugins.yaml`.
76+
2. **Plugin process launches** via the configured command (for example `node plugin.js`).
77+
3. **Handshake** occurs where the plugin registers its metadata and commands.
78+
4. **Plugin subscribes** to the events it wants.
79+
5. **Events flow** from Dragonfly to the plugin in real time.
80+
6. **Plugin executes actions** by sending protobuf messages back to the host.
24981

250-
## Documentation
82+
## Building Plugins
25183

252-
- **[Plugin Examples](examples/plugins/README.md)** - Complete guide to example plugins
253-
- **[Plugin System](plugin/README.md)** - Core plugin system documentation
254-
- **[Protocol Buffer Definitions](plugin/proto/types/plugin.proto)** - API reference
255-
- **[Plugin Architecture](docs/plugin-architecture.md)** - Design documentation
84+
1. Copy an example from `examples/plugins/` or start fresh with `proto/types/plugin.proto`.
85+
2. Run `make proto` (or `buf generate` with your template) to refresh client stubs.
86+
3. Add your command + args + socket info to `cmd/plugins/plugins.yaml`.
87+
4. Implement the handshake: reply to `PluginHello`, register commands, then send `EventSubscribe`.
88+
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.
25689

257-
258-
## Protobuf generation
259-
to generate our protobuf types, you will need to install [buf](https://buf.build/docs/cli/installation/) and protoc-gen-go:
90+
## Development Workflow
26091

26192
```bash
262-
# Install buf
263-
# Follow instructions at https://buf.build/docs/cli/installation/
264-
265-
# Install protoc-gen-go
266-
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
93+
make proto # regenerate protobuf artifacts + post-gen scripts
94+
go test ./... # run all Go suites
95+
make run # launch Dragonfly host with sample config
96+
npm run dev --prefix examples/plugins/typescript # TypeScript live dev
97+
examples/plugins/php/bin/php7/bin/php examples/plugins/php/src/HelloPlugin.php # PHP sample
26798
```
26899

269-
Then run:
270-
```bash
271-
make proto
272-
```
100+
## Prerequisites
101+
102+
- Go 1.22+ with `GOBIN` on your `PATH`.
103+
- [buf](https://buf.build/docs/cli/installation/) and `protoc-gen-go` (`go install google.golang.org/protobuf/cmd/protoc-gen-go@latest`).

0 commit comments

Comments
 (0)