Skip to content

Commit

Permalink
refactoring, better event handling, new context functions
Browse files Browse the repository at this point in the history
  • Loading branch information
vyPal committed Dec 22, 2024
1 parent 237c280 commit bdb684b
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 62 deletions.
4 changes: 3 additions & 1 deletion pumpkin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
57 changes: 49 additions & 8 deletions pumpkin/src/plugin/api/context.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,55 @@
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<ContextAction>,
channel: Sender<ContextAction>,
}
impl Context {
pub fn new(metadata: PluginMetadata<'static>, channel: Sender<ContextAction>) -> Context {
Context {
metadata,
_channel: channel,
}
#[must_use]
pub fn new(metadata: PluginMetadata<'static>, channel: Sender<ContextAction>) -> 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() {
fs::create_dir_all(&path).unwrap();
}
path
}

pub async fn get_player_by_name(
&self,
player_name: String,
) -> Result<PlayerEvent<'static>, 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;
Expand All @@ -37,10 +58,15 @@ impl Context {

pub enum ContextAction {
// TODO: Implement when dispatcher is mutable
GetPlayerByName {
player_name: String,
response: oneshot::Sender<Result<PlayerEvent<'static>, String>>,
},
}

pub fn handle_context(
metadata: PluginMetadata<'static>, /* , dispatcher: Arc<CommandDispatcher<'static>> */
server: Arc<Server>,
) -> Context {
let (send, mut recv) = mpsc::channel(1);
tokio::spawn(async move {
Expand All @@ -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();
}
}
}
}
});
Expand Down
79 changes: 78 additions & 1 deletion pumpkin/src/plugin/api/types/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -80,3 +80,80 @@ impl<'a> PlayerEvent<'a> {
rx.await.unwrap();
}
}

pub async fn player_event_handler(
server: Arc<Server>,
player: Arc<Player>,
) -> 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
}
114 changes: 62 additions & 52 deletions pumpkin/src/plugin/mod.rs
Original file line number Diff line number Diff line change
@@ -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>,
Expand All @@ -14,7 +14,7 @@ type PluginData = (

pub struct PluginManager {
plugins: Vec<PluginData>,
command_dispatcher: Option<Arc<CommandDispatcher<'static>>>,
server: Option<Arc<Server>>,
}

impl Default for PluginManager {
Expand All @@ -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<bool, String>;
type EventFuture<'a> = Pin<Box<dyn Future<Output = EventResult> + Send + 'a>>;

fn create_default_handler() -> EventFuture<'static> {
Box::pin(async { Ok(false) })
}

fn handle_player_event<'a>(
hooks: &'a mut Box<dyn Plugin>,
context: &'a Context,
event: &'a (dyn Any + Send + Sync),
handler: impl Fn(
&'a mut Box<dyn Plugin>,
&'a Context,
&'a types::player::PlayerEvent,
) -> EventFuture<'a>,
) -> EventFuture<'a> {
event
.downcast_ref::<types::player::PlayerEvent>()
.map_or_else(|| create_default_handler(), |e| handler(hooks, context, e))
}

fn match_event<'a>(
event_name: &str,
hooks: &'a mut Box<dyn Plugin>,
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<CommandDispatcher<'static>>) {
self.command_dispatcher = Some(dispatcher);
pub fn set_server(&mut self, server: Arc<Server>) {
self.server = Some(server);
}

pub fn load_plugins(&mut self) -> Result<(), String> {
Expand All @@ -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;
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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::<types::player::PlayerEvent>() {
hooks.on_player_join(&context, event)
} else {
Box::pin(async { Ok(false) })
}
}
"player_leave" => {
if let Some(event) = event.downcast_ref::<types::player::PlayerEvent>() {
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::<types::player::PlayerEvent>() {
hooks.on_player_join(&context, event)
} else {
Box::pin(async { Ok(false) })
}
}
"player_leave" => {
if let Some(event) = event.downcast_ref::<types::player::PlayerEvent>() {
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),
_ => {}
}
}
Expand Down

0 comments on commit bdb684b

Please sign in to comment.