From bdb684bb42fc4b434a10b36da5285bba2846fa39 Mon Sep 17 00:00:00 2001 From: vyPal Date: Sun, 22 Dec 2024 18:57:11 +0100 Subject: [PATCH] refactoring, better event handling, new context functions --- pumpkin/src/main.rs | 4 +- pumpkin/src/plugin/api/context.rs | 57 +++++++++++-- pumpkin/src/plugin/api/types/player.rs | 79 ++++++++++++++++- pumpkin/src/plugin/mod.rs | 114 ++++++++++++++----------- 4 files changed, 192 insertions(+), 62 deletions(-) diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 830fff21..3d666355 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -179,7 +179,9 @@ async fn main() { let server = Arc::new(Server::new()); let mut ticker = Ticker::new(BASIC_CONFIG.tps); - PLUGIN_MANAGER.lock().await.load_plugins().unwrap(); + let mut loader_lock = PLUGIN_MANAGER.lock().await; + loader_lock.set_server(server.clone()); + loader_lock.load_plugins().unwrap(); log::info!("Started Server took {}ms", time.elapsed().as_millis()); log::info!("You now can connect to the server, Listening on {}", addr); diff --git a/pumpkin/src/plugin/api/context.rs b/pumpkin/src/plugin/api/context.rs index 66c44f6d..a575cdd1 100644 --- a/pumpkin/src/plugin/api/context.rs +++ b/pumpkin/src/plugin/api/context.rs @@ -1,27 +1,32 @@ -use std::{fs, path::Path}; +use std::{fs, path::Path, sync::Arc}; use tokio::sync::mpsc::{self, Sender}; -use super::PluginMetadata; +use crate::server::Server; + +use super::{ + types::player::{player_event_handler, PlayerEvent}, + PluginMetadata, +}; pub struct Context { metadata: PluginMetadata<'static>, - _channel: Sender, + channel: Sender, } impl Context { - pub fn new(metadata: PluginMetadata<'static>, channel: Sender) -> Context { - Context { - metadata, - _channel: channel, - } + #[must_use] + pub fn new(metadata: PluginMetadata<'static>, channel: Sender) -> Self { + Self { metadata, channel } } + #[must_use] pub fn get_logger(&self) -> Logger { Logger { plugin_name: self.metadata.name.to_string(), } } + #[must_use] pub fn get_data_folder(&self) -> String { let path = format!("./plugins/{}", self.metadata.name); if !Path::new(&path).exists() { @@ -29,6 +34,22 @@ impl Context { } path } + + pub async fn get_player_by_name( + &self, + player_name: String, + ) -> Result, String> { + let (send, recv) = oneshot::channel(); + let _ = self + .channel + .send(ContextAction::GetPlayerByName { + player_name, + response: send, + }) + .await; + recv.await.unwrap() + } + /* TODO: Implement when dispatcher is mutable pub async fn register_command(&self, tree: crate::command::tree::CommandTree<'static>) { self.channel.send(ContextAction::RegisterCommand(tree)).await; @@ -37,10 +58,15 @@ impl Context { pub enum ContextAction { // TODO: Implement when dispatcher is mutable + GetPlayerByName { + player_name: String, + response: oneshot::Sender, String>>, + }, } pub fn handle_context( metadata: PluginMetadata<'static>, /* , dispatcher: Arc> */ + server: Arc, ) -> Context { let (send, mut recv) = mpsc::channel(1); tokio::spawn(async move { @@ -49,6 +75,21 @@ pub fn handle_context( /* ContextAction::RegisterCommand(_tree) => { // TODO: Implement when dispatcher is mutable } */ + ContextAction::GetPlayerByName { + player_name, + response, + } => { + let player = server.get_player_by_name(&player_name).await; + if let Some(player) = player { + response + .send(Ok( + player_event_handler(server.clone(), player.clone()).await + )) + .unwrap(); + } else { + response.send(Err("Player not found".to_string())).unwrap(); + } + } } } }); diff --git a/pumpkin/src/plugin/api/types/player.rs b/pumpkin/src/plugin/api/types/player.rs index 7452ec2d..dd430f70 100644 --- a/pumpkin/src/plugin/api/types/player.rs +++ b/pumpkin/src/plugin/api/types/player.rs @@ -4,7 +4,7 @@ use pumpkin_core::text::TextComponent; use tokio::sync::mpsc; use uuid::Uuid; -use crate::entity::player::Player; +use crate::{entity::player::Player, server::Server}; pub enum PlayerEventAction<'a> { SendMessage { @@ -80,3 +80,80 @@ impl<'a> PlayerEvent<'a> { rx.await.unwrap(); } } + +pub async fn player_event_handler( + server: Arc, + player: Arc, +) -> PlayerEvent<'static> { + let (send, mut recv) = mpsc::channel(1); + let player_event = PlayerEvent::new(player.clone(), send); + let players_copy = server.get_all_players().await; + tokio::spawn(async move { + while let Some(action) = recv.recv().await { + match action { + PlayerEventAction::SendMessage { + message, + player_id, + response, + } => { + if let Some(player) = + players_copy.iter().find(|p| p.gameprofile.id == player_id) + { + player.send_system_message(&message).await; + } + response.send(()).unwrap(); + } + PlayerEventAction::Kick { + reason, + player_id, + response, + } => { + if let Some(player) = + players_copy.iter().find(|p| p.gameprofile.id == player_id) + { + player.kick(reason).await; + } + response.send(()).unwrap(); + } + PlayerEventAction::SetHealth { + health, + food, + saturation, + player_id, + response, + } => { + if let Some(player) = + players_copy.iter().find(|p| p.gameprofile.id == player_id) + { + player.set_health(health, food, saturation).await; + } + response.send(()).unwrap(); + } + PlayerEventAction::Kill { + player_id, + response, + } => { + if let Some(player) = + players_copy.iter().find(|p| p.gameprofile.id == player_id) + { + player.kill().await; + } + response.send(()).unwrap(); + } + PlayerEventAction::SetGameMode { + game_mode, + player_id, + response, + } => { + if let Some(player) = + players_copy.iter().find(|p| p.gameprofile.id == player_id) + { + player.set_gamemode(game_mode).await; + } + response.send(()).unwrap(); + } + } + } + }); + player_event +} diff --git a/pumpkin/src/plugin/mod.rs b/pumpkin/src/plugin/mod.rs index b4786b56..f53b5a1f 100644 --- a/pumpkin/src/plugin/mod.rs +++ b/pumpkin/src/plugin/mod.rs @@ -1,9 +1,9 @@ pub mod api; pub use api::*; -use std::{any::Any, fs, path::Path, sync::Arc}; +use std::{any::Any, fs, future::Future, path::Path, pin::Pin, sync::Arc}; -use crate::command::dispatcher::CommandDispatcher; +use crate::server::Server; type PluginData = ( PluginMetadata<'static>, @@ -14,7 +14,7 @@ type PluginData = ( pub struct PluginManager { plugins: Vec, - command_dispatcher: Option>>, + server: Option>, } impl Default for PluginManager { @@ -23,17 +23,59 @@ impl Default for PluginManager { } } +const EVENT_PLAYER_JOIN: &str = "player_join"; +const EVENT_PLAYER_LEAVE: &str = "player_leave"; + +type EventResult = Result; +type EventFuture<'a> = Pin + Send + 'a>>; + +fn create_default_handler() -> EventFuture<'static> { + Box::pin(async { Ok(false) }) +} + +fn handle_player_event<'a>( + hooks: &'a mut Box, + context: &'a Context, + event: &'a (dyn Any + Send + Sync), + handler: impl Fn( + &'a mut Box, + &'a Context, + &'a types::player::PlayerEvent, + ) -> EventFuture<'a>, +) -> EventFuture<'a> { + event + .downcast_ref::() + .map_or_else(|| create_default_handler(), |e| handler(hooks, context, e)) +} + +fn match_event<'a>( + event_name: &str, + hooks: &'a mut Box, + context: &'a Context, + event: &'a (dyn Any + Send + Sync), +) -> EventFuture<'a> { + match event_name { + EVENT_PLAYER_JOIN => { + handle_player_event(hooks, context, event, |h, c, e| h.on_player_join(c, e)) + } + EVENT_PLAYER_LEAVE => { + handle_player_event(hooks, context, event, |h, c, e| h.on_player_leave(c, e)) + } + _ => create_default_handler(), + } +} + impl PluginManager { #[must_use] pub fn new() -> Self { - PluginManager { + Self { plugins: vec![], - command_dispatcher: None, + server: None, } } - pub fn set_command_dispatcher(&mut self, dispatcher: Arc>) { - self.command_dispatcher = Some(dispatcher); + pub fn set_server(&mut self, server: Arc) { + self.server = Some(server); } pub fn load_plugins(&mut self) -> Result<(), String> { @@ -58,8 +100,11 @@ impl PluginManager { let metadata: &PluginMetadata = unsafe { &**library.get::<*const PluginMetadata>(b"METADATA").unwrap() }; - // let dispatcher = self.command_dispatcher.clone().expect("Command dispatcher not set").clone(); - let context = handle_context(metadata.clone() /* , dispatcher */); + // The chance that this will panic is non-existent, but just in case + let context = handle_context( + metadata.clone(), /* , dispatcher */ + self.server.clone().expect("Server not set"), + ); let mut plugin_box = plugin_fn(); let res = plugin_box.on_load(&context); let mut loaded = true; @@ -102,7 +147,10 @@ impl PluginManager { }; if let Some(matching_event) = registered_events.iter().find(|e| e.name == event_name) { - let context = handle_context(metadata.clone() /* , dispatcher.clone() */); + let context = handle_context( + metadata.clone(), /* , dispatcher.clone() */ + self.server.clone().expect("Server not set"), + ); if matching_event.blocking { blocking_hooks.push((context, hooks)); @@ -135,55 +183,17 @@ impl PluginManager { let event = event as &(dyn Any + Sync + Send); for (context, hooks) in blocking_hooks { - let r = match event_name { - "player_join" => { - if let Some(event) = event.downcast_ref::() { - hooks.on_player_join(&context, event) - } else { - Box::pin(async { Ok(false) }) - } - } - "player_leave" => { - if let Some(event) = event.downcast_ref::() { - hooks.on_player_leave(&context, event) - } else { - Box::pin(async { Ok(false) }) - } - } - _ => Box::pin(async { Ok(false) }), - }; - match r.await { + match match_event(event_name, hooks, &context, event).await { Ok(true) => return true, - Err(e) => { - log::error!("Error in plugin: {}", e); - } + Err(e) => log::error!("Error in plugin: {}", e), _ => {} } } for (context, hooks) in non_blocking_hooks { - let r = match event_name { - "player_join" => { - if let Some(event) = event.downcast_ref::() { - hooks.on_player_join(&context, event) - } else { - Box::pin(async { Ok(false) }) - } - } - "player_leave" => { - if let Some(event) = event.downcast_ref::() { - hooks.on_player_leave(&context, event) - } else { - Box::pin(async { Ok(false) }) - } - } - _ => Box::pin(async { Ok(false) }), - }; - match r.await { + match match_event(event_name, hooks, &context, event).await { Ok(true) => continue, - Err(e) => { - log::error!("Error in plugin: {}", e); - } + Err(e) => log::error!("Error in plugin: {}", e), _ => {} } }