Simple plugin server example

This commit is contained in:
2025-12-11 22:36:29 +01:00
parent f49e5aa108
commit d2fe5d9d62
5 changed files with 119 additions and 3 deletions
+2
View File
@@ -1,11 +1,13 @@
#[macro_export] #[macro_export]
macro_rules! logger { macro_rules! logger {
(const $i:ident $name:expr) => { (const $i:ident $name:expr) => {
#[allow(unused)]
pub const $i: $crate::once_cell::sync::Lazy<$crate::utils::logger::Logger> = pub const $i: $crate::once_cell::sync::Lazy<$crate::utils::logger::Logger> =
$crate::once_cell::sync::Lazy::new(|| $crate::utils::logger::Logger::new($name)); $crate::once_cell::sync::Lazy::new(|| $crate::utils::logger::Logger::new($name));
}; };
($i:ident $name:expr) => { ($i:ident $name:expr) => {
#[allow(unused)]
pub const $i: $crate::once_cell::sync::Lazy<$crate::utils::logger::Logger> = pub const $i: $crate::once_cell::sync::Lazy<$crate::utils::logger::Logger> =
$crate::once_cell::sync::Lazy::new(|| $crate::utils::logger::Logger::new($name)); $crate::once_cell::sync::Lazy::new(|| $crate::utils::logger::Logger::new($name));
}; };
+1
View File
@@ -1,4 +1,5 @@
mod macros; mod macros;
mod plugin;
mod requests; mod requests;
mod server; mod server;
mod types; mod types;
+90
View File
@@ -0,0 +1,90 @@
use crate::{logger, plugin::Plugin};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fs,
io::Read,
net::{TcpListener, TcpStream},
path::Path,
process::Command,
sync::{Arc, Mutex},
};
logger!(LOGGER "Plugin Loader");
#[derive(Debug, Serialize, Deserialize)]
pub struct PluginJson {
pub id: String,
pub version: String,
pub supported_versions: Vec<String>,
pub file: String,
pub args: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PluginHandshake {
pub id: String,
}
pub struct PluginLoader {
plugins: Vec<Plugin>,
plugin_clients: Arc<Mutex<HashMap<String, TcpStream>>>,
}
impl PluginLoader {
pub fn new() -> Self {
Self {
plugins: Vec::new(),
plugin_clients: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn load(&self, json_path: &Path) -> Plugin {
let json_string = fs::read_to_string(json_path).unwrap();
let plugin_json: PluginJson = serde_json::from_str(&json_string).unwrap();
LOGGER.info(format!("Loading {}", plugin_json.id));
Command::new(plugin_json.file)
.args(plugin_json.args)
.current_dir("plugins")
.spawn()
.unwrap();
while self
.plugin_clients
.lock()
.unwrap()
.get(&plugin_json.id)
.is_none()
{}
Plugin(
self.plugin_clients
.lock()
.unwrap()
.get(&plugin_json.id)
.unwrap()
.try_clone()
.unwrap(),
)
}
pub fn start_server(&self) {
let plugin_clients = self.plugin_clients.clone();
std::thread::spawn(move || {
let listener = TcpListener::bind("0.0.0.0:7243").unwrap();
LOGGER.info("Server listening at 0.0.0.0:7243");
for stream in listener.incoming() {
let mut stream = stream.unwrap();
let mut s = String::new();
stream.read_to_string(&mut s).unwrap();
let plugin_handshake: PluginHandshake = serde_json::from_str(&s).unwrap();
plugin_clients
.lock()
.unwrap()
.insert(plugin_handshake.id, stream);
}
});
}
}
+7
View File
@@ -0,0 +1,7 @@
use std::net::TcpStream;
pub mod loader;
pub struct Plugin(TcpStream);
impl Plugin {}
+19 -3
View File
@@ -1,12 +1,15 @@
use std::{ use std::{
collections::HashSet, collections::HashSet,
fs,
net::{TcpListener, TcpStream}, net::{TcpListener, TcpStream},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use crate::{ use crate::{
logger, types, logger,
plugin::loader::PluginLoader,
types,
utils::{self, client::Client}, utils::{self, client::Client},
}; };
@@ -25,6 +28,7 @@ pub struct Server {
pub config: ServerConfig, pub config: ServerConfig,
pub clients: Mutex<HashSet<Client>>, pub clients: Mutex<HashSet<Client>>,
pub db: utils::database::Database, pub db: utils::database::Database,
pub plugin_loader: PluginLoader,
} }
impl Default for ServerConfig { impl Default for ServerConfig {
@@ -32,8 +36,8 @@ impl Default for ServerConfig {
Self { Self {
port: 7080, port: 7080,
server_name: format!("Server Name"), server_name: format!("Server Name"),
server_id: format!("offline-server"), server_id: format!("important"),
server_key: format!(""), server_key: format!("important"),
channels: Vec::new(), channels: Vec::new(),
} }
} }
@@ -58,13 +62,25 @@ impl Server {
root: root.to_path_buf(), root: root.to_path_buf(),
config, config,
clients: Mutex::new(HashSet::new()), clients: Mutex::new(HashSet::new()),
plugin_loader: PluginLoader::new(),
}) })
} }
pub fn run(self: &Arc<Self>) -> crate::Result<()> { pub fn run(self: &Arc<Self>) -> crate::Result<()> {
// Load plugins // Load plugins
Self::LOGGER.info("Loading plugins");
for entry in fs::read_dir(self.root.join("plugins"))? {
let entry = entry?;
let path = entry.path();
if path.extension().and_then(|s| s.to_str()) == Some("json") {
self.plugin_loader.load(&path);
}
}
Self::LOGGER.info("Plugins loaded");
// Initialize plugins // Initialize plugins
Self::LOGGER.info("Initializing plugins");
// Start server // Start server
let listener = TcpListener::bind(format!("0.0.0.0:{}", self.config.port))?; let listener = TcpListener::bind(format!("0.0.0.0:{}", self.config.port))?;