mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-10-28 06:50:00 +00:00
wip lua event stuff
This commit is contained in:
1 parent
01fb538129
commit
4f52e30545
4 files changed
+110
-8
No files matched your search
@@ -1,7 +1,8 @@
|
|||||||
print("test from lua")
|
print("test from lua")
|
||||||
|
print("Player count: " .. MP.GetPlayerCount())
|
||||||
|
|
||||||
function onPluginLoaded()
|
function onPluginLoaded()
|
||||||
print("HI!")
|
print("HI!")
|
||||||
end
|
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;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
use super::{Backend, ServerBoundPluginEvent};
|
use super::{
|
||||||
|
Backend,
|
||||||
|
ServerBoundPluginEvent,
|
||||||
|
PluginBoundPluginEvent,
|
||||||
|
ScriptEvent,
|
||||||
|
Argument
|
||||||
|
};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc::Sender;
|
|
||||||
|
use tokio::sync::mpsc::{Sender, Receiver};
|
||||||
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
use mlua::{UserData, UserDataMethods, Value};
|
use mlua::{UserData, UserDataMethods, Value, Function, Variadic};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Context {
|
struct Context {
|
||||||
@@ -28,13 +38,32 @@ impl<'lua> FromLua<'lua> for Context {
|
|||||||
|
|
||||||
impl UserData for Context {
|
impl UserData for Context {
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
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);
|
debug!("Event handler registered: {} (EVENT) = {} (LUA)", event_name, handler_name);
|
||||||
let me: Context = lua.globals().get("MP")?;
|
let me: Context = lua.globals().get("MP")?;
|
||||||
// TODO: Figure out how to handle these errors (?)
|
// TODO: Figure out how to handle these errors (?)
|
||||||
let _ = me.tx.blocking_send(ServerBoundPluginEvent::RegisterEventHandler((event_name, handler_name)));
|
let _ = me.tx.blocking_send(ServerBoundPluginEvent::RegisterEventHandler((event_name, handler_name)));
|
||||||
Ok(())
|
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(())
|
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::runtime::Runtime;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio::sync::mpsc::{self, Sender, Receiver};
|
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.
|
/// 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
|
/// 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 (?)
|
// still have to require Send as the runtime might run on different threads (?)
|
||||||
pub trait Backend: Send {
|
pub trait Backend: Send {
|
||||||
fn load(&mut self, code: String) -> anyhow::Result<()>;
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum Argument {
|
pub enum Argument {
|
||||||
|
String(String),
|
||||||
|
Boolean(bool),
|
||||||
Number(f32),
|
Number(f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ScriptEvent {
|
||||||
|
OnPluginLoaded,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PluginBoundPluginEvent {
|
pub enum PluginBoundPluginEvent {
|
||||||
CallEventHandler((String, Vec<Argument>))
|
CallEventHandler((ScriptEvent, Vec<Argument>)),
|
||||||
|
PlayerCount(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -29,6 +42,7 @@ pub enum ServerBoundPluginEvent {
|
|||||||
|
|
||||||
/// Arguments: (event name, handler function name)
|
/// Arguments: (event name, handler function name)
|
||||||
RegisterEventHandler((String, String)),
|
RegisterEventHandler((String, String)),
|
||||||
|
RequestPlayerCount(oneshot::Sender<PluginBoundPluginEvent>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Plugin {
|
pub struct Plugin {
|
||||||
@@ -55,8 +69,14 @@ impl Plugin {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Some(message) = pb_rx.blocking_recv() {
|
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 {
|
} else {
|
||||||
|
error!("Event receiver has closed!"); // TODO: We probably want to display the plugin name here too lol
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,4 +87,23 @@ impl Plugin {
|
|||||||
rx: sb_rx,
|
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