horizon-plugin-api

Crates.iohorizon-plugin-api
lib.rshorizon-plugin-api
version0.2.2
created_at2024-11-07 03:58:26.582799+00
updated_at2025-06-13 06:26:17.49922+00
descriptionAPI utils and shared types for Horizon plugins
homepage
repository
max_upload_size
id1439461
size40,592
Tristan Poland (Trident_For_U) (tristanpoland)

documentation

README

I'll explain how to implement and use the Horizon Plugin API in a project. The plugin API you've shared is a framework for building modular functionality for the Horizon game server.

Understanding the Plugin API

The Horizon Plugin API provides a way to extend the Horizon game server with custom functionality through plugins. Here's how it works:

  1. Each plugin is a separate Rust crate that implements specific traits
  2. The main server loads plugins dynamically at runtime
  3. Plugins can interact with each other and the core server

Implementing a Plugin

Let's create a simple plugin that adds chat functionality to the Horizon server.

Step 1: Create the plugin crate structure

First, create a new directory in the plugins folder:

mkdir -p plugins/chat_plugin/src
cd plugins/chat_plugin

Step 2: Create the Cargo.toml file

[package]
name = "chat_plugin"
version = "0.1.0"
edition = "2021"

[dependencies]
horizon-plugin-api = "0.2.0"
horizon_data_types = "0.4.0"
socketioxide = "0.15.1"
parking_lot = "0.12.3"
serde = "1.0.216"
serde_json = "1.0.134"

Step 3: Implement the plugin

Create src/lib.rs with the following code:

use horizon_data_types::Player;
use socketioxide::extract::SocketRef;
pub use horizon_plugin_api::{Plugin, Pluginstate, LoadedPlugin};
use parking_lot::RwLock;
use std::sync::Arc;
use std::collections::HashMap;
use serde_json::{json, Value};

// Define the plugin API trait
pub trait PluginAPI {
    fn player_joined(&self, socket: SocketRef, player: Arc<RwLock<horizon_data_types::Player>>);
}

// Define the plugin constructor trait
pub trait PluginConstruct {
    fn new(plugins: HashMap<String, (Pluginstate, Plugin)>) -> Plugin;
    fn get_structs(&self) -> Vec<&str>;
}

// Implement the constructor for the Plugin type
impl PluginConstruct for Plugin {
    fn new(_plugins: HashMap<String, (Pluginstate, Plugin)>) -> Plugin {
        println!("Chat plugin initialized!");
        Plugin {}
    }

    fn get_structs(&self) -> Vec<&str> {
        vec!["ChatMessage"]
    }
}

// Implement the API for the Plugin type
impl PluginAPI for Plugin {
    fn player_joined(&self, socket: SocketRef, player: Arc<RwLock<horizon_data_types::Player>>) {
        println!("Player joined chat system");
        setup_chat_listeners(socket, player);
    }
}

// Setup chat event listeners
fn setup_chat_listeners(socket: SocketRef, player: Arc<RwLock<Player>>) {
    // Handle incoming chat messages
    socket.on("chat_message", move |data: socketioxide::extract::Data<Value>, socket: SocketRef| {
        let message = data.0;
        
        // Get player name
        let player_name = player.read().get_name().unwrap_or("Unknown".to_string());
        
        // Create the message payload with sender info
        let payload = json!({
            "sender": player_name,
            "message": message["text"],
            "timestamp": chrono::Utc::now().timestamp()
        });
        
        // Broadcast the message to all clients
        socket.broadcast().emit("chat_broadcast", payload).ok();
        
        // Echo back to sender with confirmation
        socket.emit("chat_sent", json!({"success": true})).ok();
        
        println!("Chat message processed from {}", player_name);
    });
}

Using the Plugin in the Horizon Server

The plugin system automatically loads plugins from the plugins directory. Here's how the server interacts with the plugins:

  1. The server's PluginManager scans the plugins directory during startup
  2. It loads each plugin based on the Cargo.toml information
  3. Plugins are initialized with the new() method
  4. The server calls plugin methods like player_joined when appropriate

Testing the Plugin

To test our chat plugin:

  1. Build the plugin:
cd plugins/chat_plugin
cargo build
  1. Start the Horizon server, which will automatically load our plugin:
cd ../..
cargo run
  1. Connect clients to the server and test the chat functionality.

Advanced Plugin Interactions

Plugins can interact with each other through dependencies. For example, our chat plugin could depend on a permission plugin to check if users are allowed to send messages.

Implementing Inter-Plugin Communication:

// In chat_plugin/src/lib.rs:
impl PluginConstruct for Plugin {
    fn new(plugins: HashMap<String, (Pluginstate, Plugin)>) -> Plugin {
        // Get reference to permission plugin
        if let Some((_, permission_plugin)) = plugins.get("permission_plugin") {
            // Cast to the permission plugin's API type
            let permission_api = permission_plugin as &dyn permission_plugin::PluginAPI;
            
            // Use the permission plugin's functionality
            println!("Permission plugin is available: {}", 
                     permission_api.get_version());
        }
        
        Plugin {}
    }
    
    // Rest of implementation...
}

Best Practices for Plugin Development

  1. Keep plugins focused: Each plugin should do one thing well
  2. Minimize dependencies: Only depend on other plugins when necessary
  3. Handle errors gracefully: Don't crash the server if something goes wrong
  4. Document your API: Make it clear how other plugins can interact with yours
  5. Follow the event-driven model: Use the event system for most interactions
  6. Keep state minimal: Don't store large amounts of data in the plugin itself

Complete Project Structure

A complete Horizon project with plugins would look like:

horizon-project/
├── backend_api/         # Backend API implementation
├── plugin_api/          # Plugin API implementation
├── plugins/
│   ├── chat_plugin/     # Our chat plugin
│   ├── permission_plugin/
│   └── other_plugins/
├── server/              # Main server implementation
└── Cargo.toml           # Workspace configuration

The plugin system provides a clean, modular architecture that makes it easy to extend the Horizon server with new functionality without modifying the core server code.

Commit count: 0

cargo fmt