From 0335d22809f15db10667238a34a96aa1779281d6 Mon Sep 17 00:00:00 2001 From: Luuk van Oijen Date: Fri, 17 Nov 2023 14:29:19 +0100 Subject: [PATCH] wip player identifiers stuff --- Cargo.lock | 71 +++++++++++++++++++++++++ Cargo.toml | 1 + Resources/Server/TestPlugin/main.lua | 18 +++++-- src/server/backend.rs | 1 + src/server/client.rs | 79 +++------------------------- src/server/mod.rs | 37 +++++++++---- src/server/plugins/backend_lua.rs | 33 +++++++++--- src/server/plugins/mod.rs | 25 ++++++++- 8 files changed, 170 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 863d9ca..72a1889 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.75" @@ -106,6 +121,7 @@ dependencies = [ "pretty_env_logger", "reqwest", "serde", + "serde-aux", "serde_json", "tokio", "toml", @@ -166,6 +182,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -511,6 +539,29 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.4.0" @@ -1084,6 +1135,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-aux" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3dfe1b7eb6f9dcf011bd6fad169cdeaae75eda0d61b1a99a3f015b41b0cae39" +dependencies = [ + "chrono", + "serde", + "serde_json", +] + [[package]] name = "serde_derive" version = "1.0.192" @@ -1537,6 +1599,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index aaa1880..0ba3e40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ futures = "0.3.29" reqwest = { version = "0.11", features = ["json"] } serde = { version = "1.0", features = ["derive"] } serde_json = "*" +serde-aux = "4.2.0" toml = "0.5" flate2 = "1.0" diff --git a/Resources/Server/TestPlugin/main.lua b/Resources/Server/TestPlugin/main.lua index 8f8dd75..85a3be2 100644 --- a/Resources/Server/TestPlugin/main.lua +++ b/Resources/Server/TestPlugin/main.lua @@ -7,6 +7,7 @@ end function onPlayerAuthenticated(joined_name, role, is_guest, identifiers) print("hi welcome mista " .. joined_name) + print("u a guest? " .. tostring(is_guest)) print("current players:") for id, name in pairs(MP.GetPlayers()) do @@ -16,11 +17,22 @@ function onPlayerAuthenticated(joined_name, role, is_guest, identifiers) print("Player count: " .. MP.GetPlayerCount()) - return 0 -- 0 = do not block + for key, value in pairs(identifiers) do + print(key .. ": " .. value) + end + + -- print("now trying with getting it ourself") + -- for key, value in pairs(MP.GetPlayerIdentifiers(pid)) do + -- print(key .. ": " .. value) + -- end + + return 0 -- 0 = do not cancel end -function onPlayerDisconnect(pid) - print("Player with PID " .. pid .. " has left!") +function onPlayerDisconnect(pid, name, identifiers) + -- Player is already gone here, so for now, player data is no longer + -- requestable for player with the id `pid` + print("Player " .. name .. " (" .. pid .. ") has left!") end MP.RegisterEventHandler("onInit", "onPluginLoaded") diff --git a/src/server/backend.rs b/src/server/backend.rs index 4c8bd63..1e578d8 100644 --- a/src/server/backend.rs +++ b/src/server/backend.rs @@ -14,5 +14,6 @@ pub async fn authentication_request( .json(&map) .send() .await?; + // panic!("json: {:?}", resp.text().await); Ok(resp.json().await?) } diff --git a/src/server/client.rs b/src/server/client.rs index 4dd05ab..a4fae15 100644 --- a/src/server/client.rs +++ b/src/server/client.rs @@ -17,6 +17,7 @@ use tokio::task::JoinHandle; use nalgebra::*; use serde::Deserialize; +use serde_aux::prelude::*; use super::backend::*; use super::car::*; @@ -39,6 +40,8 @@ pub enum ClientState { #[derive(Deserialize, Debug, PartialEq, Clone)] pub struct UserData { + #[serde(deserialize_with = "deserialize_number_from_string")] + pub uid: u64, pub createdAt: String, pub guest: bool, pub roles: String, @@ -107,78 +110,6 @@ impl Client { pub async fn authenticate(&mut self, config: &super::Config) -> anyhow::Result { debug!("Authenticating client {}...", self.id); - self.socket.readable().await?; - // let mut tmp = vec![0u8; 1]; - // while self.socket.peek(&mut tmp).await? == 0 {} - // // Authentication works a little differently than normal - // // Not sure why, but the BeamMP source code shows they - // // also only read a single byte during authentication - // let code = self.read_raw(1).await?[0]; - // debug!("code: '{}' / {}", code as char, code); - // match code as char { - // 'C' => { - // - // break 'waiting_for_c; - // } - // 'D' => { - // // The download sequence is so awful - // // It currently requires us to track what the next file is that - // // we need to provide, which is hard to do with the current - // // server design. - // // I think I will simply keep a counter around that will - // // track what the next mod is per client. - // // TODO: Clean this up. It also needs to be moved out of the client code IMO - // - // let id = self.read_raw(1).await?[0] as usize; - // debug!("HandleDownload connection for client id: {}", id); - // - // let mut mod_name = { - // let mut lock = CLIENT_MOD_PROGRESS.lock().await; - // if lock.get(&id).is_none() { lock.insert(id, 0); } - // let next_id = lock.get_mut(&id).unwrap(); - // - // let bmod = &config.mods[*next_id]; // TODO: This is a bit uhh yeah - // debug!("Mod name: {}", bmod.0); - // - // *next_id += 1; - // - // if *next_id >= config.mods.len() { - // // I think this is where the connection should be closed, instead of after - // // just 1 mod. - // } - // - // bmod.0.clone() - // }; - // - // if mod_name.starts_with("/") == false { - // mod_name = format!("/{mod_name}"); - // } - // - // let mod_path = format!("Resources/Client{mod_name}"); - // let file_data = std::fs::read(mod_path)?; - // - // let packet = RawPacket::from_data(file_data[(file_data.len()/2)..].to_vec()); - // - // { - // let mut lock = self.write_half.lock().await; - // lock.writable().await?; - // trace!("Sending packets!"); - // if let Err(e) = tcp_write_raw(lock.deref_mut(), Packet::Raw(packet)).await { - // error!("{:?}", e); - // } - // trace!("Packets sent!"); - // drop(lock); - // } - // - // // return Err(ClientError::IsDownloader.into()); - // return Ok(false); - // } - // _ => { - // error!("Unknown code: {}", code); - // return Err(ClientError::AuthenticateError.into()); - // } - // } - // TODO: Check client version trace!("Client version packet"); self.socket.readable().await?; @@ -354,6 +285,10 @@ impl Client { self.disconnect(); } + pub fn get_userdata(&self) -> UserData { + self.info.as_ref().unwrap().clone() + } + // Panics when userdata is not set! pub fn get_name(&self) -> &str { &self.info.as_ref().unwrap().username diff --git a/src/server/mod.rs b/src/server/mod.rs index ecf31d5..0b0c1c9 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -367,25 +367,29 @@ impl Server { // with the client acception runtime. If that one locks, the server won't accept // more clients, but it will at least still process all other clients let mut joined_names = Vec::new(); - if let Ok(mut clients_incoming_lock) = self.clients_incoming.try_lock() { + if let Ok(mut clients_incoming_lock) = self.clients_incoming.try_lock() { // TODO: Why do I use try_lock here? if clients_incoming_lock.len() > 0 { trace!( "Accepting {} incoming clients...", clients_incoming_lock.len() ); for i in 0..clients_incoming_lock.len() { - let name = clients_incoming_lock[i] - .info - .as_ref() - .unwrap() - .username - .clone(); + let (name, role, is_guest, beammp_id) = { + let client = clients_incoming_lock[i] + .info + .as_ref() + .unwrap(); + (client.username.clone(), client.roles.clone(), client.guest, client.uid) + }; info!("Welcome {name}!"); joined_names.push(name.clone()); let mut vrx = Vec::new(); for plugin in &self.plugins { let (tx, rx) = tokio::sync::oneshot::channel(); - plugin.send_event(PluginBoundPluginEvent::CallEventHandler((ScriptEvent::OnPlayerAuthenticated { name: name.clone() }, Some(tx)))).await; + plugin.send_event(PluginBoundPluginEvent::CallEventHandler((ScriptEvent::OnPlayerAuthenticated { name: name.clone(), role: role.clone(), is_guest, identifiers: PlayerIdentifiers { + ip: String::from("not yet implemented"), + beammp_id, + } }, Some(tx)))).await; // TODO: This never returns, because it blocks the entire process function // from running, so it never manages to run the function correctly. // let res = rx.await.unwrap_or(Argument::Number(-1f32)); @@ -447,7 +451,7 @@ impl Server { // TODO: Error handling (?) match event { ServerBoundPluginEvent::PluginLoaded => plugin.send_event(PluginBoundPluginEvent::CallEventHandler((ScriptEvent::OnPluginLoaded, None))).await, - ServerBoundPluginEvent::RequestPlayerCount(responder) => { let _ = responder.send(PluginBoundPluginEvent::PlayerCount(self.clients.len() + self.clients_queue.len())); } + ServerBoundPluginEvent::RequestPlayerCount(responder) => { let _ = responder.send(PluginBoundPluginEvent::PlayerCount(self.clients.len() + self.clients_queue.len())); }, ServerBoundPluginEvent::RequestPlayers(responder) => { trace!("request players received"); let mut players = HashMap::new(); @@ -457,7 +461,17 @@ impl Server { trace!("sending player list..."); let _ = responder.send(PluginBoundPluginEvent::Players(players)); trace!("player list sent"); - } + }, + ServerBoundPluginEvent::RequestPlayerIdentifiers((pid, responder)) => { + if let Some(client) = self.clients.iter().find(|client| client.id == pid) { + let _ = responder.send(PluginBoundPluginEvent::PlayerIdentifiers(PlayerIdentifiers { + ip: String::from("not yet implemented"), + beammp_id: client.get_userdata().uid, + })); + } else { + let _ = responder.send(PluginBoundPluginEvent::None); + } + }, _ => {}, } } @@ -498,8 +512,9 @@ impl Server { } let id = self.clients[i].id; + let name = self.clients[i].get_name().to_string(); for plugin in &mut self.plugins { - plugin.send_event(PluginBoundPluginEvent::CallEventHandler((ScriptEvent::OnPlayerDisconnect { pid: id }, None))).await; + plugin.send_event(PluginBoundPluginEvent::CallEventHandler((ScriptEvent::OnPlayerDisconnect { pid: id, name: name.clone() }, None))).await; } info!("Disconnecting client {}...", id); diff --git a/src/server/plugins/backend_lua.rs b/src/server/plugins/backend_lua.rs index 2de6056..d780927 100644 --- a/src/server/plugins/backend_lua.rs +++ b/src/server/plugins/backend_lua.rs @@ -137,8 +137,8 @@ impl Backend for BackendLua { fn call_event_handler(&mut self, event: ScriptEvent, resp: Option>) { let (event_name, args) = match event { ScriptEvent::OnPluginLoaded => ("onInit", vec![]), - ScriptEvent::OnPlayerAuthenticated { name } => ("onPlayerAuth", vec![Argument::String(name)]), - ScriptEvent::OnPlayerDisconnect { pid } => ("onPlayerDisconnect", vec![Argument::Number(pid as f32)]), + ScriptEvent::OnPlayerAuthenticated { name, role, is_guest, identifiers } => ("onPlayerAuth", vec![Argument::String(name), Argument::String(role), Argument::Boolean(is_guest), Argument::Table(identifiers.to_map())]), + ScriptEvent::OnPlayerDisconnect { pid, name } => ("onPlayerDisconnect", vec![Argument::Number(pid as f32), Argument::String(name)]), }; let mut ret = -1f32; @@ -150,11 +150,7 @@ impl Backend for BackendLua { let func: LuaResult = self.lua.globals().get(handler_name.clone()); 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)), - } + arg_to_value(&self.lua, arg) }).filter(|v| v.is_some()); match func.call::<_, Option>(Variadic::from_iter(mapped_args)) { Ok(res) => { trace!("fn ret: {:?}", ret); ret = res.unwrap_or(-1f32); } @@ -172,3 +168,26 @@ impl Backend for BackendLua { debug!("call_event_handler done"); } } + +fn arg_to_value(lua: &Lua, arg: Argument) -> Option { + match arg { + Argument::String(s) => if let Ok(lua_str) = 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)), + Argument::Integer(i) => Some(Value::Integer(i as i64)), + Argument::Table(t) => { + if let Ok(table) = lua.create_table() { + for (key, value) in t { + if let Some(v) = arg_to_value(lua, value) { + if let Err(e) = table.set(key, v) { + error!("[LUA] Error occured trying to put data into table: {:?}", e); + } + } + } + Some(Value::Table(table)) + } else { + None + } + } + } +} diff --git a/src/server/plugins/mod.rs b/src/server/plugins/mod.rs index 0cd7fa8..a371896 100644 --- a/src/server/plugins/mod.rs +++ b/src/server/plugins/mod.rs @@ -23,23 +23,43 @@ pub enum Argument { String(String), Boolean(bool), Number(f32), + Integer(i64), + Table(HashMap), +} + +#[derive(Debug)] +pub struct PlayerIdentifiers { + pub ip: String, + pub beammp_id: u64, +} + +impl PlayerIdentifiers { + pub fn to_map(&self) -> HashMap { + let mut m = HashMap::new(); + m.insert(String::from("ip"), Argument::String(self.ip.clone())); + m.insert(String::from("beammp"), Argument::Integer(self.beammp_id as i64)); // TODO: Uhh we could lose data here + m + } } #[derive(Debug)] pub enum ScriptEvent { OnPluginLoaded, - OnPlayerAuthenticated { name: String }, + OnPlayerAuthenticated { name: String, role: String, is_guest: bool, identifiers: PlayerIdentifiers }, - OnPlayerDisconnect { pid: u8 }, + OnPlayerDisconnect { pid: u8, name: String }, } #[derive(Debug)] pub enum PluginBoundPluginEvent { + None, + CallEventHandler((ScriptEvent, Option>)), PlayerCount(usize), Players(HashMap), + PlayerIdentifiers(PlayerIdentifiers), } #[derive(Debug)] @@ -48,6 +68,7 @@ pub enum ServerBoundPluginEvent { RequestPlayerCount(oneshot::Sender), RequestPlayers(oneshot::Sender), + RequestPlayerIdentifiers((u8, oneshot::Sender)), } pub struct Plugin {