diff --git a/Cargo.lock b/Cargo.lock index b2b4931..2b23c74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,6 +97,7 @@ dependencies = [ "anyhow", "async-trait", "flate2", + "lazy_static", "log", "nalgebra", "num_enum", diff --git a/Cargo.toml b/Cargo.toml index 86740f8..fc85191 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,8 @@ edition = "2021" log = "0.4" pretty_env_logger = "0.4.0" +lazy_static = "1" + anyhow = "1.0.66" nalgebra = "0.31" num_enum = "0.5.7" diff --git a/Resources/Client/LuuksDraftingMod.zip b/Resources/Client/LuuksDraftingMod.zip new file mode 100644 index 0000000..73f465b Binary files /dev/null and b/Resources/Client/LuuksDraftingMod.zip differ diff --git a/Resources/Client/LuuksRadarMod.zip b/Resources/Client/LuuksRadarMod.zip new file mode 100644 index 0000000..c1d5230 Binary files /dev/null and b/Resources/Client/LuuksRadarMod.zip differ diff --git a/src/config.rs b/src/config.rs index 021da65..28df9a5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,6 +2,9 @@ use serde::Deserialize; #[derive(Deserialize)] pub struct Config { + #[serde(skip)] // Skipping uses Default::default, which makes a new vector for us :) + pub mods: Vec<(String, usize)>, + #[serde(rename = "General")] pub general: GeneralSettings, } diff --git a/src/main.rs b/src/main.rs index e941d6b..7783115 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,16 @@ -#[macro_use] extern crate async_trait; #[macro_use] extern crate log; +#[macro_use] extern crate async_trait; +#[macro_use] extern crate lazy_static; mod server; mod config; #[tokio::main] async fn main() { - let user_config: config::Config = toml::from_str( + pretty_env_logger::formatted_timed_builder().filter_level(log::LevelFilter::max()).init(); + // pretty_env_logger::formatted_timed_builder().filter_level(log::LevelFilter::Info).init(); + + let mut user_config: config::Config = toml::from_str( &std::fs::read_to_string("ServerConfig.toml") .map_err(|_| error!("Failed to read config file!")) .expect("Failed to read config file!") @@ -14,6 +18,21 @@ async fn main() { .map_err(|_| error!("Failed to parse config file!")) .expect("Failed to parse config file!"); + // TODO: This should not error lol + for entry in std::fs::read_dir("Resources/Client").expect("Failed to read Resources/Client!") { + if let Ok(entry) = entry { + if entry.path().is_file() { + if let Ok(metadata) = entry.metadata() { + if let Some(filename) = entry.path().file_name().map(|s| s.to_string_lossy()) { + user_config.mods.push((filename.to_string(), metadata.len() as usize)); + } + } + } + } + } + + debug!("Mods: {:?}", user_config.mods); + let user_config = std::sync::Arc::new(user_config); let mut server = server::Server::new(user_config) diff --git a/src/server/client.rs b/src/server/client.rs index e5a66e2..d9f1d33 100644 --- a/src/server/client.rs +++ b/src/server/client.rs @@ -24,6 +24,10 @@ use super::packet::*; static ATOMIC_ID_COUNTER: AtomicU8 = AtomicU8::new(0); +lazy_static! { + static ref CLIENT_MOD_PROGRESS: Mutex> = Mutex::new(HashMap::new()); +} + #[derive(PartialEq)] pub enum ClientState { None, @@ -114,7 +118,7 @@ impl Client { } } - pub async fn authenticate(&mut self, config: &super::Config) -> anyhow::Result<()> { + pub async fn authenticate(&mut self, config: &super::Config) -> anyhow::Result { debug!("Authenticating client {}...", self.id); 'waiting_for_c: loop { @@ -128,6 +132,11 @@ impl Client { debug!("code: '{}' / {}", code as char, code); match code as char { 'C' => { + // We now delete existing data for this client ID, just in case. + // TODO: This seems like a recipe for disaster + let mut lock = CLIENT_MOD_PROGRESS.lock().await; + lock.remove(&(self.id as usize)); + // TODO: Check client version trace!("Client version packet"); self.socket.readable().await?; @@ -136,10 +145,54 @@ impl Client { break 'waiting_for_c; } 'D' => { - let id = self.read_raw(1).await?[0]; + // 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); - debug!("Not handling this for now!"); - return Err(ClientError::IsDownloader.into()); + + self.write_packet(Packet::Raw(RawPacket::from_str("AG"))).await?; + + // TODO: How does this work??? + + 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 + + *next_id += 1; + + debug!("Mod name: {}", bmod.0); + + let mut mod_name = 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); + // self.write_packet(Packet::Raw(packet)).await?; + + { + let mut lock = self.write_half.lock().await; + lock.writable().await?; + trace!("Sending packet!"); + if let Err(e) = tcp_write_raw(lock.deref_mut(), Packet::Raw(packet)).await { + error!("{:?}", e); + } + trace!("Packet sent!"); + drop(lock); + } + + // return Err(ClientError::IsDownloader.into()); + return Ok(false); } _ => { return Err(ClientError::AuthenticateError.into()); @@ -186,7 +239,7 @@ impl Client { ); self.sync(config).await?; - Ok(()) + Ok(true) } // TODO: https://github.com/BeamMP/BeamMP-Server/blob/master/src/TNetwork.cpp#L619 @@ -205,9 +258,20 @@ impl Client { match packet.data[0] as char { 'S' if packet.data.len() > 1 => match packet.data[1] as char { 'R' => { - let file_packet = RawPacket::from_code('-'); + // let file_packet = RawPacket::from_code('-'); // let file_data = "/bepis_dysoon_uu201_v3.zip;/bepis_laudi_v8_revolution.zip;/simraceclubclient.zip;48353220;50283849;1937;"; - // let file_packet = RawPacket::from_str(file_data); + let mut file_data = String::new(); + for (name, size) in &config.mods { + let mut mod_name = name.clone(); + if mod_name.starts_with("/") == false { + mod_name = format!("/{mod_name}"); + } + file_data.push_str(&format!("{mod_name};")); + } + for (name, size) in &config.mods { + file_data.push_str(&format!("{size};")); + } + let file_packet = RawPacket::from_str(&file_data); self.write_packet(Packet::Raw(file_packet)) .await? } @@ -218,13 +282,6 @@ impl Client { let mut file_name = packet.data_as_string().clone(); file_name.remove(0); // Remove f debug!("Client requested file {}", file_name); - self.kick(&format!("You have not yet downloaded {}!", file_name)).await; - // let mut lock = self.write_half.lock().await; - // lock.writable().await?; - // trace!("Sending packet!"); - // if let Err(e) = tcp_send_file(lock.deref_mut(), file_name).await { - // error!("{:?}", e); - // } } _ => error!("Unknown packet! {:?}", packet), } diff --git a/src/server/mod.rs b/src/server/mod.rs index 36e4924..b391d29 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -80,11 +80,11 @@ impl Server { let ci_ref = clients_incoming_ref.clone(); set.spawn(async move { - socket.set_nodelay(true); + socket.set_nodelay(true); // TODO: Is this good? let mut client = Client::new(socket); match client.authenticate(&cfg_ref).await { - Ok(_) => { + Ok(b) if b => { let mut lock = ci_ref .lock() .map_err(|e| error!("{:?}", e)) @@ -92,6 +92,9 @@ impl Server { lock.push(client); drop(lock); }, + Ok(b) => { + debug!("Downloader?"); + }, Err(e) => { error!("Authentication error occured, kicking player..."); error!("{:?}", e); diff --git a/src/server/packet.rs b/src/server/packet.rs index 43e5fab..27ffb2b 100644 --- a/src/server/packet.rs +++ b/src/server/packet.rs @@ -73,6 +73,13 @@ impl RawPacket { } } + pub fn from_data(data: Vec) -> Self { + Self { + header: data.len() as u32, + data, + } + } + pub fn from_str(str_data: &str) -> Self { let data = str_data.as_bytes().to_vec(); Self {