mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-04-23 08:27:06 +00:00
wip lua event stuff
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
print("test from lua")
|
||||
print("Player count: " .. MP.GetPlayerCount())
|
||||
|
||||
function onPluginLoaded()
|
||||
print("HI!")
|
||||
end
|
||||
|
||||
MP:RegisterEventHandler("onPluginLoaded", "onPluginLoaded")
|
||||
MP.RegisterEventHandler("onPluginLoaded", "onPluginLoaded")
|
||||
|
||||
@@ -300,6 +300,19 @@ impl Server {
|
||||
self.broadcast(Packet::Raw(RawPacket::from_str(&data)), None).await;
|
||||
}
|
||||
|
||||
// Receive plugin events and process them
|
||||
for plugin in &mut self.plugins {
|
||||
for event in plugin.get_events() {
|
||||
debug!("event: {:?}", event);
|
||||
// TODO: Error handling (?)
|
||||
match event {
|
||||
ServerBoundPluginEvent::PluginLoaded => plugin.send_event(PluginBoundPluginEvent::CallEventHandler((ScriptEvent::OnPluginLoaded, Vec::new()))).await,
|
||||
ServerBoundPluginEvent::RequestPlayerCount(responder) => { let _ = responder.send(PluginBoundPluginEvent::PlayerCount(self.clients.len())); }
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
use super::{Backend, ServerBoundPluginEvent};
|
||||
use super::{
|
||||
Backend,
|
||||
ServerBoundPluginEvent,
|
||||
PluginBoundPluginEvent,
|
||||
ScriptEvent,
|
||||
Argument
|
||||
};
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
|
||||
use tokio::sync::mpsc::{Sender, Receiver};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use mlua::{UserData, UserDataMethods, Value};
|
||||
use mlua::{UserData, UserDataMethods, Value, Function, Variadic};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Context {
|
||||
@@ -28,13 +38,32 @@ impl<'lua> FromLua<'lua> for Context {
|
||||
|
||||
impl UserData for Context {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("RegisterEventHandler", |lua, _, (event_name, handler_name): (String, String)| {
|
||||
// Implement all the API functions here
|
||||
|
||||
methods.add_function("RegisterEventHandler", |lua, (event_name, handler_name): (String, String)| {
|
||||
debug!("Event handler registered: {} (EVENT) = {} (LUA)", event_name, handler_name);
|
||||
let me: Context = lua.globals().get("MP")?;
|
||||
// TODO: Figure out how to handle these errors (?)
|
||||
let _ = me.tx.blocking_send(ServerBoundPluginEvent::RegisterEventHandler((event_name, handler_name)));
|
||||
Ok(())
|
||||
});
|
||||
|
||||
methods.add_function("GetPlayerCount", |lua, ()| {
|
||||
let me: Context = lua.globals().get("MP")?;
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let r = me.tx.blocking_send(ServerBoundPluginEvent::RequestPlayerCount(tx));
|
||||
debug!("{:?}", r);
|
||||
let message = rx.blocking_recv();
|
||||
if let Ok(message) = message {
|
||||
if let PluginBoundPluginEvent::PlayerCount(player_count) = message {
|
||||
Ok(player_count)
|
||||
} else {
|
||||
unreachable!() // This really should never be reachable
|
||||
}
|
||||
} else {
|
||||
todo!("Receiving a response from the server failed! How?")
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,4 +108,24 @@ impl Backend for BackendLua {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn call_event_handler(&mut self, event: ScriptEvent, args: Vec<Argument>) {
|
||||
let event_name = match event {
|
||||
ScriptEvent::OnPluginLoaded => "onPluginLoaded",
|
||||
};
|
||||
|
||||
let func: LuaResult<Function> = self.lua.globals().get(event_name);
|
||||
if let Ok(func) = func {
|
||||
let mapped_args = args.into_iter().map(|arg| {
|
||||
match arg {
|
||||
Argument::String(s) => if let Ok(lua_str) = self.lua.create_string(&s) { Some(Value::String(lua_str)) } else { None },
|
||||
Argument::Boolean(b) => Some(Value::Boolean(b)),
|
||||
Argument::Number(f) => Some(Value::Number(f as f64)),
|
||||
}
|
||||
}).filter(|v| v.is_some());
|
||||
if let Err(e) = func.call::<_, ()>(Variadic::from_iter(mapped_args)) {
|
||||
error!("[LUA] {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,23 +4,36 @@ use std::sync::Arc;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::mpsc::{self, Sender, Receiver};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
/// NOTE: Send is required as the backend is constructed on the main thread and sent over.
|
||||
/// Even if we construct it inside the runtime however, because of tokio, we would
|
||||
// still have to require Send as the runtime might run on different threads (?)
|
||||
pub trait Backend: Send {
|
||||
fn load(&mut self, code: String) -> anyhow::Result<()>;
|
||||
fn load_api(&mut self, tx: Arc<Sender<ServerBoundPluginEvent>>) -> anyhow::Result<()> { Ok(()) }
|
||||
fn load_api(&mut self, tx: Arc<Sender<ServerBoundPluginEvent>>) -> anyhow::Result<()>;
|
||||
|
||||
fn call_event_handler(&mut self, event: ScriptEvent, args: Vec<Argument>);
|
||||
}
|
||||
|
||||
// TODO: This is quite focused on Lua right now, perhaps in the future we want to modify this list
|
||||
// to be more versatile?
|
||||
#[derive(Debug)]
|
||||
pub enum Argument {
|
||||
String(String),
|
||||
Boolean(bool),
|
||||
Number(f32),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ScriptEvent {
|
||||
OnPluginLoaded,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PluginBoundPluginEvent {
|
||||
CallEventHandler((String, Vec<Argument>))
|
||||
CallEventHandler((ScriptEvent, Vec<Argument>)),
|
||||
PlayerCount(usize),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -29,6 +42,7 @@ pub enum ServerBoundPluginEvent {
|
||||
|
||||
/// Arguments: (event name, handler function name)
|
||||
RegisterEventHandler((String, String)),
|
||||
RequestPlayerCount(oneshot::Sender<PluginBoundPluginEvent>),
|
||||
}
|
||||
|
||||
pub struct Plugin {
|
||||
@@ -55,8 +69,14 @@ impl Plugin {
|
||||
|
||||
loop {
|
||||
if let Some(message) = pb_rx.blocking_recv() {
|
||||
debug!("Received message: {:?}", message);
|
||||
match message {
|
||||
PluginBoundPluginEvent::CallEventHandler((event, args)) => {
|
||||
backend.call_event_handler(event, args);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
} else {
|
||||
error!("Event receiver has closed!"); // TODO: We probably want to display the plugin name here too lol
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -67,4 +87,23 @@ impl Plugin {
|
||||
rx: sb_rx,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: For performance I think we can turn this into an iterator instead of first allocating
|
||||
// a full vector?
|
||||
pub fn get_events(&mut self) -> Vec<ServerBoundPluginEvent> {
|
||||
let mut events = Vec::new();
|
||||
loop {
|
||||
match self.rx.try_recv() {
|
||||
Ok(event) => events.push(event),
|
||||
Err(mpsc::error::TryRecvError::Disconnected) => break, // TODO: This means the runtime is dead!!! Handle this!!!
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
events
|
||||
}
|
||||
|
||||
// TODO: Handle error when connection is closed, as it means the runtime is down
|
||||
pub async fn send_event(&self, event: PluginBoundPluginEvent) {
|
||||
let _ = self.tx.send(event).await;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user