diff --git a/Cargo.toml b/Cargo.toml index fca85e6..89a8e21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,25 @@ members = [ 'foundation', 'server', + 'protocol', 'client', - 'serverctl', - 'example_plugin' ] + +[workspace.dependencies] +# common data types +uuid = {version = "1.1.2", features = ["serde", "v4"]} + +# maths +rand = "0.8.5" + +# serialisation +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +# async tokio +tokio = { version = "1.9.0", features = ["full"] } + +# protobuf +bytes = "1.6.0" +prost = "0.12" +prost-build = { version = "0.12" } diff --git a/client/Cargo.toml b/client/Cargo.toml index c36c13a..50bd86a 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -1,22 +1,17 @@ [package] name = "client" version = "0.1.0" -authors = ["michael-bailey "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cursive = "0.17" -uuid = {version = "1.1.2", features = ["serde", "v4"]} -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -crossbeam = "0.8.0" -crossbeam-channel = "0.5.0" -tokio = { version = "1.9.0", features = ["full"] } -futures = "0.3.16" +uuid.workspace = true +tokio.workspace = true +cursive = "0.20.0" +rand.workspace = true -async-trait = "0.1.52" +bytes.workspace = true +prost.workspace = true -server = {path = '../server'} -foundation = {path = '../foundation'} +foundation = { path = '../foundation' } diff --git a/client/src/main.rs b/client/src/main.rs index f88edec..a1d135a 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -1,34 +1,5 @@ -mod managers; -mod worker; -mod worker_message; - -use cursive::{ - menu::{Item, Tree}, - traits::Nameable, - views::{Dialog, TextView}, - Cursive, - CursiveExt, -}; -use worker::Worker; +mod test; fn main() { - let mut app = Cursive::default(); - let worker_stream = Worker::new(app.cb_sink().clone()).start(); - - app.set_user_data(worker_stream); - app.add_layer( - Dialog::new() - .content(TextView::new("Hello world").with_name("TextView")) - .button("close", |s| s.quit()), - ); - app.menubar().autohide = false; - app.menubar().add_subtree( - "Application", - Tree::new() - .item(Item::leaf("About", |s| s.quit())) - .delimiter() - .item(Item::leaf("Quit", |s| s.quit())), - ); - app.set_fps(30); - app.run(); + println!("Please dont use this"); } diff --git a/client/src/managers/Network.rs b/client/src/managers/Network.rs deleted file mode 100644 index ceb6f1b..0000000 --- a/client/src/managers/Network.rs +++ /dev/null @@ -1,221 +0,0 @@ -use std::{ - io::{Error, ErrorKind}, - mem, - sync::{atomic::AtomicBool, Arc}, -}; - -use async_trait::async_trait; -use foundation::{ - connection::Connection, - messages::{ - client::{ClientStreamIn, ClientStreamOut}, - network::{NetworkSockIn, NetworkSockOut}, - }, - prelude::IManager, -}; -use tokio::{ - net::ToSocketAddrs, - sync::{mpsc::Sender, Mutex}, -}; -use uuid::Uuid; - -use crate::managers::NetworkManagerMessage; - -pub struct NetworkManager -where - M: From, -{ - #[allow(unused)] - server_connection: Mutex>>, - - #[allow(unused)] - cursive: Sender, - - is_logged_in: AtomicBool, -} - -impl NetworkManager -where - M: From, -{ - pub fn new(sender: Sender) -> Arc { - Arc::new(NetworkManager { - server_connection: Mutex::new(None), - cursive: sender, - is_logged_in: AtomicBool::new(false), - }) - } - - #[allow(unused)] - pub async fn info( - self: &Arc, - host: T, - ) -> Result { - let connection = Connection::new(); - println!("Created connection"); - connection.connect(host).await?; - let req = connection.read().await?; - - println!("request: {:?}", req); - - if let NetworkSockOut::Request = req { - connection - .write::(NetworkSockIn::Info) - .await?; - return Ok(connection.read::().await?.into()); - } else { - Err(Error::new( - ErrorKind::ConnectionAborted, - "Request not received", - )) - } - } - - #[allow(unused)] - pub async fn login( - self: &Arc, - host: String, - uuid: Uuid, - username: String, - address: String, - ) -> Result<(), Error> { - let connection = Connection::new(); - - let _ = connection.connect(host).await?; - - println!("created connection"); - - let req = connection.read().await?; - - println!("read request"); - - return if let NetworkSockOut::Request = req { - println!("got request"); - - connection - .write(NetworkSockIn::Connect { - username, - uuid, - address, - }) - .await?; - let res = connection.read().await?; - - // switch over to ClientStreamOut - if let ClientStreamOut::Connected = res { - let mut connection_lock = self.server_connection.lock().await; - let _ = mem::replace(&mut *connection_lock, Some(connection)); - Ok(()) - } else { - Err(Error::new( - ErrorKind::ConnectionRefused, - format!("expected connecting received: {:?}", res), - )) - } - } else { - println!("request not found"); - Err(Error::new( - ErrorKind::ConnectionAborted, - "Server did not send request", - )) - }; - } - - #[allow(unused)] - pub async fn logout(self: &Arc) -> Result<(), Error> { - let mut connection_lock = self.server_connection.lock().await; - let connection = mem::replace(&mut *connection_lock, None).unwrap(); - - connection.write(ClientStreamIn::Disconnect).await?; - - return if let ClientStreamOut::Disconnected = connection.read().await? { - Ok(()) - } else { - Err(Error::new( - ErrorKind::InvalidData, - "disconnect failed, forcing disconnect", - )) - }; - } -} - -#[async_trait] -impl IManager for NetworkManager -where - M: From + Send, -{ - async fn run(self: &Arc) { - println!("networkManager tick") - } -} - -#[cfg(test)] -mod test { - use std::future::Future; - - use serverlib::Server; - use tokio::sync::mpsc::channel; - use uuid::Uuid; - - use crate::managers::{network::NetworkManagerMessage, NetworkManager}; - - async fn wrap_setup(test: T) - where - T: FnOnce(u16) -> F, - F: Future, - { - let server = Server::new().await.unwrap(); - let port = server.port().await; - - tokio::spawn(async move { - server.start().await; - }); - test(port).await; - } - #[tokio::test] - async fn test_fetch_server_info() { - use NetworkManagerMessage::Info; - #[allow(unused)] - let (tx, rx) = channel::(16); - - wrap_setup(|port| async move { - let network = NetworkManager::new(tx); - let info = network - .info(format!("localhost:{}", port)) - .await - .expect("Failed to fetch info"); - assert_eq!( - info, - Info { - server_name: "oof".to_string(), - server_owner: "michael".to_string() - } - ); - }) - .await; - } - #[tokio::test] - async fn test_login_and_logout_to_server() { - #[allow(unused)] - let (tx, rx) = channel::(16); - - let network = NetworkManager::new(tx); - - println!("created network manger"); - - wrap_setup(|port| async move { - network - .login( - format!("localhost:{}", port), - Uuid::default(), - "user1".to_string(), - "localhost".to_string(), - ) - .await - .expect("login failed"); - - network.logout().await.expect("logout failed"); - }) - .await; - } -} diff --git a/client/src/managers/message.rs b/client/src/managers/message.rs deleted file mode 100644 index 39c43af..0000000 --- a/client/src/managers/message.rs +++ /dev/null @@ -1,51 +0,0 @@ -use foundation::{messages::network::NetworkSockOut, ClientDetails}; - -#[derive(Debug)] -pub enum NetworkManagerMessage { - #[allow(unused)] - Users(Vec), - Info { - server_name: String, - server_owner: String, - }, - Error(&'static str), -} - -impl From for NetworkManagerMessage { - fn from(other: NetworkSockOut) -> Self { - use NetworkManagerMessage::{Error, Info as NewInfo}; - use NetworkSockOut::GotInfo as OldInfo; - match other { - OldInfo { - server_name, - server_owner, - } => NewInfo { - server_name, - server_owner, - }, - _ => Error("Error occurred with conversion"), - } - } -} - -impl PartialEq for NetworkManagerMessage { - fn eq(&self, other: &Self) -> bool { - use NetworkManagerMessage::Info; - match self { - Info { - server_owner, - server_name, - } => { - if let Info { - server_owner: other_owner, - server_name: other_name, - } = other - { - return server_owner == other_owner && server_name == other_name; - } - false - } - _ => false, - } - } -} diff --git a/client/src/managers/mod.rs b/client/src/managers/mod.rs deleted file mode 100644 index 52b3057..0000000 --- a/client/src/managers/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod network; - -#[path = "message.rs"] -mod message; - -pub use message::NetworkManagerMessage; -pub use network::NetworkManager; diff --git a/client/src/managers/network.rs b/client/src/managers/network.rs deleted file mode 100644 index ceb6f1b..0000000 --- a/client/src/managers/network.rs +++ /dev/null @@ -1,221 +0,0 @@ -use std::{ - io::{Error, ErrorKind}, - mem, - sync::{atomic::AtomicBool, Arc}, -}; - -use async_trait::async_trait; -use foundation::{ - connection::Connection, - messages::{ - client::{ClientStreamIn, ClientStreamOut}, - network::{NetworkSockIn, NetworkSockOut}, - }, - prelude::IManager, -}; -use tokio::{ - net::ToSocketAddrs, - sync::{mpsc::Sender, Mutex}, -}; -use uuid::Uuid; - -use crate::managers::NetworkManagerMessage; - -pub struct NetworkManager -where - M: From, -{ - #[allow(unused)] - server_connection: Mutex>>, - - #[allow(unused)] - cursive: Sender, - - is_logged_in: AtomicBool, -} - -impl NetworkManager -where - M: From, -{ - pub fn new(sender: Sender) -> Arc { - Arc::new(NetworkManager { - server_connection: Mutex::new(None), - cursive: sender, - is_logged_in: AtomicBool::new(false), - }) - } - - #[allow(unused)] - pub async fn info( - self: &Arc, - host: T, - ) -> Result { - let connection = Connection::new(); - println!("Created connection"); - connection.connect(host).await?; - let req = connection.read().await?; - - println!("request: {:?}", req); - - if let NetworkSockOut::Request = req { - connection - .write::(NetworkSockIn::Info) - .await?; - return Ok(connection.read::().await?.into()); - } else { - Err(Error::new( - ErrorKind::ConnectionAborted, - "Request not received", - )) - } - } - - #[allow(unused)] - pub async fn login( - self: &Arc, - host: String, - uuid: Uuid, - username: String, - address: String, - ) -> Result<(), Error> { - let connection = Connection::new(); - - let _ = connection.connect(host).await?; - - println!("created connection"); - - let req = connection.read().await?; - - println!("read request"); - - return if let NetworkSockOut::Request = req { - println!("got request"); - - connection - .write(NetworkSockIn::Connect { - username, - uuid, - address, - }) - .await?; - let res = connection.read().await?; - - // switch over to ClientStreamOut - if let ClientStreamOut::Connected = res { - let mut connection_lock = self.server_connection.lock().await; - let _ = mem::replace(&mut *connection_lock, Some(connection)); - Ok(()) - } else { - Err(Error::new( - ErrorKind::ConnectionRefused, - format!("expected connecting received: {:?}", res), - )) - } - } else { - println!("request not found"); - Err(Error::new( - ErrorKind::ConnectionAborted, - "Server did not send request", - )) - }; - } - - #[allow(unused)] - pub async fn logout(self: &Arc) -> Result<(), Error> { - let mut connection_lock = self.server_connection.lock().await; - let connection = mem::replace(&mut *connection_lock, None).unwrap(); - - connection.write(ClientStreamIn::Disconnect).await?; - - return if let ClientStreamOut::Disconnected = connection.read().await? { - Ok(()) - } else { - Err(Error::new( - ErrorKind::InvalidData, - "disconnect failed, forcing disconnect", - )) - }; - } -} - -#[async_trait] -impl IManager for NetworkManager -where - M: From + Send, -{ - async fn run(self: &Arc) { - println!("networkManager tick") - } -} - -#[cfg(test)] -mod test { - use std::future::Future; - - use serverlib::Server; - use tokio::sync::mpsc::channel; - use uuid::Uuid; - - use crate::managers::{network::NetworkManagerMessage, NetworkManager}; - - async fn wrap_setup(test: T) - where - T: FnOnce(u16) -> F, - F: Future, - { - let server = Server::new().await.unwrap(); - let port = server.port().await; - - tokio::spawn(async move { - server.start().await; - }); - test(port).await; - } - #[tokio::test] - async fn test_fetch_server_info() { - use NetworkManagerMessage::Info; - #[allow(unused)] - let (tx, rx) = channel::(16); - - wrap_setup(|port| async move { - let network = NetworkManager::new(tx); - let info = network - .info(format!("localhost:{}", port)) - .await - .expect("Failed to fetch info"); - assert_eq!( - info, - Info { - server_name: "oof".to_string(), - server_owner: "michael".to_string() - } - ); - }) - .await; - } - #[tokio::test] - async fn test_login_and_logout_to_server() { - #[allow(unused)] - let (tx, rx) = channel::(16); - - let network = NetworkManager::new(tx); - - println!("created network manger"); - - wrap_setup(|port| async move { - network - .login( - format!("localhost:{}", port), - Uuid::default(), - "user1".to_string(), - "localhost".to_string(), - ) - .await - .expect("login failed"); - - network.logout().await.expect("logout failed"); - }) - .await; - } -} diff --git a/client/src/test/client_test.rs b/client/src/test/client_test.rs new file mode 100644 index 0000000..f763512 --- /dev/null +++ b/client/src/test/client_test.rs @@ -0,0 +1,44 @@ +#[cfg(test)] +mod test { + use foundation::{ + client::network_connection::NetworkConnection, + prelude::Info, + }; + use uuid::Uuid; + + #[tokio::test] + async fn get_info() { + let client = NetworkConnection::connect( + "127.0.0.1:6500" + .parse() + .expect("failed to parse address string"), + ) + .await + .expect("failed to connect to test server"); + + let info: Info = client.send_get_info().await.unwrap(); + + println!("info: {:?}", info) + } + + #[tokio::test] + async fn connect_and_disconnect() { + let client = NetworkConnection::connect( + "127.0.0.1:6500" + .parse() + .expect("failed to parse address string"), + ) + .await + .expect("failed to connect to test server"); + + let (w, r) = client + .send_connect(Uuid::new_v4(), "test user".into()) + .await + .unwrap(); + + drop(w); + drop(r); + + println!("finished") + } +} diff --git a/client/src/test/mod.rs b/client/src/test/mod.rs new file mode 100644 index 0000000..6fc4647 --- /dev/null +++ b/client/src/test/mod.rs @@ -0,0 +1 @@ +mod client_test; diff --git a/client/src/worker.rs b/client/src/worker.rs deleted file mode 100644 index a5ef6ac..0000000 --- a/client/src/worker.rs +++ /dev/null @@ -1,75 +0,0 @@ -use std::{sync::Arc, thread::spawn, time::Duration}; - -use crossbeam_channel::Sender as CrossSender; -use foundation::ClientDetails; -use tokio::{ - runtime::Runtime, - sync::{ - mpsc::{channel, Sender as TokioSender}, - Mutex, - }, - time::sleep, -}; - -use crate::{ - managers::NetworkManager, - worker_message::WorkerMessage, - Cursive, - TextView, -}; - -pub type CursiveSender = CrossSender>; - -pub struct Worker { - cursive_sender: CursiveSender, - - network_manager: Arc>, - - number: Arc>, - - #[allow(unused)] - user_details: Mutex>, -} - -impl Worker { - pub fn new(sender: CursiveSender) -> Worker { - #[allow(unused)] - let (tx, rx) = channel::(16); - - Worker { - network_manager: NetworkManager::new(tx.clone()), - number: Arc::new(Mutex::new(0)), - user_details: Mutex::new(None), - cursive_sender: sender, - } - } - - pub fn start(self) -> TokioSender { - #[allow(unused)] - let (tx, rx) = channel::(16); - spawn(move || { - let sender = self.cursive_sender.clone(); - let rt = Runtime::new().unwrap(); - let tmp_num = self.number.clone(); - #[allow(unused)] - let network_manager = self.network_manager.clone(); - rt.block_on(async move { - let a = &tmp_num; - loop { - let num = Arc::clone(&a); - sleep(Duration::new(1, 0)).await; - let _ = sender.send(Box::new(move |s| { - let num = &num.clone(); - let mut num_lock = num.blocking_lock(); - *num_lock += 1; - let a = *num_lock; - s.find_name::("TextView") - .unwrap() - .set_content(a.to_string()); - })); - } - }) - }); - tx - } -} diff --git a/client/src/worker_message.rs b/client/src/worker_message.rs deleted file mode 100644 index 485dece..0000000 --- a/client/src/worker_message.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::managers::NetworkManagerMessage; - -pub enum WorkerMessage { - Info { - server_name: String, - server_owner: String, - }, - #[allow(unused)] - Error(String), -} - -impl From for WorkerMessage { - fn from(other: NetworkManagerMessage) -> Self { - #[allow(unused)] - use NetworkManagerMessage::{Error, Info as OldInfo}; - #[allow(unused)] - use WorkerMessage::{Error as NewError, Info as NewInfo}; - match other { - OldInfo { - server_name, - server_owner, - } => NewInfo { - server_owner, - server_name, - }, - _ => todo!(), - } - } -} diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..e693e4b --- /dev/null +++ b/docs/index.md @@ -0,0 +1,10 @@ +# Chat Kit + +This is a self hosted, distributed shat system. +It derives a lot of ideas from Discord, IRC and RCS. + +This repository contains a couple of crates. +- Protocol: The protocol message structures +- Foundation: Shared structures and functions utilised in the server and client crate. +- Server: The server that accepts client connections and manages state between them. +- Client: A basic terminal client, used for testing and will be unstable. \ No newline at end of file diff --git a/example_plugin/Cargo.toml b/example_plugin/Cargo.toml deleted file mode 100644 index 30ea348..0000000 --- a/example_plugin/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "example_plugin" -version = "0.1.0" -authors = ["michael-bailey "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -crate-type = ["dylib"] -name = "ExamplePlugin" -path = "src/lib.rs" - - -[dependencies] -uuid = {version = "0.8", features = ["serde", "v4"]} -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -zeroize = "1.1.0" -futures = "0.3.16" -async-trait = "0.1.52" - -server = {path = "../server"} \ No newline at end of file diff --git a/example_plugin/src/example.rs b/example_plugin/src/example.rs deleted file mode 100644 index bd69a02..0000000 --- a/example_plugin/src/example.rs +++ /dev/null @@ -1,63 +0,0 @@ -// use futures::lock::Mutex; -// use serverlib::plugin::WeakPluginInterface; -// use std::sync::Mutex as StdMutex; -// use std::thread::sleep; -// use std::time::Duration; - -// use serverlib::plugin::IPlugin; -// use serverlib::plugin::PluginDetails; - -// #[derive(Debug)] -// pub struct ExamplePlugin { -// number: Mutex, -// interface: StdMutex>, -// } - -// impl Default for ExamplePlugin { -// fn default() -> Self { -// ExamplePlugin { -// number: Mutex::new(0), -// interface: StdMutex::default(), -// } -// } -// } - -// #[async_trait::async_trait] -// impl IPlugin for ExamplePlugin { -// fn details(&self) -> PluginDetails { -// PluginDetails { -// display_name: "ExamplePlugin", -// id: "io.github.michael-bailey.ExamplePlugin", -// version: "0.0.1", -// contacts: vec!["bailey-michael1@outlook.com"], -// } -// } - -// fn set_interface(&self, interface: WeakPluginInterface) { -// if let Ok(mut lock) = self.interface.lock() { -// *lock = Some(interface); -// } -// } - -// async fn event(&self) { -// println!("Not Implemented"); -// } - -// fn init(&self) { -// println!("[ExamplePlugin]: example init") -// } - -// async fn run(&self) { -// println!("Example!!!"); -// sleep(Duration::new(1, 0)); -// let mut a = self.number.lock().await; -// *a = a.overflowing_add(1).0; -// println!("[ExamplePlugin]: example run {}", *a); -// } - -// fn deinit(&self) { -// if let Some(mut lock) = self.number.try_lock() { -// *lock = 0; -// } -// } -// } diff --git a/example_plugin/src/lib.rs b/example_plugin/src/lib.rs deleted file mode 100644 index 6bdc4d3..0000000 --- a/example_plugin/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -mod example; - -use std::sync::Arc; - -// use serverlib::plugin::Plugin; - -// use crate::example::ExamplePlugin; -// use serverlib::plugin::plugin::Plugin; -// use std::sync::Arc; - -// #[no_mangle] -// pub extern "C" fn get_plugin() -> Plugin { -// Arc::new(ExamplePlugin::default()) -// } diff --git a/foundation/Cargo.toml b/foundation/Cargo.toml index 4c7fc5b..cee5c78 100644 --- a/foundation/Cargo.toml +++ b/foundation/Cargo.toml @@ -23,4 +23,7 @@ serde_json = "1.0" openssl = "0.10" uuid = {version = "1.1.2", features = ["serde", "v4"]} tokio = { version = "1.9.0", features = ["full"] } -serde = { version = "1.0", features = ["derive"] } \ No newline at end of file +serde = { version = "1.0", features = ["derive"] } + +prost.workspace = true +protocol = { path = '../protocol' } \ No newline at end of file diff --git a/foundation/src/client/mod.rs b/foundation/src/client/mod.rs new file mode 100644 index 0000000..5497025 --- /dev/null +++ b/foundation/src/client/mod.rs @@ -0,0 +1,15 @@ +use tokio::task::JoinHandle; + +use crate::client::server_writer_connection::ServerWriterConnection; + +pub mod network_connection; +pub mod server_reader_connection; +pub mod server_writer_connection; + +pub enum NetworkState { + Disconnected, + Connection { + reader_handle: JoinHandle<()>, + writer: ServerWriterConnection, + }, +} diff --git a/foundation/src/client/network_connection.rs b/foundation/src/client/network_connection.rs new file mode 100644 index 0000000..1407e76 --- /dev/null +++ b/foundation/src/client/network_connection.rs @@ -0,0 +1,123 @@ +use std::{io, net::SocketAddr}; + +use protocol::prelude::{ + network_client_message, + network_server_message, + Connect, + GetInfo, + Info, + NetworkClientMessage, + NetworkServerMessage, + Request, +}; +use tokio::{io::split, net::TcpStream}; +use uuid::Uuid; + +use crate::{ + client::{ + server_reader_connection::ServerReaderConnection, + server_writer_connection::ServerWriterConnection, + }, + networking::protobuf::{read_message, write_message}, +}; + +/// # NetworkConnection +/// encapsulates the state of the network connection +/// will connect to a server and ensure it is usinghte protobuf protocol +/// +/// you can then either get info or connect to the server +pub struct NetworkConnection { + pub(super) stream: TcpStream, +} + +impl NetworkConnection { + pub async fn connect(address: SocketAddr) -> io::Result { + let mut stream = TcpStream::connect(address).await.unwrap(); + + let msg = + read_message::(&mut stream).await?; + + let NetworkServerMessage { + message: Some(network_server_message::Message::Request(Request {})), + } = msg + else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Received invalid start message from server", + )); + }; + + Ok(Self { stream }) + } + + /// Will consume the connection, and fetch the servers info. + pub async fn send_get_info(mut self) -> io::Result { + _ = write_message( + &mut self.stream, + NetworkClientMessage { + message: Some(network_client_message::Message::GetInfo(GetInfo {})), + }, + ) + .await; + + let message = + read_message::(&mut self.stream).await?; + + let NetworkServerMessage { + message: Some(network_server_message::Message::GotInfo(msg)), + } = message + else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "sent for info got different message back", + )); + }; + + Ok(msg) + } + + /// consumes this struct and returns a tuple of the sernding and receiving ahlfs of teh connected conneciton + pub async fn send_connect( + mut self, + uuid: Uuid, + username: String, + ) -> io::Result<(ServerWriterConnection, ServerReaderConnection)> { + _ = write_message( + &mut self.stream, + NetworkClientMessage { + message: Some(network_client_message::Message::Connect(Connect { + username, + uuid: uuid.to_string(), + })), + }, + ) + .await; + + let message = + read_message::(&mut self.stream).await?; + + let NetworkServerMessage { + message: Some(network_server_message::Message::Connected(_)), + } = message + else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "sent connect got different message back or failed to connect", + )); + }; + + Ok(self.into()) + } +} + +impl From + for (ServerWriterConnection, ServerReaderConnection) +{ + fn from(value: NetworkConnection) -> Self { + let (read_half, write_half) = split(value.stream); + ( + ServerWriterConnection::new(write_half), + ServerReaderConnection::new(read_half), + ) + } +} diff --git a/foundation/src/client/server_reader_connection.rs b/foundation/src/client/server_reader_connection.rs new file mode 100644 index 0000000..a13e6bd --- /dev/null +++ b/foundation/src/client/server_reader_connection.rs @@ -0,0 +1,26 @@ +use std::io; + +use protocol::prelude::ConnectedServerMessage; +use tokio::{io::ReadHalf, net::TcpStream}; + +use crate::networking::protobuf::read_message; + +pub struct ServerReaderConnection { + reader: ReadHalf, +} + +impl ServerReaderConnection { + pub(crate) fn new(read_half: ReadHalf) -> Self { + Self { reader: read_half } + } + + // move to other one + pub async fn get_message(&mut self) -> io::Result { + let message = read_message::>( + &mut self.reader, + ) + .await + .unwrap(); + Ok(message) + } +} diff --git a/foundation/src/client/server_writer_connection.rs b/foundation/src/client/server_writer_connection.rs new file mode 100644 index 0000000..c60208e --- /dev/null +++ b/foundation/src/client/server_writer_connection.rs @@ -0,0 +1,14 @@ +use tokio::{io::WriteHalf, net::TcpStream}; + +#[allow(dead_code)] +pub struct ServerWriterConnection { + writer: WriteHalf, +} + +impl ServerWriterConnection { + pub(crate) fn new(writer: WriteHalf) -> Self { + Self { writer } + } + + pub async fn request_clients(&mut self) {} +} diff --git a/foundation/src/connection.rs b/foundation/src/connection.rs deleted file mode 100644 index fe3064b..0000000 --- a/foundation/src/connection.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::{ - io::{Error, ErrorKind, Write}, - mem, - sync::Arc, -}; - -use serde::{de::DeserializeOwned, Serialize}; -use tokio::{ - io, - io::{AsyncBufReadExt, AsyncWriteExt, BufReader, ReadHalf, WriteHalf}, - net::{TcpStream, ToSocketAddrs}, - sync::Mutex, -}; - -#[derive(Debug)] -pub struct Connection { - stream_rx: Mutex>>>, - stream_tx: Mutex>>, -} - -impl Connection { - pub fn new() -> Arc { - Arc::new(Connection { - stream_rx: Mutex::new(None), - stream_tx: Mutex::new(None), - }) - } - - pub async fn connect(&self, host: T) -> Result<(), Error> { - let connection = TcpStream::connect(host).await?; - let (rd, wd) = io::split(connection); - - let mut writer_lock = self.stream_tx.lock().await; - let mut reader_lock = self.stream_rx.lock().await; - - let _ = mem::replace(&mut *writer_lock, Some(wd)); - let _ = mem::replace(&mut *reader_lock, Some(BufReader::new(rd))); - - Ok(()) - } - - pub async fn write(&self, message: T) -> Result<(), Error> - where - T: Serialize, - { - let mut out_buffer = Vec::new(); - let out = serde_json::to_string(&message).unwrap(); - let mut writer_lock = self.stream_tx.lock().await; - let old = mem::replace(&mut *writer_lock, None); - writeln!(&mut out_buffer, "{}", out)?; - - let Some(mut writer) = old else { - return Err(Error::new(ErrorKind::Interrupted, "Writer does not exist")); - }; - - writer.write_all(&out_buffer).await?; - writer.flush().await?; - let _ = mem::replace(&mut *writer_lock, Some(writer)); - Ok(()) - } - - pub async fn read(&self) -> Result - where - T: DeserializeOwned, - { - let mut buffer = String::new(); - let mut reader_lock = self.stream_rx.lock().await; - let old = mem::replace(&mut *reader_lock, None); - - if let Some(mut reader) = old { - let _ = reader.read_line(&mut buffer).await?; - let _ = mem::replace(&mut *reader_lock, Some(reader)); - Ok(serde_json::from_str(&buffer).unwrap()) - } else { - Err(Error::new(ErrorKind::Interrupted, "Reader does not exist")) - } - } -} - -impl From for Connection { - fn from(stream: TcpStream) -> Self { - let (rd, wd) = io::split(stream); - Connection { - stream_tx: Mutex::new(Some(wd)), - stream_rx: Mutex::new(Some(BufReader::new(rd))), - } - } -} - -#[cfg(test)] -mod test { - use std::{future::Future, io::Error, panic}; - - use serde::{Deserialize, Serialize}; - use tokio::net::TcpListener; - - use crate::connection::Connection; - - #[derive(Serialize, Deserialize, Debug, PartialEq)] - enum TestMessages { - Ping, - Pong, - } - - #[tokio::test] - async fn a() -> Result<(), Error> { - wrap_setup(|port| async move { - println!("{}", port); - let connection = Connection::new(); - connection - .connect(format!("localhost:{}", &port)) - .await - .unwrap(); - connection.write(&TestMessages::Ping).await.unwrap(); - let res = connection.read::().await.unwrap(); - - assert_eq!(res, TestMessages::Pong); - }) - .await - } - - async fn wrap_setup(test: T) -> Result<(), std::io::Error> - where - T: FnOnce(u16) -> F + panic::UnwindSafe, - F: Future, - { - let server = TcpListener::bind("localhost:0").await?; - let addr = server.local_addr()?; - - // create tokio server execution - tokio::spawn(async move { - while let Ok((stream, addr)) = server.accept().await { - use TestMessages::{Ping, Pong}; - - println!("[server]: Connected {}", &addr); - let connection = Connection::from(stream); - if let Ok(Ping) = connection.read::().await { - connection.write::(Pong).await.unwrap() - } - } - }); - - test(addr.port()).await; - Ok(()) - } -} diff --git a/foundation/src/encryption/mod.rs b/foundation/src/encryption/mod.rs deleted file mode 100644 index 0374cd8..0000000 --- a/foundation/src/encryption/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -// use openssl::sha::sha256; -// use openssl::symm::{Cipher, Crypter, Mode}; - -#[cfg(test)] -mod test { - use openssl::{ - sha::sha256, - symm::{Cipher, Crypter, Mode}, - }; - - #[test] - fn testEncryption() { - let plaintext = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.".as_bytes(); - let key = sha256(b"This is a key"); - let IV = b"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; - - let encrypter = - Crypter::new(Cipher::aes_256_gcm(), Mode::Encrypt, &key, Some(IV)); - let mut ciphertext = vec![0u8; 1024]; - let cipherlen = encrypter - .unwrap() - .update(plaintext, ciphertext.as_mut_slice()) - .unwrap(); - - let decrypter = - Crypter::new(Cipher::aes_256_gcm(), Mode::Decrypt, &key, Some(IV)); - let mut decrypted = vec![0u8; 1024]; - decrypter - .unwrap() - .update(&ciphertext[..cipherlen], decrypted.as_mut_slice()) - .unwrap(); - - println!("{:?}", plaintext); - println!("{:?}", ciphertext.as_slice()); - println!("{:?}", decrypted.as_slice()); - - println!("{:?}", plaintext.len()); - println!("{:?}", ciphertext.len()); - println!("{:?}", decrypted.len()); - } -} diff --git a/foundation/src/event/event.rs b/foundation/src/event/event.rs deleted file mode 100644 index 166328f..0000000 --- a/foundation/src/event/event.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::collections::HashMap; - -use futures::channel::oneshot::{channel, Receiver, Sender}; - -use crate::event::{ - event_result::EventResultBuilder, - EventResult, - EventResultType, -}; - -/// # Eventw -/// Object that holds details about an event being passed through the application. -/// -/// ## Properties -/// - r#type: The event type -/// - args: A hashmap of arguments to be carried by the event -/// - sender: The sender to send the result for the event. -/// - receiver: The reciever of the event result from the event. -pub struct Event -where - T: Sync + Send, -{ - pub r#type: T, - args: HashMap, - sender: Sender, - receiver: Option>, -} - -impl Event -where - T: Sync + Send, -{ - /// Fetches an argument from the arguments of the event. - pub fn get_arg(&self, key: String) -> Option { - self.args.get(&key).cloned() - } - - /// Creates an event result using the sender of the event. - /// This consumes the event. - pub fn respond(self, result_type: EventResultType) -> EventResultBuilder { - EventResult::create(result_type, self.sender) - } - - /// Used to await the result of the event if required. - pub fn get_reciever(&mut self) -> Receiver { - self.receiver.take().unwrap() - } -} - -pub struct EventBuilder { - #[allow(dead_code)] - r#type: T, - - #[allow(dead_code)] - args: HashMap, - - #[allow(dead_code)] - sender: Sender, - - #[allow(dead_code)] - receiver: Option>, -} - -impl EventBuilder { - #[allow(dead_code)] - pub(super) fn new(r#type: T) -> EventBuilder { - let (sender, receiver) = channel(); - EventBuilder { - r#type, - args: HashMap::new(), - sender, - receiver: Some(receiver), - } - } - - pub fn add_arg, V: Into>( - mut self, - key: K, - value: V, - ) -> Self { - self.args.insert(key.into(), value.into()); - self - } - - #[allow(dead_code)] - pub(crate) fn build(self) -> Event - where - T: Sync + Send, - { - Event { - r#type: self.r#type, - args: self.args, - sender: self.sender, - receiver: self.receiver, - } - } -} diff --git a/foundation/src/event/event_result.rs b/foundation/src/event/event_result.rs deleted file mode 100644 index 3a7e57e..0000000 --- a/foundation/src/event/event_result.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::collections::HashMap; - -use futures::channel::oneshot::Sender; - -pub enum EventResultType { - Success, - NoResponse, - InvalidArgs, - InvalidCode, - Other(String), -} - -pub struct EventResult { - code: EventResultType, - args: HashMap, -} - -impl EventResult { - pub fn create( - result_type: EventResultType, - sender: Sender, - ) -> EventResultBuilder { - EventResultBuilder::new(result_type, sender) - } -} - -/// # EventResultBuilder -/// Builds the result of an event -pub struct EventResultBuilder { - code: EventResultType, - args: HashMap, - sender: Sender, -} - -impl EventResultBuilder { - pub(self) fn new( - result_type: EventResultType, - sender: Sender, - ) -> Self { - Self { - code: result_type, - args: HashMap::default(), - sender, - } - } - - pub fn add_arg(mut self, key: String, value: String) -> Self { - self.args.insert(key, value); - self - } - - pub fn send(self) { - self - .sender - .send(EventResult { - code: self.code, - args: self.args, - }) - .ok(); - } -} diff --git a/foundation/src/event/mod.rs b/foundation/src/event/mod.rs deleted file mode 100644 index caa0d30..0000000 --- a/foundation/src/event/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[allow(clippy::module_inception)] -mod event; -mod event_result; -mod responder; - -pub use event::{Event, EventBuilder}; -pub use event_result::{EventResult, EventResultType}; - -pub use self::responder::IResponder; diff --git a/foundation/src/event/responder.rs b/foundation/src/event/responder.rs deleted file mode 100644 index 0d1753d..0000000 --- a/foundation/src/event/responder.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::sync::Weak; - -use crate::event::Event; - -pub trait IResponder -where - T: Sync + Send, -{ - fn post_event(&self, event: Event) { - if let Some(next) = self.get_next() { - if let Some(next) = next.upgrade() { - next.post_event(event); - return; - } - } - self.r#final(event); - } - fn get_next(&self) -> Option>>; - fn on_event(&self, event: Event); - fn r#final(&self, _event: Event) {} -} diff --git a/foundation/src/lib.rs b/foundation/src/lib.rs index 8e63b55..a24d4ff 100644 --- a/foundation/src/lib.rs +++ b/foundation/src/lib.rs @@ -1,17 +1,14 @@ -extern crate core; -pub mod connection; -pub mod encryption; -pub mod event; +pub mod client; pub mod messages; pub mod models; +pub mod networking; pub mod prelude; -pub mod test; use serde::{Deserialize, Serialize}; use uuid::Uuid; /** - * #ClientDetails. + * # ClientDetails. * This defines the fileds a client would want to send when connecitng * uuid: the unique id of the user. * username: the users user name. diff --git a/foundation/src/messages/client.rs b/foundation/src/messages/client.rs index 065a54e..050dc36 100644 --- a/foundation/src/messages/client.rs +++ b/foundation/src/messages/client.rs @@ -5,7 +5,7 @@ use crate::{models::message::Message, ClientDetails}; /// This enum defined the message that the server will receive from a client /// This uses the serde library to transform to and from json. -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] #[serde(tag = "type")] pub enum ClientStreamIn { GetClients, diff --git a/foundation/src/messages/network.rs b/foundation/src/messages/network.rs index 0cff3b6..3ef751f 100644 --- a/foundation/src/messages/network.rs +++ b/foundation/src/messages/network.rs @@ -23,7 +23,7 @@ pub enum NetworkSockOut { server_name: String, server_owner: String, }, - Connecting, + Connected, Error, } @@ -42,7 +42,7 @@ impl PartialEq for NetworkSockOut { server_name: name_other, }, ) => server_name == name_other && server_owner == owner_other, - (NetworkSockOut::Connecting, NetworkSockOut::Connecting) => true, + (NetworkSockOut::Connected, NetworkSockOut::Connected) => true, _ => false, } } diff --git a/foundation/src/models/message.rs b/foundation/src/models/message.rs index 4e2021d..ebd5b16 100644 --- a/foundation/src/models/message.rs +++ b/foundation/src/models/message.rs @@ -4,10 +4,10 @@ use uuid::Uuid; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Message { - id: Uuid, - from: Uuid, - content: String, - time: DateTime, + pub id: Uuid, + pub from: Uuid, + pub content: String, + pub time: DateTime, } impl Message { diff --git a/foundation/src/networking/json.rs b/foundation/src/networking/json.rs new file mode 100644 index 0000000..242d087 --- /dev/null +++ b/foundation/src/networking/json.rs @@ -0,0 +1,51 @@ +use std::io; + +use serde::{de::DeserializeOwned, Serialize}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; + +pub async fn write_message(stream: &mut S, message: M) +where + S: AsyncWrite + AsyncWriteExt + Unpin, + M: Serialize, +{ + let mut message = serde_json::to_string(&message).unwrap(); + message.push('\n'); + _ = stream.write(message.as_bytes()).await; +} + +// todo: Handle error properly +pub async fn read_message(stream: &mut S) -> io::Result +where + S: AsyncRead + AsyncReadExt + Unpin, + M: DeserializeOwned, +{ + let string = read_line(stream).await?; + Ok(serde_json::from_str(&string).unwrap()) +} + +#[allow(clippy::redundant_guards, clippy::needless_range_loop)] +async fn read_line(stream: &mut S) -> Result +where + S: AsyncRead + AsyncReadExt + Unpin, +{ + let mut buf = vec![0; 1024]; + let mut newline_found = false; + let mut result = Vec::new(); + loop { + let n = match stream.read(&mut buf).await { + Ok(n) if n == 0 => return Ok(String::from_utf8(result).unwrap()), + Ok(n) => n, + Err(e) => return Err(e), + }; + for i in 0..n { + if buf[i] == b'\n' { + newline_found = true; + break; + } + result.push(buf[i]); + } + if newline_found { + return Ok(String::from_utf8(result).unwrap()); + } + } +} diff --git a/foundation/src/networking/mod.rs b/foundation/src/networking/mod.rs new file mode 100644 index 0000000..71334a2 --- /dev/null +++ b/foundation/src/networking/mod.rs @@ -0,0 +1,2 @@ +pub mod json; +pub mod protobuf; diff --git a/foundation/src/networking/protobuf.rs b/foundation/src/networking/protobuf.rs new file mode 100644 index 0000000..d4468ed --- /dev/null +++ b/foundation/src/networking/protobuf.rs @@ -0,0 +1,66 @@ +use std::io::{self, ErrorKind}; + +use prost::{ + bytes::{BufMut, Bytes, BytesMut}, + Message, +}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; + +pub async fn write_message(stream: &mut S, message: T) -> io::Result<()> +where + T: Message + Default, + S: AsyncWrite + AsyncWriteExt + Unpin, +{ + let message = encode_message::(&message)?; + stream.write_all(&message).await?; + Ok(()) +} + +pub fn encode_message(msg: &T) -> io::Result +where + T: Message, +{ + let length = msg.encoded_len(); + let mut buffer = BytesMut::with_capacity(4 + length); + buffer.put_u32(length as u32); + let encode_result = msg.encode(&mut buffer); + if let Err(err) = encode_result { + return Err(io::Error::new( + ErrorKind::InvalidInput, + format!("message encoding failed: {:?}", err), + )); + } + + Ok(buffer.into()) +} + +pub async fn read_message(stream: &mut S) -> io::Result +where + T: Message + Default, + S: AsyncRead + AsyncReadExt + Unpin, +{ + let size = stream.read_u32().await?; + + let mut buffer = BytesMut::with_capacity(size as usize); + unsafe { buffer.set_len(size as usize) }; + + stream.read_exact(&mut buffer).await?; + + let message = decode_message::(buffer.into())?; + + Ok(message) +} + +pub fn decode_message(buffer: Bytes) -> io::Result +where + T: Message + Default, +{ + let msg_result = T::decode(buffer); + match msg_result { + Ok(msg) => Ok(msg), + Err(err) => Err(io::Error::new( + ErrorKind::InvalidInput, + format!("message decoding failed: {:?}", err), + )), + } +} diff --git a/foundation/src/prelude.rs b/foundation/src/prelude.rs index 8908ab9..b94c0d4 100644 --- a/foundation/src/prelude.rs +++ b/foundation/src/prelude.rs @@ -1,55 +1 @@ -use std::{ - sync::{Arc, Weak}, - time::Duration, -}; - -use async_trait::async_trait; -use tokio::time::sleep; - -/// # IManager -/// This is used with all managers to implement multitasking -/// -/// ## Methods -/// - init: gets executed once before a tokio task is created -/// - run: gets called once every tick in a tokio task -/// - start: runs the init function then creates the tokio task for the run function -#[async_trait] -pub trait IManager { - /// This defines some setup before the tokio loop is started - async fn init(self: &Arc) - where - Self: Send + Sync + 'static, - { - } - - /// this is used to get a future that can be awaited - async fn run(self: &Arc); - - /// This is used to start a future through tokio - fn start(self: &Arc) - where - Self: Send + Sync + 'static, - { - let weak_self: Weak = Arc::downgrade(self); - - // this looks horrid but works - tokio::spawn(async move { - let weak_self = weak_self.clone(); - - let a = weak_self.upgrade().unwrap(); - a.init().await; - drop(a); - - loop { - sleep(Duration::new(1, 0)).await; - if let Some(manager) = Weak::upgrade(&weak_self) { - manager.run().await - } - } - }); - } -} - -trait Visitor { - fn visit(&self, message: T); -} +pub use protocol::prelude::*; diff --git a/foundation/src/test/connection_pair.rs b/foundation/src/test/connection_pair.rs deleted file mode 100644 index 00c7000..0000000 --- a/foundation/src/test/connection_pair.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::{io::Error, net::SocketAddr, sync::Arc}; - -use tokio::{ - join, - net::{TcpListener, TcpStream}, -}; - -use crate::connection::Connection; - -pub async fn create_connection_pair( -) -> Result<(Arc, (Arc, SocketAddr)), Error> { - let listener: TcpListener = TcpListener::bind("localhost:0000").await?; - - let port = listener.local_addr()?.port(); - - let (server_res, client_res) = join!( - async { TcpStream::connect(format!("localhost:{}", port)).await }, - async { listener.accept().await } - ); - - let (client, addr) = client_res?; - let server = Arc::new(Connection::from(server_res?)); - let client = Arc::new(Connection::from(client)); - Ok((server, (client, addr))) -} diff --git a/foundation/src/test/mod.rs b/foundation/src/test/mod.rs deleted file mode 100644 index 9e53c5c..0000000 --- a/foundation/src/test/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod connection_pair; - -pub use connection_pair::create_connection_pair; diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml new file mode 100644 index 0000000..9259ded --- /dev/null +++ b/protocol/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "protocol" +version = "0.1.0" +authors = ["michael-bailey "] +edition = "2018" + +[lib] + +[dependencies] +chrono = "0.4" +uuid = {version = "1.1.2", features = ["serde", "v4"]} +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +crossbeam = "0.8.0" +crossbeam-channel = "0.5.0" +zeroize = "1.1.0" +tokio = { version = "1.9.0", features = ["full"] } +futures = "0.3.16" +async-trait = "0.1.52" +toml = "0.8.8" + +# prost setup +bytes.workspace = true +prost.workspace = true + +[build-dependencies] +prost-build.workspace = true + diff --git a/protocol/build.rs b/protocol/build.rs new file mode 100644 index 0000000..27b5585 --- /dev/null +++ b/protocol/build.rs @@ -0,0 +1,10 @@ +use std::io::Result; + +// Use this in build.rs +fn main() -> Result<()> { + prost_build::compile_protos( + &["src/proto/network.proto", "src/proto/connected.proto"], + &["src/proto"], + )?; + Ok(()) +} diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs new file mode 100644 index 0000000..dead048 --- /dev/null +++ b/protocol/src/lib.rs @@ -0,0 +1,5 @@ +mod proto; + +pub mod prelude { + pub use super::proto::*; +} diff --git a/protocol/src/proto/connected.proto b/protocol/src/proto/connected.proto new file mode 100644 index 0000000..459c621 --- /dev/null +++ b/protocol/src/proto/connected.proto @@ -0,0 +1,81 @@ +syntax = "proto3"; + +package chatkit.messages; + +// messages from the client when connected. +message ConnectedClientMessage { + oneof message { + GetClients get_clients = 1; + GetGlobalMessages get_global_message = 2; + SendGlobalMessage send_global_message = 3; + SendPrivateMessage send_private_message = 4; + Disconnect disconnect = 5; + } +} + +message GetClients {} +message GetGlobalMessages {} + +message SendGlobalMessage { + string content = 1; +} + +message SendPrivateMessage { + string uuid = 1; + string to = 2; + string content = 3; +} + +message Disconnect {} + + +// messages from the Server when connected. +message ConnectedServerMessage { + oneof message { + ConnectedClients connected_clients = 1; + GlobalMessages global_messages = 2; + PrivateMessage private_message = 3; + Disconnected disconnected = 4; + GlobalMessage global_message = 5; + ClientConnected client_connected = 6; + ClientDisconnected client_disconnected = 7; + } +} + +message ConnectedClients { + repeated ClientDetails clients = 1; +} + +message ClientConnected { + ClientDetails details = 1; +} + +message ClientDisconnected { + string uuid = 1; +} + +message ClientDetails { + string uuid = 1; + string name = 2; + string address = 3; +} + +message GlobalMessages { + repeated GlobalMessage messages = 1; +} + +message GlobalMessage { + string uuid = 1; + string from = 2; + string content = 3; +} + +message PrivateMessage { + string uuid = 1; + string from = 2; + string content = 3; +} + +message Disconnected { + string reason = 1; +} \ No newline at end of file diff --git a/protocol/src/proto/mod.rs b/protocol/src/proto/mod.rs new file mode 100644 index 0000000..311b6f5 --- /dev/null +++ b/protocol/src/proto/mod.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/chatkit.messages.rs")); diff --git a/protocol/src/proto/network.proto b/protocol/src/proto/network.proto new file mode 100644 index 0000000..4432321 --- /dev/null +++ b/protocol/src/proto/network.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +package chatkit.messages; + +// Network messages sent from the client. +message NetworkClientMessage { + oneof message { + GetInfo get_info = 1; + Connect connect = 2; + } +} + +message GetInfo {} + +message Connect { + string username = 1; + string uuid = 2; +} + +// Network messages sent from the server. +message NetworkServerMessage { + oneof message { + Request request = 1; + Info got_info = 2; + Connected connected = 3; + + } +} + +message Request {} + +message Info { + string server_name = 1; + string owner = 2; +} + +message Connected {} diff --git a/scripts/test.lua b/scripts/test.lua deleted file mode 100644 index 83f40d6..0000000 --- a/scripts/test.lua +++ /dev/null @@ -1,6 +0,0 @@ -print("Test Script") - -print(Server.ClientManager:getCount()) - - -print("Test Script") \ No newline at end of file diff --git a/server/Cargo.toml b/server/Cargo.toml index 47c5f03..d6bd8f0 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,22 +21,26 @@ path = "src/main.rs" [dependencies] chrono = "0.4" clap = {version = "4.4.8", features = ["derive"]} -uuid = {version = "1.1.2", features = ["serde", "v4"]} -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" +uuid.workspace = true +serde.workspace = true +serde_json.workspace = true crossbeam = "0.8.0" crossbeam-channel = "0.5.0" zeroize = "1.1.0" openssl = "0.10.33" -tokio = { version = "1.9.0", features = ["full"] } +tokio.workspace = true futures = "0.3.16" -async-trait = "0.1.52" +async-trait = "0.1.80" actix = "0.13" rhai = {version = "1.7.0"} mlua = { version = "0.9.2", features=["lua54", "async", "serde", "macros", "vendored"] } libloading = "0.8.1" toml = "0.8.8" -aquamarine = "0.3.2" + tokio-stream = "0.1.9" +# protobuf +bytes.workspace = true +prost.workspace = true + foundation = {path = '../foundation'} \ No newline at end of file diff --git a/server/src/chat/mod.rs b/server/src/chat/mod.rs new file mode 100644 index 0000000..3960c5f --- /dev/null +++ b/server/src/chat/mod.rs @@ -0,0 +1,29 @@ +use foundation::prelude::GlobalMessage; + +pub struct ChatManager { + messages: Vec, +} + +impl ChatManager { + pub fn new() -> Self { + Self { + messages: Vec::new(), + } + } + + pub fn add_message(&mut self, message: GlobalMessage) { + println!("[ChatManager] added new global message {:?}", message); + self.messages.push(message); + } + + pub fn get_messages(&mut self) -> Vec { + println!("[ChatManager] got all messages"); + self.messages.clone() + } +} + +impl Default for ChatManager { + fn default() -> Self { + Self::new() + } +} diff --git a/server/src/client_management/chat_manager/actor.rs b/server/src/client_management/chat_manager/actor.rs deleted file mode 100644 index 6460ab7..0000000 --- a/server/src/client_management/chat_manager/actor.rs +++ /dev/null @@ -1,87 +0,0 @@ -use actix::{Actor, Addr, Context, Handler}; -use foundation::models::message::Message; -use uuid::Uuid; - -use crate::client_management::chat_manager::messages::{ - ChatManagerDataMessage, - ChatManagerDataResponse, - ChatManagerMessage, -}; - -pub(crate) struct ChatManager { - messages: Vec, -} - -impl ChatManager { - pub fn new() -> Addr { - Self { - messages: Vec::new(), - } - .start() - } - - // no need for a remove methods because this is a read only system - fn add_message( - &mut self, - _ctx: &mut Context, - id: Uuid, - content: String, - ) { - println!( - "[ChatManager] add_message id: {:?} content: {:?}", - id, content - ); - self.messages.push(Message::new(id, content)) - } - - fn get_messages(&self, _ctx: &mut Context) -> ChatManagerDataResponse { - println!("[ChatManager] getting messages"); - ChatManagerDataResponse::GotMessages(self.messages.clone()) - } - - fn get_message( - &self, - _ctx: &mut Context, - index: usize, - ) -> ChatManagerDataResponse { - println!("[ChatManager] getting message index: {:?}", index); - ChatManagerDataResponse::GotMessage(self.messages.get(index).cloned()) - } -} - -impl Actor for ChatManager { - type Context = Context; -} - -impl Handler for ChatManager { - type Result = (); - - fn handle( - &mut self, - msg: ChatManagerMessage, - ctx: &mut Self::Context, - ) -> Self::Result { - println!("[ChatManager] got message: {:?}", msg); - match msg { - ChatManagerMessage::AddMessage(id, content) => { - self.add_message(ctx, id, content) - } - } - } -} - -impl Handler for ChatManager { - type Result = ChatManagerDataResponse; - - fn handle( - &mut self, - msg: ChatManagerDataMessage, - ctx: &mut Self::Context, - ) -> Self::Result { - println!("[ChatManager] got message: {:?}", msg); - match msg { - ChatManagerDataMessage::GetMessages => self.get_messages(ctx), - ChatManagerDataMessage::GetMessage(index) => self.get_message(ctx, index), - } - } -} diff --git a/server/src/client_management/chat_manager/messages.rs b/server/src/client_management/chat_manager/messages.rs deleted file mode 100644 index 67d70de..0000000 --- a/server/src/client_management/chat_manager/messages.rs +++ /dev/null @@ -1,23 +0,0 @@ -use actix::{Message as ActixMessage, MessageResponse}; -use foundation::models::message::Message; -use uuid::Uuid; - -#[derive(ActixMessage, Debug)] -#[rtype(result = "()")] -pub enum ChatManagerMessage { - AddMessage(Uuid, String), -} - -#[allow(dead_code)] -#[derive(ActixMessage, Debug)] -#[rtype(result = "ChatManagerDataResponse")] -pub enum ChatManagerDataMessage { - GetMessages, - GetMessage(usize), -} - -#[derive(MessageResponse)] -pub enum ChatManagerDataResponse { - GotMessages(Vec), - GotMessage(Option), -} diff --git a/server/src/client_management/chat_manager/mod.rs b/server/src/client_management/chat_manager/mod.rs deleted file mode 100644 index e8913b0..0000000 --- a/server/src/client_management/chat_manager/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! Contains all the structures for managing chat storage. -//! it contains: -//! - ChatManager -//! - Messages -//! - Mesage type - -mod actor; - -mod messages; - -pub(crate) use actor::ChatManager; -pub(crate) use messages::{ - ChatManagerDataMessage, - ChatManagerDataResponse, - ChatManagerMessage, -}; diff --git a/server/src/client_management/client/actor.rs b/server/src/client_management/client/actor.rs deleted file mode 100644 index d8675eb..0000000 --- a/server/src/client_management/client/actor.rs +++ /dev/null @@ -1,287 +0,0 @@ -use actix::{Actor, Addr, AsyncContext, Context, Handler, WeakRecipient}; -use foundation::{ - messages::client::{ClientStreamIn, ClientStreamOut}, - ClientDetails, -}; -use uuid::Uuid; - -use crate::{ - client_management::client::messages::{ - ClientDataMessage, - ClientDataResponse, - ClientMessage, - ClientObservableMessage, - }, - network::{Connection, ConnectionObservableOutput}, - prelude::messages::{ConnectionMessage, ObservableMessage}, -}; - -/// # Client -/// This represents a connected client. -/// it will handle received message from a connection. -pub struct Client { - connection: Addr, - details: ClientDetails, - observers: Vec>, -} - -impl Client { - pub(crate) fn new( - connection: Addr, - details: ClientDetails, - ) -> Addr { - Client { - connection, - details, - observers: Vec::default(), - } - .start() - } - - #[inline] - fn get_clients(&self, ctx: &mut Context) { - println!("[Client] getting clients"); - use ClientObservableMessage::GetClients; - self.broadcast(GetClients(ctx.address().downgrade())); - } - - #[inline] - fn get_messages(&self, ctx: &mut Context) { - println!("[Client] getting messages"); - use ClientObservableMessage::GetGlobalMessages; - self.broadcast(GetGlobalMessages(ctx.address().downgrade())); - } - - #[inline] - fn send_message(&self, ctx: &mut Context, to: Uuid, content: String) { - println!("[Client] sending message"); - use ClientObservableMessage::Message; - self.broadcast(Message(ctx.address().downgrade(), to, content)); - } - - #[inline] - fn send_gloal_message(&self, ctx: &mut Context, content: String) { - println!("[Client] sending global message"); - use ClientObservableMessage::GlobalMessage; - self.broadcast(GlobalMessage(ctx.address().downgrade(), content)); - } - - #[inline] - fn disconnect(&self, _ctx: &mut Context) { - println!("[Client] disconnecting"); - use ClientObservableMessage::Disconnecting; - self.broadcast(Disconnecting(self.details.uuid)); - } - - #[inline] - fn broadcast(&self, message: ClientObservableMessage) { - println!("[Client] broadcasting message"); - for recp in &self.observers { - if let Some(upgraded) = recp.upgrade() { - upgraded.do_send(message.clone()); - } - } - } - - - pub(crate) fn _error(&self, msg: String) { - println!("[Client] sending error: {}", msg); - use serde_json::to_string; - use ConnectionMessage::SendData; - - let msg = to_string::(&ClientStreamOut::Error { msg }) - .expect("[Client] This should not fail"); - - self.connection.do_send(SendData(msg)); - } -} - -impl Actor for Client { - type Context = Context; - - // tells the client that it has been connected. - fn started(&mut self, ctx: &mut Self::Context) { - use foundation::messages::client::ClientStreamOut::Connected; - use serde_json::to_string; - - use crate::{ - network::ConnectionMessage::SendData, - prelude::messages::ObservableMessage::Subscribe, - }; - println!("[Client] started"); - self - .connection - .do_send::>(Subscribe( - ctx.address().recipient().downgrade(), - )); - self - .connection - .do_send(SendData(to_string::(&Connected).unwrap())); - } - - fn stopped(&mut self, ctx: &mut Self::Context) { - use foundation::messages::client::ClientStreamOut::Disconnected; - use serde_json::to_string; - - use crate::{ - network::ConnectionMessage::SendData, - prelude::messages::ObservableMessage::Unsubscribe, - }; - - println!("[Client] stopped"); - - self - .connection - .do_send::>(Unsubscribe( - ctx.address().recipient().downgrade(), - )); - self.connection.do_send(SendData( - to_string::(&Disconnected).unwrap(), - )); - } -} - -impl Handler for Client { - type Result = ClientDataResponse; - fn handle( - &mut self, - msg: ClientDataMessage, - _ctx: &mut Self::Context, - ) -> Self::Result { - match msg { - ClientDataMessage::Details => { - ClientDataResponse::Details(self.details.clone()) - } - _ => todo!(), - } - } -} - -// Handles incoming messages to the client. -impl Handler for Client { - type Result = (); - fn handle( - &mut self, - msg: ClientMessage, - _ctx: &mut Self::Context, - ) -> Self::Result { - use foundation::messages::client::ClientStreamOut::{ - ConnectedClients, - GlobalChatMessages, - GlobalMessage, - UserMessage, - }; - use serde_json::to_string; - - use crate::{ - client_management::client::messages::ClientMessage::{ - ClientList, - ClientlySentMessage, - GloballySentMessage, - MessageList, - }, - network::ConnectionMessage::SendData, - }; - - match msg { - ClientList(clients) => self.connection.do_send(SendData( - to_string::(&ConnectedClients { clients }) - .expect("[Client] Failed to encode string"), - )), - - MessageList(messages) => self.connection.do_send(SendData( - to_string::(&GlobalChatMessages { messages }) - .expect("[Client] Failed to encode string"), - )), - - ClientlySentMessage { content, from } => { - self.connection.do_send(SendData( - to_string::(&UserMessage { from, content }) - .expect("[Client] Failed to encode string"), - )) - } - - GloballySentMessage { from, content } => { - self.connection.do_send(SendData( - to_string::(&GlobalMessage { from, content }) - .expect("[Client] Failed to encode string"), - )) - } - } - } -} - -// Handles outputs from the connection. -impl Handler for Client { - type Result = (); - - fn handle( - &mut self, - msg: ConnectionObservableOutput, - ctx: &mut Self::Context, - ) -> Self::Result { - use foundation::messages::client::ClientStreamIn::{ - Disconnect, - GetClients, - GetMessages, - SendGlobalMessage, - SendMessage, - }; - use serde_json::from_str; - - use crate::network::ConnectionObservableOutput::RecvData; - - match msg { - RecvData(_sender, data) => { - if let Ok(msg) = from_str::(data.as_str()) { - match msg { - GetClients => self.get_clients(ctx), - GetMessages => self.get_messages(ctx), - SendMessage { to, content } => self.send_message(ctx, to, content), - SendGlobalMessage { content } => { - self.send_gloal_message(ctx, content) - } - Disconnect => self.disconnect(ctx), - } - } - } - ConnectionObservableOutput::ConnectionClosed(_) => self - .broadcast(ClientObservableMessage::Disconnecting(self.details.uuid)), - - } - } -} - -impl Handler> for Client { - type Result = (); - - fn handle( - &mut self, - msg: ObservableMessage, - _ctx: &mut Self::Context, - ) -> Self::Result { - use crate::prelude::messages::ObservableMessage::{Subscribe, Unsubscribe}; - match msg { - Subscribe(r) => { - println!("[Client] adding subscriber"); - self.observers.push(r); - } - Unsubscribe(r) => { - println!("[Client] removing subscriber"); - let r = r.upgrade(); - self.observers = self - .observers - .clone() - .into_iter() - .filter(|a| a.upgrade() != r) - .collect(); - } - } - } -} - -impl Drop for Client { - fn drop(&mut self) { - println!("[Client] Dropping value") - } -} diff --git a/server/src/client_management/client/messages.rs b/server/src/client_management/client/messages.rs deleted file mode 100644 index 60e3d05..0000000 --- a/server/src/client_management/client/messages.rs +++ /dev/null @@ -1,44 +0,0 @@ -use actix::{Message, MessageResponse, WeakAddr}; -use foundation::{models::message::Message as StoredMessage, ClientDetails}; -use uuid::Uuid; - -use crate::client_management::client::Client; - -/// Message sent ot the clients delegate -#[derive(Message)] -#[rtype(result = "()")] -pub enum ClientMessage { - ClientList(Vec), - MessageList(Vec), - - ClientlySentMessage { from: Uuid, content: String }, - GloballySentMessage { from: Uuid, content: String }, -} - -#[derive(Message)] -#[rtype(result = "ClientDataResponse")] -pub enum ClientDataMessage { - Details, - Uuid, - Username, - Address, -} - -#[derive(MessageResponse)] -pub enum ClientDataResponse { - Details(ClientDetails), - Uuid(Uuid), - Username(String), - Address(String), -} - -/// message that is sent to all observers of the current client. -#[derive(Message, Clone)] -#[rtype(result = "()")] -pub enum ClientObservableMessage { - Message(WeakAddr, Uuid, String), - GlobalMessage(WeakAddr, String), - GetClients(WeakAddr), - GetGlobalMessages(WeakAddr), - Disconnecting(Uuid), -} diff --git a/server/src/client_management/client/mod.rs b/server/src/client_management/client/mod.rs deleted file mode 100644 index 0e94bc0..0000000 --- a/server/src/client_management/client/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod actor; -mod messages; - -pub use actor::Client; -pub use messages::*; diff --git a/server/src/client_management/client_manager.rs b/server/src/client_management/client_manager.rs deleted file mode 100644 index 2148a4e..0000000 --- a/server/src/client_management/client_manager.rs +++ /dev/null @@ -1,336 +0,0 @@ -use std::collections::HashMap; - -use actix::{ - fut::wrap_future, - Actor, - ActorFutureExt, - Addr, - AsyncContext, - Context, - Handler, - WeakAddr, - WeakRecipient, -}; -use foundation::ClientDetails; -use tokio_stream::StreamExt; -use uuid::Uuid; - -use crate::client_management::{ - chat_manager::{ - ChatManager, - ChatManagerDataMessage, - ChatManagerDataResponse, - ChatManagerMessage, - }, - client::{ - Client, - ClientDataMessage, - ClientDataResponse, - ClientDataResponse::Details, - ClientMessage, - ClientObservableMessage, - }, - messages::{ - ClientManagerDataMessage, - ClientManagerDataResponse, - ClientManagerDataResponse::{ClientCount, Clients}, - ClientManagerMessage, - ClientManagerOutput, - }, -}; - -pub struct ClientManager { - clients: HashMap>, - chat_manager: Addr, - _delegate: WeakRecipient, -} - -impl ClientManager { - pub(crate) fn new( - delegate: WeakRecipient, - ) -> Addr { - ClientManager { - _delegate: delegate, - clients: HashMap::new(), - chat_manager: ChatManager::new(), - } - .start() - } - - pub(crate) fn send_client_list( - &self, - ctx: &mut Context, - sender: WeakAddr, - ) { - println!("[ClientManager] sending update to client"); - use crate::client_management::client::ClientMessage::ClientList; - if let Some(to_send) = sender.upgrade() { - let client_addr: Vec> = - self.clients.values().cloned().collect(); - - let collection = tokio_stream::iter(client_addr) - .then(|addr| addr.send(ClientDataMessage::Details)) - .map(|val| { - if let Details(details) = val.unwrap() { - details - } else { - ClientDetails::default() - } - }) - .collect(); - - let fut = wrap_future(async move { - let a: Vec<_> = collection.await; - let _ = to_send.send(ClientList(a)).await; - }); - - ctx.spawn(fut); - } - } - - pub(crate) fn send_global_messages( - &self, - ctx: &mut Context, - sender: WeakAddr, - ) { - if let Some(to_send) = sender.upgrade() { - let fut = wrap_future( - self.chat_manager.send(ChatManagerDataMessage::GetMessages), - ) - .map(move |out, _a, _ctx| { - if let Ok(ChatManagerDataResponse::GotMessages(res)) = out { - to_send.do_send(ClientMessage::MessageList(res)); - } - }); - ctx.spawn(fut); - }; - } - - pub(crate) fn send_message_request( - &self, - ctx: &mut Context, - sender: WeakAddr, - to: Uuid, - content: String, - ) { - println!("[ClientManager] sending message to client"); - let client_addr: Vec> = - self.clients.values().cloned().collect(); - - let collection = tokio_stream::iter(client_addr.clone()) - .then(|addr| addr.send(ClientDataMessage::Details)) - .map(|val| val.unwrap()) - .map(|val: ClientDataResponse| { - if let Details(details) = val { - details - } else { - ClientDetails::default() - } - }) - .collect(); - - let fut = wrap_future(async move { - if let Some(sender) = sender.upgrade() { - let sender_details: ClientDataResponse = - sender.send(ClientDataMessage::Details).await.unwrap(); - - let from = if let Details(details) = sender_details { - details.uuid - } else { - ClientDetails::default().uuid - }; - - let client_details: Vec = collection.await; - let pos = client_details.iter().position(|i| i.uuid == to); - if let Some(pos) = pos { - client_addr[pos] - .send(ClientMessage::ClientlySentMessage { content, from }) - .await - .expect("TODO: panic message"); - } - } - }); - - ctx.spawn(fut); - } - - pub(crate) fn send_global_message_request( - &self, - ctx: &mut Context, - sender: WeakAddr, - content: String, - ) { - println!("[ClientManager] sending message to client"); - use crate::client_management::client::ClientMessage::GloballySentMessage; - - let client_addr: Vec> = - self.clients.values().cloned().collect(); - - if let Some(sender) = sender.upgrade() { - let cm = self.chat_manager.clone(); - - let snd1 = sender.clone(); - let snd2 = sender; - - let cont1 = content.clone(); - let cont2 = content; - - let fut = wrap_future(async move { - println!("[ClientManager] sending to all clients"); - let details: ClientDataResponse = - snd1.send(ClientDataMessage::Details).await.unwrap(); - - let from = if let Details(details) = details { - details.uuid - } else { - ClientDetails::default().uuid - }; - - let collection = tokio_stream::iter(client_addr) - .then(move |addr| { - addr.send(GloballySentMessage { - content: cont1.clone(), - from, - }) - }) - .collect(); - let _: Vec<_> = collection.await; - }); - - let chat_manager_fut = wrap_future(async move { - println!("[ClientManager] storing in chat manager"); - let details: ClientDataResponse = - snd2.send(ClientDataMessage::Details).await.unwrap(); - - let from = if let Details(details) = details { - details.uuid - } else { - ClientDetails::default().uuid - }; - - let _ = cm.send(ChatManagerMessage::AddMessage(from, cont2)).await; - }); - ctx.spawn(fut); - ctx.spawn(chat_manager_fut); - } - } - - fn add_client( - &mut self, - ctx: &mut Context, - uuid: Uuid, - addr: Addr, - ) { - println!("[ClientManager] adding client"); - use crate::prelude::messages::ObservableMessage::Subscribe; - let recp = ctx.address().recipient::(); - println!("[ClientManager] sending subscribe message to client"); - addr.do_send(Subscribe(recp.downgrade())); - self.clients.insert(uuid, addr); - for (_k, v) in self.clients.clone() { - self.send_client_list(ctx, v.downgrade()) - } - } - - fn remove_client(&mut self, ctx: &mut Context, uuid: Uuid) { - println!("[ClientManager] removing client"); - use crate::prelude::messages::ObservableMessage::Unsubscribe; - let recp = ctx.address().recipient::(); - let addr = self.clients.remove(&uuid); - if let Some(addr) = addr { - println!("[ClientManager] sending unsubscribe message to client"); - addr.do_send(Unsubscribe(recp.downgrade())); - } - println!("[ClientManager] sending client list to other clients"); - for (_k, v) in self.clients.iter() { - self.send_client_list(ctx, v.downgrade()) - } - } - - fn disconnect_client( - &mut self, - ctx: &mut Context, - uuid: Uuid, - ) { - println!("[ClientManager] disconnecting client"); - use crate::prelude::messages::ObservableMessage::Unsubscribe; - let recp = ctx.address().recipient::(); - if let Some(addr) = self.clients.remove(&uuid) { - addr.do_send(Unsubscribe(recp.downgrade())); - self.remove_client(ctx, uuid); - } - } -} - -impl Actor for ClientManager { - type Context = Context; - - fn started(&mut self, _ctx: &mut Self::Context) { - println!("[ClientManager] started"); - } -} - -impl Handler for ClientManager { - type Result = (); - fn handle( - &mut self, - msg: ClientManagerMessage, - ctx: &mut Self::Context, - ) -> Self::Result { - use ClientManagerMessage::{AddClient, RemoveClient}; - match msg { - // todo: Add subscription to the client. - AddClient(uuid, addr) => self.add_client(ctx, uuid, addr), - // todo: remove subscription to client. - RemoveClient(uuid) => self.remove_client(ctx, uuid), - } - } -} - -impl Handler for ClientManager { - type Result = (); - - fn handle( - &mut self, - msg: ClientObservableMessage, - ctx: &mut Self::Context, - ) -> Self::Result { - use crate::client_management::client::ClientObservableMessage::{ - Disconnecting, - GetClients, - GetGlobalMessages, - GlobalMessage, - Message, - }; - match msg { - Message(sender, to, content) => { - self.send_message_request(ctx, sender, to, content) - } - GlobalMessage(sender, content) => { - self.send_global_message_request(ctx, sender, content) - } - GetClients(sender) => self.send_client_list(ctx, sender), - GetGlobalMessages(sender) => self.send_global_messages(ctx, sender), - Disconnecting(uuid) => self.disconnect_client(ctx, uuid), - } - } -} - -impl Handler for ClientManager { - type Result = ClientManagerDataResponse; - - fn handle( - &mut self, - msg: ClientManagerDataMessage, - _ctx: &mut Self::Context, - ) -> Self::Result { - match msg { - ClientManagerDataMessage::ClientCount => { - ClientCount(self.clients.values().count()) - } - ClientManagerDataMessage::Clients => { - Clients(self.clients.values().map(|a| a.downgrade()).collect()) - } - } - } -} diff --git a/server/src/client_management/messages.rs b/server/src/client_management/messages.rs deleted file mode 100644 index 067dd85..0000000 --- a/server/src/client_management/messages.rs +++ /dev/null @@ -1,32 +0,0 @@ -use actix::{Addr, Message, MessageResponse, WeakAddr}; -use uuid::Uuid; - -use crate::client_management::{client::Client, ClientManager}; - -#[derive(Message)] -#[rtype(result = "()")] -pub(crate) enum ClientManagerMessage { - AddClient(Uuid, Addr), - #[allow(dead_code)] - RemoveClient(Uuid), -} - -#[derive(Message)] -#[rtype(result = "()")] -pub(crate) enum ClientManagerOutput { - #[allow(dead_code)] - UpdateRequest(Addr), -} - -#[derive(Message)] -#[rtype(result = "ClientManagerDataResponse")] -pub enum ClientManagerDataMessage { - ClientCount, - Clients, -} - -#[derive(MessageResponse)] -pub enum ClientManagerDataResponse { - ClientCount(usize), - Clients(Vec>), -} diff --git a/server/src/client_management/mod.rs b/server/src/client_management/mod.rs deleted file mode 100644 index 5e69979..0000000 --- a/server/src/client_management/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Contains code that handles the lifecycle of connected clients -//! -//! This collects all parts used by the client manager actor -//! -//! It's responsibility is: -//! - to handle client to client communication. -//! - to handle server to client communication. -//! - to handler client lifecycle events such as dicconection. - -mod chat_manager; -pub mod client; -mod client_manager; -mod messages; - -#[allow(unused_imports)] -use chat_manager::{ - ChatManager, - ChatManagerDataMessage, - ChatManagerDataResponse, - ChatManagerMessage, -}; -pub(crate) use client_manager::ClientManager; -pub(crate) use messages::{ - ClientManagerDataMessage, - ClientManagerDataResponse, - ClientManagerMessage, - ClientManagerOutput, -}; diff --git a/server/src/config_manager/arg_parser.rs b/server/src/config_manager/arg_parser.rs deleted file mode 100644 index 05519a3..0000000 --- a/server/src/config_manager/arg_parser.rs +++ /dev/null @@ -1,14 +0,0 @@ -use clap::Parser; - -#[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] -pub struct Arguments { - #[clap(short, long, value_parser = clap::value_parser!(u16).range(1..))] - pub port: Option, - - #[clap(short, long, value_parser)] - pub name: Option, - - #[clap(short, long, value_parser)] - pub owner: Option, -} diff --git a/server/src/config_manager/builder.rs b/server/src/config_manager/builder.rs deleted file mode 100644 index e279fce..0000000 --- a/server/src/config_manager/builder.rs +++ /dev/null @@ -1,32 +0,0 @@ -use actix::{Actor, Addr}; - -use crate::config_manager::{arg_parser::Arguments, ConfigManager}; - -pub(super) struct Builder { - pub(super) file_path: String, - pub(super) args: Option, -} - -impl Builder { - pub(super) fn new() -> Self { - Self { - file_path: "./config_file.toml".to_owned(), - args: None, - } - } - - #[allow(dead_code)] - pub fn config_path(mut self, path: impl Into) -> Self { - self.file_path = path.into(); - self - } - - pub fn args(mut self, args: Arguments) -> Self { - self.args.replace(args); - self - } - - pub(super) fn build(self) -> Addr { - ConfigManager::from(self).start() - } -} diff --git a/server/src/config_manager/config_manager.rs b/server/src/config_manager/config_manager.rs deleted file mode 100644 index 58d888b..0000000 --- a/server/src/config_manager/config_manager.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::{ - collections::BTreeMap, - fs::{File, OpenOptions}, - io::Read, - sync::Once, -}; - -use actix::{Actor, Addr, Context, Handler, Recipient}; -use clap::Parser; -use toml::Value; - -use crate::{ - config_manager::{ - arg_parser::Arguments, - builder::Builder, - messages::{ - ConfigManagerDataMessage, - ConfigManagerDataResponse, - ConfigManagerOutput, - }, - types::ConfigValue::{Dict, Number, String as ConfigString}, - ConfigValue, - }, - prelude::messages::ObservableMessage, -}; - -static mut SHARED: Option> = None; -static INIT: Once = Once::new(); - -#[allow(dead_code)] -pub(crate) struct ConfigManager { - file: File, - stored: ConfigValue, - root: ConfigValue, - subscribers: Vec>>, -} - -// static methods -impl ConfigManager { - pub fn shared() -> Addr { - INIT.call_once(|| { - let builder = Self::create().args(Arguments::parse()).build(); - unsafe { SHARED = Some(builder) } - }); - unsafe { SHARED.clone().unwrap() } - } - - pub(super) fn create() -> Builder { - Builder::new() - } -} - -// instance methods -impl ConfigManager { - pub fn get_value(&self, key: String) -> Option { - if let Dict(dict) = &self.root { - dict.get(&key).cloned() - } else { - None - } - } - - pub fn set_value( - &mut self, - key: String, - value: Option, - ) -> Option { - value.and_then(|value| { - if let (Dict(stored), Dict(root)) = (&mut self.stored, &mut self.root) { - stored.insert(key.clone(), value.clone()); - root.insert(key.clone(), value.clone()); - Some(value) - } else { - None - } - }) - } - - // this doesn't work for now - pub fn soft_set_value( - &mut self, - key: String, - value: Option, - ) -> Option { - value.and_then(|value| { - if let Dict(root) = &mut self.root { - root.insert(key, value.clone()); - Some(value) - } else { - None - } - }) - } -} - -impl Actor for ConfigManager { - type Context = Context; - - fn started(&mut self, _ctx: &mut Self::Context) { - println!("[ConfigManager] starting"); - println!("[ConfigManager] started"); - } -} - -impl Handler for ConfigManager { - type Result = ConfigManagerDataResponse; - - fn handle( - &mut self, - msg: ConfigManagerDataMessage, - _ctx: &mut Self::Context, - ) -> Self::Result { - use ConfigManagerDataResponse::{GotValue, SetValue, SoftSetValue}; - - match msg { - ConfigManagerDataMessage::GetValue(val) => GotValue(self.get_value(val)), - ConfigManagerDataMessage::SetValue(key, value) => { - SetValue(key.clone(), self.set_value(key, value)) - } - ConfigManagerDataMessage::SoftSetValue(key, value) => { - SoftSetValue(key.clone(), self.soft_set_value(key, value)) - } - } - } -} - -impl From for ConfigManager { - fn from(builder: Builder) -> Self { - println!("got args: {:#?}", builder.args); - - let mut file = OpenOptions::new() - .write(true) - .read(true) - .create(true) - .open(builder.file_path) - .ok() - .unwrap(); - - let mut output = String::new(); - file - .read_to_string(&mut output) - .expect("failed to read from file"); - - let stored = output - .parse::() - .map(|v| v.into()) - .ok() - .unwrap_or_else(|| Dict(BTreeMap::new())); - - println!("[ConfigManager] got stored: {:?}", stored); - - let mut root = stored.clone(); - if let Dict(root) = &mut root { - builder.args.map(|v| { - v.port - .map(|p| root.insert("Network.Port".to_owned(), Number(p.into()))); - - v.name.map(|n| { - root.insert("Server.Name".to_owned(), ConfigString(n.into())) - }); - - v.owner.map(|o| { - root.insert("Server.Owner".to_owned(), ConfigString(o.into())) - }); - }); - } - - Self { - file, - root, - stored, - subscribers: Vec::default(), - } - } -} diff --git a/server/src/config_manager/messages.rs b/server/src/config_manager/messages.rs deleted file mode 100644 index 6552415..0000000 --- a/server/src/config_manager/messages.rs +++ /dev/null @@ -1,27 +0,0 @@ -use actix::{Message, MessageResponse}; - -use crate::config_manager::types::ConfigValue; - -#[derive(Message, Debug)] -#[rtype(result = "()")] -pub enum ConfigManagerOutput { - #[allow(dead_code)] - ConfigUpdated(String, ConfigValue), -} - -#[derive(Message, Debug)] -#[rtype(result = "ConfigManagerDataResponse")] -pub enum ConfigManagerDataMessage { - GetValue(String), - #[allow(dead_code)] - SetValue(String, Option), - #[allow(dead_code)] - SoftSetValue(String, Option), -} - -#[derive(MessageResponse, Debug)] -pub enum ConfigManagerDataResponse { - GotValue(Option), - SetValue(String, Option), - SoftSetValue(String, Option), -} diff --git a/server/src/config_manager/mod.rs b/server/src/config_manager/mod.rs deleted file mode 100644 index 15096ea..0000000 --- a/server/src/config_manager/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! # config_manager -//! This module contains all the code that deals with server configuration. -//! It tries to implement a singleton actor, that will be fetchable globaly. - -pub mod arg_parser; -mod builder; -mod config_manager; -mod messages; -mod types; - -pub(crate) use config_manager::ConfigManager; -pub(crate) use messages::{ - ConfigManagerDataMessage, - ConfigManagerDataResponse, -}; -pub(crate) use types::ConfigValue; diff --git a/server/src/config_manager/types.rs b/server/src/config_manager/types.rs deleted file mode 100644 index 4a9f8b0..0000000 --- a/server/src/config_manager/types.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::collections::BTreeMap; - -use toml::value::Value; - -/// # ConfigValue -/// Each value type that can be used within a config file. -/// gets used when reading and writing to a config file. -#[derive(Clone, Debug)] -pub enum ConfigValue { - Dict(BTreeMap), - Array(Vec), - String(String), - Number(i64), - Float(f64), - Bool(bool), -} - -impl From for Value { - fn from(v: ConfigValue) -> Self { - match v { - ConfigValue::Dict(dict) => { - Value::Table(dict.into_iter().map(|(k, v)| (k, v.into())).collect()) - } - ConfigValue::Array(arr) => { - Value::Array(arr.into_iter().map(|v| v.into()).collect()) - } - ConfigValue::String(s) => Value::String(s), - ConfigValue::Number(n) => Value::Integer(n), - ConfigValue::Float(f) => Value::Float(f), - ConfigValue::Bool(b) => Value::Boolean(b), - } - } -} - -impl From for ConfigValue { - fn from(v: Value) -> Self { - match v { - Value::Table(dict) => ConfigValue::Dict( - dict.into_iter().map(|(k, v)| (k, v.into())).collect(), - ), - Value::Array(arr) => { - ConfigValue::Array(arr.into_iter().map(|v| v.into()).collect()) - } - Value::String(s) => ConfigValue::String(s), - Value::Integer(n) => ConfigValue::Number(n), - Value::Float(f) => ConfigValue::Float(f), - Value::Boolean(b) => ConfigValue::Bool(b), - Value::Datetime(d) => ConfigValue::String(d.to_string()), - } - } -} diff --git a/server/src/connection/client_info.rs b/server/src/connection/client_info.rs new file mode 100644 index 0000000..853c618 --- /dev/null +++ b/server/src/connection/client_info.rs @@ -0,0 +1,32 @@ +use std::net::SocketAddr; + +use uuid::Uuid; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ClientInfo { + uuid: Uuid, + username: String, + addr: SocketAddr, +} + +impl ClientInfo { + pub fn new(uuid: Uuid, username: String, addr: SocketAddr) -> Self { + Self { + uuid, + username, + addr, + } + } + + pub fn get_uuid(&self) -> Uuid { + self.uuid + } + + pub fn get_username(&self) -> String { + self.username.clone() + } + + pub fn get_addr(&self) -> SocketAddr { + self.addr + } +} diff --git a/server/src/connection/client_thread.rs b/server/src/connection/client_thread.rs new file mode 100644 index 0000000..20da26b --- /dev/null +++ b/server/src/connection/client_thread.rs @@ -0,0 +1,87 @@ +use foundation::prelude::{ClientDetails, GlobalMessage, PrivateMessage}; +use tokio::{sync::mpsc::UnboundedSender, task::JoinHandle}; +use uuid::Uuid; + +use crate::{ + connection::{ + client_info::ClientInfo, + connection_manager::ConnectionManagerMessage, + }, + network::{ClientWriter, NetworkConnection}, +}; + +pub struct ClientThread { + read_task: JoinHandle<()>, + writer: Box, +} + +impl ClientThread { + pub async fn new_run( + uuid: Uuid, + conn: Box, + connection_manager_sender: UnboundedSender, + ) -> Self { + println!("[ClientThread] creating thread"); + let (writer, reader) = conn.send_connected(uuid).await; + + println!("[ClientThread] creating tasks"); + ClientThread { + read_task: reader.start_run(uuid, connection_manager_sender.clone()), + writer, + } + } + + pub async fn send_clients(&mut self, clients: Vec) { + self.writer.send_clients(clients).await + } + + pub async fn send_client_joined(&mut self, details: ClientDetails) { + self.writer.send_client_joined(details).await; + } + pub async fn send_client_left(&mut self, uuid: Uuid) { + self.writer.send_client_left(uuid).await + } + + // todo: link this in with message storage + pub(crate) async fn send_global_message(&mut self, message: GlobalMessage) { + self.writer.send_global_message(message).await; + } + + pub(crate) async fn send_global_messages( + &mut self, + messages: Vec, + ) { + self.writer.send_global_messages(messages).await; + } + + pub(crate) async fn send_disconnected(&mut self) { + self.writer.send_disconnect().await + } + + pub(crate) async fn send_private_message( + &mut self, + from: Uuid, + uuid: Uuid, + content: String, + ) { + self + .writer + .send_private_message(PrivateMessage { + uuid: uuid.to_string(), + from: from.to_string(), + content, + }) + .await; + } +} + +impl Drop for ClientThread { + fn drop(&mut self) { + self.read_task.abort(); + } +} + +pub enum ClientMessage { + SendClients(Vec), + SendGlobalMessages(Vec), +} diff --git a/server/src/connection/connection_manager.rs b/server/src/connection/connection_manager.rs new file mode 100644 index 0000000..b846807 --- /dev/null +++ b/server/src/connection/connection_manager.rs @@ -0,0 +1,252 @@ +use std::{collections::HashMap, net::SocketAddr}; + +use foundation::prelude::{ClientDetails, GlobalMessage}; +use tokio::sync::{ + mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + Mutex, +}; +use uuid::Uuid; + +use crate::{ + connection::{client_info::ClientInfo, client_thread::ClientThread}, + network::NetworkConnection, + server_va::ServerMessages, +}; + +pub struct ConnectionManager { + receiver: Mutex>, + sender: UnboundedSender, + server_sender: UnboundedSender, + client_map: HashMap, + client_tasks_map: HashMap, +} + +impl ConnectionManager { + pub fn new(server_sender: UnboundedSender) -> Self { + let (tx, rx) = unbounded_channel(); + Self { + client_map: HashMap::new(), + client_tasks_map: HashMap::new(), + server_sender, + receiver: Mutex::new(rx), + sender: tx, + } + } + + pub async fn run(&mut self) { + loop { + let mut lock = self.receiver.lock().await; + let msg = lock.recv().await; + drop(lock); + + match msg { + Some(ConnectionManagerMessage::AddClient { + conn, + uuid, + username, + addr, + }) => self.add_client(conn, uuid, username, addr).await, + + Some(ConnectionManagerMessage::Disconnected { uuid }) => { + self.remove_client(uuid).await + } + Some(ConnectionManagerMessage::BroadcastGlobalMessage { + from, + content, + }) => { + self.broadcast_global_message(from, content).await; + } + Some(ConnectionManagerMessage::SendClientsTo { uuid }) => { + self.send_clients_to(uuid).await; + } + Some(ConnectionManagerMessage::SendGlobalMessages { uuid }) => { + self.send_global_messages(uuid).await; + } + + Some(ConnectionManagerMessage::SendGlobalMessagesTo { + uuid, + messages, + }) => { + self.send_global_messages_to(uuid, messages).await; + } + + Some(ConnectionManagerMessage::SendPrivateMessage { + uuid, + from, + to, + content, + }) => { + self.send_private_message(to, from, uuid, content).await; + } + Some(ConnectionManagerMessage::Disconnect { uuid }) => { + self.disconnect(uuid).await + } + None => todo!(), + } + } + } + + async fn add_client( + &mut self, + conn: Box, + uuid: Uuid, + username: String, + addr: SocketAddr, + ) { + println!("[ConnectionManager] adding new client"); + let store = ClientInfo::new(uuid, username.clone(), addr); + self.client_map.insert(uuid, store); + println!("[ConnectionManager] added client info to map"); + + let thread = ClientThread::new_run(uuid, conn, self.sender.clone()).await; + self.client_tasks_map.insert(uuid, thread); + println!("[ConnectionManager] created running thread for new clinet"); + + for c in self.client_tasks_map.iter_mut() { + c.1 + .send_client_joined(ClientDetails { + uuid: uuid.to_string(), + name: username.clone(), + address: addr.to_string(), + }) + .await; + } + } + + async fn remove_client(&mut self, uuid: Uuid) { + println!("[ConnectionManager] removing {}", uuid); + self.client_map.remove(&uuid); + self.client_tasks_map.remove(&uuid); + + for c in self.client_tasks_map.iter_mut() { + c.1.send_client_left(uuid).await; + } + } + + async fn send_clients_to(&mut self, uuid: Uuid) { + let clients = self + .client_map + .values() + .cloned() + .map(|c| foundation::prelude::ClientDetails { + uuid: c.get_uuid().to_string(), + name: c.get_username(), + address: c.get_addr().to_string(), + }) + .collect(); + + let t = self.client_tasks_map.get_mut(&uuid); + let Some(t) = t else { + return; + }; + + println!("[ConnectionManager] sending client list to {:?}", clients); + + t.send_clients(clients).await; + } + + async fn broadcast_global_message(&mut self, from: Uuid, content: String) { + let message = GlobalMessage { + uuid: Uuid::new_v4().to_string(), + from: from.to_string(), + content, + }; + _ = self + .server_sender + .send(ServerMessages::AddGlobalMessage(message.clone())); + for c in self.client_tasks_map.iter_mut() { + c.1.send_global_message(message.clone()).await; + } + } + + async fn send_global_messages(&mut self, uuid: Uuid) { + _ = self + .server_sender + .send(ServerMessages::SendGlobalMessages(uuid)); + } + + async fn send_global_messages_to( + &mut self, + uuid: Uuid, + messages: Vec, + ) { + let t = self.client_tasks_map.get_mut(&uuid); + let Some(t) = t else { + return; + }; + + t.send_global_messages(messages).await; + } + + async fn send_private_message( + &mut self, + to: Uuid, + from: Uuid, + uuid: Uuid, + content: String, + ) { + let t = self.client_tasks_map.get_mut(&to); + let Some(t) = t else { + return; + }; + + t.send_private_message(from, uuid, content).await + } + + async fn disconnect(&mut self, uuid: Uuid) { + let t = self.client_tasks_map.get_mut(&uuid); + let Some(t) = t else { + return; + }; + + t.send_disconnected().await; + } + + pub fn get_sender(&self) -> UnboundedSender { + self.sender.clone() + } +} + +pub enum ConnectionManagerMessage { + // server messages + AddClient { + conn: Box, + uuid: Uuid, + username: String, + addr: SocketAddr, + }, + + // client thread messages + SendClientsTo { + uuid: Uuid, + }, + + SendGlobalMessages { + uuid: Uuid, + }, + + SendGlobalMessagesTo { + uuid: Uuid, + messages: Vec, + }, + + BroadcastGlobalMessage { + from: Uuid, + content: String, + }, + + SendPrivateMessage { + uuid: Uuid, + from: Uuid, + to: Uuid, + content: String, + }, + + Disconnect { + uuid: Uuid, + }, + + Disconnected { + uuid: Uuid, + }, +} diff --git a/server/src/connection/mod.rs b/server/src/connection/mod.rs new file mode 100644 index 0000000..5e1bff9 --- /dev/null +++ b/server/src/connection/mod.rs @@ -0,0 +1,3 @@ +pub mod client_info; +pub mod client_thread; +pub mod connection_manager; diff --git a/server/src/lua/builder.rs b/server/src/lua/builder.rs deleted file mode 100644 index c07e559..0000000 --- a/server/src/lua/builder.rs +++ /dev/null @@ -1,32 +0,0 @@ -use actix::{Addr, WeakAddr}; - -use crate::{ - client_management::ClientManager, - lua::lua_manager::LuaManager, - network::NetworkManager, - Server, -}; - -pub struct Builder { - pub(super) server: WeakAddr, - pub(super) network_manager: WeakAddr, - pub(super) client_manager: WeakAddr, -} - -impl Builder { - pub(super) fn new( - server: WeakAddr, - network_manager: WeakAddr, - client_manager: WeakAddr, - ) -> Self { - Builder { - server, - network_manager, - client_manager, - } - } - - pub(crate) fn build(self) -> Addr { - Addr::from(self) - } -} diff --git a/server/src/lua/lua_manager.rs b/server/src/lua/lua_manager.rs deleted file mode 100644 index 28d7faa..0000000 --- a/server/src/lua/lua_manager.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! # lua_manager.rs -//! -//! Holds the LuaManger struct and implements it's methods - -use actix::{fut::wrap_future, Actor, Addr, AsyncContext, Context, WeakAddr}; -use mlua::{Lua, Thread}; - -use crate::{ - client_management::ClientManager, - lua::builder::Builder, - network::NetworkManager, - scripting::scriptable_server::ScriptableServer, - Server, -}; - -/// # LuaManager -/// Holds common server objects -/// todo: change to weak references -pub struct LuaManager { - pub(super) server: WeakAddr, - pub(super) _network_manager: WeakAddr, - pub(super) _client_manager: WeakAddr, -} - -impl LuaManager { - pub fn create( - server: WeakAddr, - network_manager: WeakAddr, - client_manager: WeakAddr, - ) -> Builder { - Builder::new(server, network_manager, client_manager) - } - - fn create_lua(&self) -> Lua { - let engine = Lua::new(); - let server = ScriptableServer::from(self.server.clone()); - - let api = engine.create_table().unwrap(); - api.set::<&str, ScriptableServer>("server", server).unwrap(); - - engine.globals().set("chat", api).unwrap(); - engine - } -} - -impl Actor for LuaManager { - type Context = Context; - - fn started(&mut self, ctx: &mut Self::Context) { - let engine = self.create_lua(); - - ctx.spawn(wrap_future(async move { - let coroutine: Thread = engine - .load( - r#" - coroutine.create(function () - print("hello lua") - print(chat.server:name()) - end) - "#, - ) - .eval() - .unwrap(); - let coroutine = coroutine.into_async::<(), ()>(()); - coroutine.await.expect("TODO: panic message"); - })); - } -} - -// by implementing it for the addr type, -// we enforce the actor model on the consumer of the api. -impl From for Addr { - fn from(b: Builder) -> Addr { - LuaManager { - server: b.server, - _network_manager: b.network_manager, - _client_manager: b.client_manager, - } - .start() - } -} diff --git a/server/src/lua/mod.rs b/server/src/lua/mod.rs deleted file mode 100644 index e75a513..0000000 --- a/server/src/lua/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod builder; -mod lua_manager; - -pub use lua_manager::LuaManager; diff --git a/server/src/main.rs b/server/src/main.rs index a4b3ea1..ba6a580 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,23 +1,19 @@ //! This is the main module of the actix server. //! It starts the server and sleeps for the remainder of the program -pub(crate) mod client_management; -pub(crate) mod config_manager; -pub(crate) mod lua; -pub(crate) mod network; -pub(crate) mod prelude; -pub(crate) mod rhai; -pub(crate) mod scripting; -pub(crate) mod server; +pub mod network; -use server::Server; -use tokio::time::{sleep, Duration}; +pub mod chat; +pub mod connection; +pub mod os_signal_manager; +pub mod server_va; + +use crate::server_va::Server; /// The main function #[actix::main()] async fn main() { - let _init = Server::create().build(); - loop { - sleep(Duration::from_millis(1000)).await; - } + // creating listeners + + Server::default().run().await; } diff --git a/server/src/network/connection/actor.rs b/server/src/network/connection/actor.rs deleted file mode 100644 index a5b7819..0000000 --- a/server/src/network/connection/actor.rs +++ /dev/null @@ -1,266 +0,0 @@ -use std::{io::Write, net::SocketAddr, pin::Pin, sync::Arc, time::Duration}; - -use actix::{ - clock::timeout, - fut::wrap_future, - Actor, - ActorContext, - Addr, - AsyncContext, - Context, - Handler, - WeakRecipient, -}; -use futures::{future::join_all, Future, FutureExt}; -use tokio::{ - io::{split, AsyncBufReadExt, AsyncWriteExt, BufReader, ReadHalf, WriteHalf}, - net::TcpStream, - sync::Mutex, -}; - -use super::{ConnectionMessage, ConnectionObservableOutput}; -use crate::{ - network::connection::messages::ConnectionPrivateMessage, - prelude::messages::ObservableMessage, -}; - -/// # Connection -/// This manages a TcpStream for a given connection. -/// -/// ## Fields -/// - read_half: A temporary store fr the read half of the connection. -/// - write_half: The write half of the connection. -/// - address: The socket address of the conneciton. -/// - observers: A list of observers to events created by the connection. -/// - loop_future: the future holding the receiving loop. -pub struct Connection { - write_half: Arc>>, - _address: SocketAddr, - observers: Vec>, -} - -impl Connection { - /// Creates a new Conneciton actor from a Tokio TcpStream, - /// and start's its execution. - /// returns: the Addr of the connection. - pub(crate) fn new(stream: TcpStream, address: SocketAddr) -> Addr { - let (read_half, write_half) = split(stream); - let addr = Connection { - write_half: Arc::new(Mutex::new(write_half)), - _address: address, - observers: Vec::new(), - } - .start(); - addr.do_send(ConnectionPrivateMessage::DoRead(BufReader::new(read_half))); - addr - } - - #[inline] - fn broadcast( - &self, - ctx: &mut ::Context, - data: ConnectionObservableOutput, - ) { - let futs: Vec + Send>>> = self - .observers - .iter() - .cloned() - .map(|r| { - let data = data.clone(); - async move { - if let Some(r) = r.upgrade() { - let _ = r.send(data).await; - } - } - .boxed() - }) - .collect(); - let _ = ctx.spawn(wrap_future(async { - join_all(futs).await; - })); - } - - #[inline] - fn do_read( - &mut self, - ctx: &mut ::Context, - mut buf_reader: BufReader>, - ) { - let weak_addr = ctx.address().downgrade(); - - let read_fut = async move { - let dur = Duration::from_millis(100); - let mut buffer_string: String = Default::default(); - - let read_fut = buf_reader.read_line(&mut buffer_string); - let Ok(Ok(len)) = timeout(dur, read_fut).await else { - if let Some(addr) = weak_addr.upgrade() { - addr.do_send(ConnectionPrivateMessage::DoRead(buf_reader)); - } - return; - }; - - if len == 0 { - println!("[Connection] readline returned 0"); - if let Some(addr) = weak_addr.upgrade() { - addr.do_send(ConnectionPrivateMessage::Close); - } - return; - } - - if let Some(addr) = weak_addr.upgrade() { - let _ = addr - .send(ConnectionPrivateMessage::Broadcast( - ConnectionObservableOutput::RecvData( - addr.downgrade(), - buffer_string.clone(), - ), - )) - .await; - } - - if let Some(addr) = weak_addr.upgrade() { - addr.do_send(ConnectionPrivateMessage::DoRead(buf_reader)); - } - }; - ctx.spawn(wrap_future(read_fut)); - } - - fn close_connection(&self, ctx: &mut ::Context) { - use ConnectionObservableOutput::ConnectionClosed; - self.broadcast(ctx, ConnectionClosed(ctx.address().downgrade())) - } -} - -impl Actor for Connection { - type Context = Context; - - /// runs when the actor is started. - /// takes out eh read_half ad turns it into a buffered reader - /// then eneters loop readling lines from the tcp stream - fn started(&mut self, _ctx: &mut Self::Context) { - println!("[Connection] started"); - } - - fn stopped(&mut self, ctx: &mut Self::Context) { - use ConnectionObservableOutput::ConnectionClosed; - println!("[Connection] stopped"); - for recp in self.observers.iter() { - if let Some(recp) = recp.upgrade() { - recp.do_send(ConnectionClosed(ctx.address().downgrade())) - } - } - } -} - -impl Handler> for Connection { - type Result = (); - fn handle( - &mut self, - msg: ObservableMessage, - _ctx: &mut Self::Context, - ) -> >>::Result{ - use ObservableMessage::{Subscribe, Unsubscribe}; - match msg { - Subscribe(r) => { - println!("[Connection] adding subscriber"); - self.observers.push(r); - } - Unsubscribe(r) => { - println!("[Connection] removing subscriber"); - let r = r.upgrade(); - self.observers = self - .observers - .clone() - .into_iter() - .filter(|a| a.upgrade() != r) - .collect(); - } - }; - } -} - -impl Handler for Connection { - type Result = (); - fn handle( - &mut self, - msg: ConnectionMessage, - ctx: &mut Self::Context, - ) -> Self::Result { - use ConnectionMessage::{CloseConnection, SendData}; - let writer = Arc::downgrade(&self.write_half); - - match msg { - SendData(d) => { - ctx.spawn(wrap_future(async move { - let Some(writer) = writer.upgrade() else { - return; - }; - - println!("[Connection] sending data"); - let mut lock = writer.lock().await; - let mut buffer = Vec::new(); - let _ = writeln!(&mut buffer, "{}", d.as_str()); - let _ = lock.write_all(&buffer).await; - })); - } - CloseConnection => ctx.stop(), - }; - } -} - -// impl Handler for Connection { -// type Result = (); -// fn handle(&mut self, msg: SelfMessage, ctx: &mut Self::Context) -> Self::Result { -// use ConnectionObservableOutput::RecvData; -// use SelfMessage::UpdateObserversWithData; -// match msg { -// UpdateObserversWithData(data) => { -// let send = ctx.address(); -// let addr = self.address; -// // this is a mess -// let futs: Vec + Send>>> = self -// .observers -// .iter() -// .cloned() -// .map(|r| { -// let send = send.clone(); -// let data = data.clone(); -// async move { -// let _ = r.send(RecvData(send, addr, data)).await; -// } -// .boxed() -// }) -// .collect(); -// let _ = ctx.spawn(wrap_future(async { -// join_all(futs).await; -// })); -// } -// }; -// } -// } - -impl Handler for Connection { - type Result = (); - - fn handle( - &mut self, - msg: ConnectionPrivateMessage, - ctx: &mut Self::Context, - ) -> Self::Result { - use ConnectionPrivateMessage::Broadcast; - match msg { - Broadcast(data) => self.broadcast(ctx, data), - ConnectionPrivateMessage::DoRead(buf_reader) => { - self.do_read(ctx, buf_reader) - } - ConnectionPrivateMessage::Close => self.close_connection(ctx), - }; - } -} - -impl Drop for Connection { - fn drop(&mut self) { - println!("[Connection] Dropping value") - } -} diff --git a/server/src/network/connection/messages.rs b/server/src/network/connection/messages.rs deleted file mode 100644 index 4876323..0000000 --- a/server/src/network/connection/messages.rs +++ /dev/null @@ -1,30 +0,0 @@ -use actix::{Message, WeakAddr}; -use tokio::{ - io::{BufReader, ReadHalf}, - net::TcpStream, -}; - -use crate::prelude::actors::Connection; - -/// This is a message that can be sent to the Connection. -#[derive(Message)] -#[rtype(result = "()")] -pub(crate) enum ConnectionMessage { - SendData(String), - CloseConnection, -} - -#[derive(Message, Clone)] -#[rtype(result = "()")] -pub(crate) enum ConnectionObservableOutput { - RecvData(WeakAddr, String), - ConnectionClosed(WeakAddr), -} - -#[derive(Message)] -#[rtype(result = "()")] -pub(super) enum ConnectionPrivateMessage { - Broadcast(ConnectionObservableOutput), - DoRead(BufReader>), - Close, -} diff --git a/server/src/network/connection/mod.rs b/server/src/network/connection/mod.rs deleted file mode 100644 index 4ef6ddd..0000000 --- a/server/src/network/connection/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod actor; -mod messages; - -pub(crate) use actor::Connection; -pub(crate) use messages::{ConnectionMessage, ConnectionObservableOutput}; diff --git a/server/src/network/connection_initiator/actor.rs b/server/src/network/connection_initiator/actor.rs deleted file mode 100644 index b950792..0000000 --- a/server/src/network/connection_initiator/actor.rs +++ /dev/null @@ -1,170 +0,0 @@ -use std::net::SocketAddr; - -use actix::{ - Actor, - ActorContext, - Addr, - AsyncContext, - Context, - Handler, - WeakAddr, - WeakRecipient, -}; -use foundation::{ - messages::{ - client::{ClientStreamOut, ClientStreamOut::Error}, - network::{NetworkSockIn, NetworkSockOut}, - }, - ClientDetails, -}; -use serde_json::{from_str, to_string}; - -use crate::{ - network::InitiatorOutput, - prelude::{ - actors::Connection, - messages::{ - ConnectionMessage, - ConnectionObservableOutput, - ObservableMessage, - }, - }, -}; - -/// # ConnectionInitiator -/// Handles the initiatin of a new connection. -/// -/// This will do one of two things: -/// - Create a new client and send it to the network manager. -/// - Request the eserver info and send it to the connection. -pub struct ConnectionInitiator { - delegate: WeakRecipient, - connection: Addr, -} - -impl ConnectionInitiator { - pub(crate) fn new( - delegate: WeakRecipient, - connection: Addr, - ) -> Addr { - ConnectionInitiator { - connection, - delegate, - } - .start() - } - - fn handle_request( - &mut self, - sender: WeakAddr, - ctx: &mut ::Context, - data: String, - ) { - use InitiatorOutput::{ClientRequest, InfoRequest}; - use NetworkSockIn::{Connect, Info}; - - let msg = from_str::(data.as_str()); - if let Err(e) = msg.as_ref() { - println!("[ConnectionInitiator] error decoding message {}", e); - self.error(ctx, sender); - return; - } - let msg = msg.unwrap(); - - println!("[ConnectionInitiator] matching request"); - if let (Some(delegate), Some(sender)) = - (self.delegate.upgrade(), sender.upgrade()) - { - match msg { - Info => { - delegate.do_send(InfoRequest(ctx.address().downgrade(), sender)) - } - Connect { - uuid, - username, - address, - } => delegate.do_send(ClientRequest( - ctx.address().downgrade(), - sender, - ClientDetails { - uuid, - username, - address, - public_key: None, - }, - )), - }; - ctx.stop(); - } - } - - fn error( - &mut self, - ctx: &mut ::Context, - sender: WeakAddr, - ) { - use ConnectionMessage::{CloseConnection, SendData}; - if let Some(sender) = sender.upgrade() { - sender.do_send(SendData( - to_string::(&Error { - msg: "Error in connection initiator?".to_owned(), - }) - .unwrap(), - )); - sender.do_send(CloseConnection); - } - ctx.stop() - } -} - -impl Actor for ConnectionInitiator { - type Context = Context; - - /// on start initiate the protocol. - /// also add self as a subscriber to the connection. - fn started(&mut self, ctx: &mut Self::Context) { - use ConnectionMessage::SendData; - use NetworkSockOut::Request; - use ObservableMessage::Subscribe; - - println!("[ConnectionInitiator] started"); - - self - .connection - .do_send(Subscribe(ctx.address().recipient().downgrade())); - - self - .connection - .do_send(SendData(to_string(&Request).unwrap())); - } - - /// once stopped remove self from the connection subscribers - fn stopped(&mut self, ctx: &mut Self::Context) { - use ObservableMessage::Unsubscribe; - println!("[ConnectionInitiator] stopped"); - self - .connection - .do_send(Unsubscribe(ctx.address().recipient().downgrade())); - } -} - -impl Handler for ConnectionInitiator { - type Result = (); - fn handle( - &mut self, - msg: ConnectionObservableOutput, - ctx: &mut Self::Context, - ) -> Self::Result { - use ConnectionObservableOutput::RecvData; - - if let RecvData(sender, data) = msg { - self.handle_request(sender, ctx, data) - } - } -} - -impl Drop for ConnectionInitiator { - fn drop(&mut self) { - println!("[ConnectionInitiator] Dropping value") - } -} diff --git a/server/src/network/connection_initiator/messages.rs b/server/src/network/connection_initiator/messages.rs deleted file mode 100644 index 561c70b..0000000 --- a/server/src/network/connection_initiator/messages.rs +++ /dev/null @@ -1,15 +0,0 @@ -use actix::{Addr, Message, WeakAddr}; -use foundation::ClientDetails; - -use crate::prelude::actors::{Connection, ConnectionInitiator}; - -#[derive(Message)] -#[rtype(result = "()")] -pub(crate) enum InitiatorOutput { - InfoRequest(WeakAddr, Addr), - ClientRequest( - WeakAddr, - Addr, - ClientDetails, - ), -} diff --git a/server/src/network/connection_initiator/mod.rs b/server/src/network/connection_initiator/mod.rs deleted file mode 100644 index 07e1a19..0000000 --- a/server/src/network/connection_initiator/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod actor; -mod messages; - -pub(crate) use actor::ConnectionInitiator; -pub(crate) use messages::InitiatorOutput; diff --git a/server/src/network/json/json_client_reader.rs b/server/src/network/json/json_client_reader.rs new file mode 100644 index 0000000..5d20b2a --- /dev/null +++ b/server/src/network/json/json_client_reader.rs @@ -0,0 +1,98 @@ +use std::{io, net::SocketAddr}; + +use foundation::{ + messages::client::ClientStreamIn, + networking::json::read_message, +}; +use tokio::{io::ReadHalf, net::TcpStream, sync::mpsc::UnboundedSender}; +use uuid::Uuid; + +use crate::{ + connection::connection_manager::ConnectionManagerMessage, + network::ClientReader, +}; + +pub struct JSONClientReader { + reader: ReadHalf, + addr: SocketAddr, + uuid: Uuid, +} + +impl JSONClientReader { + pub fn new( + reader: ReadHalf, + addr: SocketAddr, + uuid: Uuid, + ) -> Self { + Self { reader, addr, uuid } + } + + // move to other one + pub async fn get_message(&mut self) -> io::Result { + read_message::, ClientStreamIn>(&mut self.reader).await + } + + pub fn handle_message( + &self, + msg: ClientStreamIn, + channel: &UnboundedSender, + ) { + println!("[JSONClientReader:{}] got message", self.addr); + + let uuid = self.uuid; + + _ = match msg { + ClientStreamIn::GetClients => { + channel.send(ConnectionManagerMessage::SendClientsTo { uuid }) + } + ClientStreamIn::GetMessages => { + channel.send(ConnectionManagerMessage::SendGlobalMessages { uuid }) + } + ClientStreamIn::SendMessage { to, content } => { + channel.send(ConnectionManagerMessage::SendPrivateMessage { + uuid: Uuid::new_v4(), + from: uuid, + to, + content, + }) + } + ClientStreamIn::SendGlobalMessage { content } => { + channel.send(ConnectionManagerMessage::BroadcastGlobalMessage { + from: uuid, + content, + }) + } + ClientStreamIn::Disconnect => { + channel.send(ConnectionManagerMessage::Disconnect { uuid }) + } + }; + } +} + +impl ClientReader for JSONClientReader { + fn start_run( + mut self: Box, + uuid: Uuid, + channel: UnboundedSender, + ) -> tokio::task::JoinHandle<()> { + tokio::spawn(async move { + loop { + let msg = self.get_message().await; + + let Ok(msg) = msg else { + let error = msg.unwrap_err(); + println!( + "[JSONClientReader:{}] errored with '{}' disconnecting", + self.addr, error + ); + + _ = channel.send(ConnectionManagerMessage::Disconnected { uuid }); + + return; + }; + + self.handle_message(msg, &channel); + } + }) + } +} diff --git a/server/src/network/json/json_client_writer.rs b/server/src/network/json/json_client_writer.rs new file mode 100644 index 0000000..0e55e8e --- /dev/null +++ b/server/src/network/json/json_client_writer.rs @@ -0,0 +1,117 @@ +use std::net::SocketAddr; + +use async_trait::async_trait; +use chrono::Local; +use foundation::{ + messages::client::ClientStreamOut, + models::message::Message, + networking::json::write_message, + prelude::{GlobalMessage, PrivateMessage}, + ClientDetails, +}; +use tokio::{io::WriteHalf, net::TcpStream}; +use uuid::Uuid; + +use crate::network::ClientWriter; + +#[allow(dead_code)] +pub struct JSONClientWriter { + writer: WriteHalf, + addr: SocketAddr, + uuid: Uuid, +} + +impl JSONClientWriter { + pub fn new( + writer: WriteHalf, + addr: SocketAddr, + uuid: Uuid, + ) -> Self { + Self { writer, addr, uuid } + } +} + +#[async_trait] +impl ClientWriter for JSONClientWriter { + async fn send_clients( + &mut self, + clients: Vec, + ) { + let message = ClientStreamOut::ConnectedClients { + clients: clients + .into_iter() + .map(|c| ClientDetails { + uuid: c.uuid.parse().unwrap(), + username: c.name, + address: c.address, + public_key: None, + }) + .collect(), + }; + println!("[JSONClientWriter:{}] sending clients", self.addr); + write_message(&mut self.writer, message).await; + } + + async fn send_client_joined( + &mut self, + details: foundation::prelude::ClientDetails, + ) { + let message = ClientStreamOut::ClientConnected { + id: details.uuid.parse().unwrap(), + username: details.name, + }; + println!( + "[ProtobufClientWriter:{}] sending client connected message", + self.addr + ); + write_message(&mut self.writer, message).await; + } + + async fn send_client_left(&mut self, uuid: Uuid) { + let message = ClientStreamOut::ClientRemoved { id: uuid }; + println!( + "[ProtobufClientWriter:{}] sending client connected message", + self.addr + ); + write_message(&mut self.writer, message).await; + } + + async fn send_global_messages(&mut self, messages: Vec) { + let message = ClientStreamOut::GlobalChatMessages { + messages: messages + .into_iter() + .map(|m| Message { + id: m.uuid.parse().unwrap(), + from: m.from.parse().unwrap(), + content: m.content, + time: Local::now(), + }) + .collect(), + }; + println!("[JSONClientWriter:{}] sending global messages", self.addr); + write_message(&mut self.writer, message).await; + } + + async fn send_private_message(&mut self, message: PrivateMessage) { + let message = ClientStreamOut::UserMessage { + from: message.from.parse().unwrap(), + content: message.content, + }; + println!("[JSONClientWriter:{}] sending private message", self.addr); + write_message(&mut self.writer, message).await; + } + + async fn send_global_message(&mut self, message: GlobalMessage) { + let message = ClientStreamOut::GlobalMessage { + from: message.from.parse().unwrap(), + content: message.content, + }; + write_message(&mut self.writer, message).await; + } + + async fn send_disconnect(&mut self) { + let message = ClientStreamOut::Disconnected; + println!("[JSONClientWriter:{}] sending disconnect", self.addr); + write_message(&mut self.writer, message).await; + } +} diff --git a/server/src/network/json/json_listener.rs b/server/src/network/json/json_listener.rs new file mode 100644 index 0000000..6830e69 --- /dev/null +++ b/server/src/network/json/json_listener.rs @@ -0,0 +1,54 @@ +use async_trait::async_trait; +use tokio::{net::TcpListener, sync::mpsc::UnboundedSender, task::JoinHandle}; + +use crate::{ + network::{ConnectionType, NetworkListener}, + server_va::ServerMessages, +}; + +/// # Listener Manager +/// This stores and awaits for connections from listeners. +/// When a connection is received, it is passed to the server +pub struct JSONListener { + listener: TcpListener, + sender: UnboundedSender, +} + +#[async_trait] +impl NetworkListener for JSONListener { + /// Binds listeners and stores them in the ListenerManager + async fn new(sender: UnboundedSender) -> Self { + let address = "0.0.0.0:5600"; + + println!("[JSONListener] setting up listeners"); + let listener = TcpListener::bind(address) + .await + .expect("[JSONListener] failed to bind to 0.0.0.0:5600"); + + Self { listener, sender } + } + + async fn run(&self) { + loop { + println!("[JSONListener] waiting for connection"); + let accept_protobuf = self.listener.accept().await; + + let Ok((stream, addr)) = accept_protobuf else { + println!("[JSONListener] accept failed"); + continue; + }; + + let msg = ServerMessages::NewConnection(ConnectionType::JsonConnection( + stream, addr, + )); + println!("[JSONListener] passing message to server"); + _ = self.sender.send(msg); + } + } + + fn start_run(sender: UnboundedSender) -> JoinHandle<()> { + tokio::spawn(async move { + JSONListener::new(sender).await.run().await; + }) + } +} diff --git a/server/src/network/json/json_network_connection.rs b/server/src/network/json/json_network_connection.rs new file mode 100644 index 0000000..c6e6808 --- /dev/null +++ b/server/src/network/json/json_network_connection.rs @@ -0,0 +1,87 @@ +use std::{io, net::SocketAddr}; + +use foundation::{ + messages::network::{NetworkSockIn, NetworkSockOut}, + networking::json::{read_message, write_message}, +}; +use tokio::{io::split, net::TcpStream}; +use uuid::Uuid; + +use crate::network::{ + json::{ + json_client_reader::JSONClientReader, + json_client_writer::JSONClientWriter, + }, + ClientReader, + ClientWriter, + NetworkConnection, + ServerRequest, +}; + +pub struct JSONNetworkConnection { + pub(super) stream: TcpStream, + pub(super) addr: SocketAddr, +} + +impl JSONNetworkConnection { + pub fn new(stream: TcpStream, addr: SocketAddr) -> Self { + Self { stream, addr } + } +} + +#[async_trait::async_trait] +impl NetworkConnection for JSONNetworkConnection { + async fn get_request(&mut self) -> io::Result { + println!("[JSONNetworkConnection] sending request"); + + write_message(&mut self.stream, NetworkSockOut::Request).await; + + println!("[JSONNetworkConnection] waiting for response"); + + let request = + read_message::(&mut self.stream).await?; + + println!("[JSONNetworkConnection] returning request"); + + match request { + NetworkSockIn::Info => Ok(ServerRequest::GetInfo), + NetworkSockIn::Connect { + uuid, + username, + address: _, + } => Ok(ServerRequest::Connect { + username, + uuid, + addr: self.addr, + }), + // _ => Ok(ServerRequest::Ignore), + } + } + + async fn send_info(mut self: Box, name: String, owner: String) { + println!("[JSONNetworkConnection] Sending info to client"); + write_message( + &mut self.stream, + NetworkSockOut::GotInfo { + server_name: name, + server_owner: owner, + }, + ) + .await; + + println!("[JSONNetworkConnection] droping connection"); + } + + async fn send_connected( + mut self: Box, + uuid: Uuid, + ) -> (Box, Box) { + write_message(&mut self.stream, NetworkSockOut::Connected).await; + + let (read, write) = split(self.stream); + + let writer = Box::new(JSONClientWriter::new(write, self.addr, uuid)); + let reader = Box::new(JSONClientReader::new(read, self.addr, uuid)); + (writer, reader) + } +} diff --git a/server/src/network/json/mod.rs b/server/src/network/json/mod.rs new file mode 100644 index 0000000..e205fab --- /dev/null +++ b/server/src/network/json/mod.rs @@ -0,0 +1,4 @@ +pub mod json_client_reader; +pub mod json_client_writer; +pub mod json_listener; +pub mod json_network_connection; diff --git a/server/src/network/listener/mod.rs b/server/src/network/listener/mod.rs deleted file mode 100644 index 8e047d7..0000000 --- a/server/src/network/listener/mod.rs +++ /dev/null @@ -1,110 +0,0 @@ -use std::net::{SocketAddr, ToSocketAddrs}; - -use actix::{ - fut::wrap_future, - Actor, - Addr, - AsyncContext, - Context, - Handler, - Message, - SpawnHandle, - WeakRecipient, -}; -use tokio::net::TcpListener; - -use crate::network::connection::Connection; - -#[derive(Message)] -#[rtype(result = "()")] -pub(super) enum ListenerMessage { - StartListening, - StopListening, -} - -#[derive(Message)] -#[rtype(result = "()")] -pub(super) enum ListenerOutput { - NewConnection(Addr), -} - -pub(super) struct NetworkListener { - address: SocketAddr, - delegate: WeakRecipient, - looper: Option, -} - -impl NetworkListener { - pub(crate) fn new( - address: T, - delegate: WeakRecipient, - ) -> Addr { - NetworkListener { - address: address - .to_socket_addrs() - .unwrap() - .collect::>()[0], - delegate, - looper: None, - } - .start() - } - - /// called when the actor is to start listening - fn start_listening(&mut self, ctx: &mut ::Context) { - println!("[NetworkListener] started listening"); - let addr = self.address; - let delegate = self.delegate.clone(); - ctx.spawn(wrap_future(async move { - use ListenerOutput::NewConnection; - - let listener = TcpListener::bind(addr).await.unwrap(); - while let Ok((stream, addr)) = listener.accept().await { - println!("[NetworkListener] accepted socket"); - let conn = Connection::new(stream, addr); - - let Some(delegate) = delegate.upgrade() else { - break; - }; - - println!("[NetworkListener] sending connection to delegate"); - delegate.do_send(NewConnection(conn)) - } - })); - } - - /// called when the actor is to stop listening - fn stop_listening(&mut self, ctx: &mut ::Context) { - println!("[NetworkListener] stopped listening"); - if let Some(fut) = self.looper.take() { - ctx.cancel_future(fut); - } - } -} - -impl Actor for NetworkListener { - type Context = Context; - - fn started(&mut self, _ctx: &mut Self::Context) { - println!("[NetworkListener] started"); - } - - fn stopped(&mut self, _ctx: &mut Self::Context) { - println!("[NetworkListener] stopped"); - } -} - -impl Handler for NetworkListener { - type Result = (); - fn handle( - &mut self, - msg: ListenerMessage, - ctx: &mut ::Context, - ) -> Self::Result { - use ListenerMessage::{StartListening, StopListening}; - match msg { - StartListening => self.start_listening(ctx), - StopListening => self.stop_listening(ctx), - } - } -} diff --git a/server/src/network/mod.rs b/server/src/network/mod.rs index 6e429da..fa95b3e 100644 --- a/server/src/network/mod.rs +++ b/server/src/network/mod.rs @@ -1,48 +1,66 @@ -#![doc = r"# Network +use std::{io, net::SocketAddr}; -This module contains network code for the server. +use async_trait::async_trait; +use foundation::prelude::{ClientDetails, GlobalMessage, PrivateMessage}; +use tokio::{net::TcpStream, sync::mpsc::UnboundedSender, task::JoinHandle}; +use uuid::Uuid; -This includes: -- The network manager: For that handles all server network connections. -- The network listener: For listening for connections on a port. -- The conneciton: An abstraction over sockets sockets, for actix. -- The connection initiator: For initiating new connections to the server - -## Diagrams - -```mermaid -sequenceDiagram - Server->>NetworkManager: creates - NetworkManager->>NetworkListener: create - NetworkManager->>+NetworkListener: start listening - - loop async tcp listen - NetworkListener->>NetworkListener: check for new connections - end - - NetworkListener->>Connection: create from socket - NetworkListener->>NetworkManager: new connection - NetworkManager->>Server: new connection - - Server->>ConnectionInitiator: create with connection -```"] - -mod connection; -mod connection_initiator; -mod listener; -mod network_manager; - -pub(crate) use connection::{ - Connection, - ConnectionMessage, - ConnectionObservableOutput, -}; -pub(crate) use connection_initiator::{ConnectionInitiator, InitiatorOutput}; -// use listener::{ListenerMessage, ListenerOutput, NetworkListener}; -pub(crate) use network_manager::{ - NetworkDataMessage, - NetworkDataOutput, - NetworkManager, - NetworkMessage, - NetworkOutput, +use crate::{ + connection::connection_manager::ConnectionManagerMessage, + server_va::ServerMessages, }; + +pub mod json; +pub mod protobuf; + +pub enum ConnectionType { + ProtobufConnection(TcpStream, SocketAddr), + JsonConnection(TcpStream, SocketAddr), +} + +#[async_trait] +pub trait NetworkListener { + async fn new(channel: UnboundedSender) -> Self; + async fn run(&self); + fn start_run(sender: UnboundedSender) -> JoinHandle<()>; +} + +#[async_trait::async_trait] +pub trait NetworkConnection: Send { + async fn get_request(&mut self) -> io::Result; + async fn send_info(self: Box, name: String, owner: String); + async fn send_connected( + self: Box, + uuid: Uuid, + ) -> (Box, Box); +} + +#[async_trait::async_trait] +pub trait ClientReader: Send { + fn start_run( + self: Box, + uuid: Uuid, + channel: UnboundedSender, + ) -> JoinHandle<()>; +} + +#[async_trait::async_trait] +pub trait ClientWriter: Send { + async fn send_clients(&mut self, clients: Vec); + async fn send_global_messages(&mut self, messages: Vec); + async fn send_global_message(&mut self, message: GlobalMessage); + async fn send_private_message(&mut self, message: PrivateMessage); + async fn send_disconnect(&mut self); + async fn send_client_joined(&mut self, details: ClientDetails); + async fn send_client_left(&mut self, uuid: Uuid); +} + +pub enum ServerRequest { + GetInfo, + Connect { + username: String, + uuid: uuid::Uuid, + addr: SocketAddr, + }, + Ignore, +} diff --git a/server/src/network/network_manager/actor.rs b/server/src/network/network_manager/actor.rs deleted file mode 100644 index 567ea3c..0000000 --- a/server/src/network/network_manager/actor.rs +++ /dev/null @@ -1,256 +0,0 @@ -use actix::{ - fut::wrap_future, - Actor, - ActorFutureExt, - Addr, - AsyncContext, - Context, - Handler, - WeakAddr, - WeakRecipient, -}; -use foundation::ClientDetails; - -use crate::{ - config_manager::{ConfigManager, ConfigManagerDataMessage, ConfigValue}, - network::{ - listener::{ListenerMessage, ListenerOutput, NetworkListener}, - network_manager::{ - messages::{NetworkMessage, NetworkOutput}, - Builder, - }, - Connection, - ConnectionInitiator, - InitiatorOutput, - NetworkDataMessage, - NetworkDataOutput, - }, -}; - -/// # NetworkManager -/// this struct will handle all networking functionality. -/// -pub struct NetworkManager { - config_manager: WeakAddr, - listener_addr: Option>, - delegate: WeakRecipient, - initiators: Vec>, -} - -impl NetworkManager { - pub fn new(delegate: WeakRecipient) -> Addr { - NetworkManager { - listener_addr: None, - delegate, - initiators: Vec::new(), - config_manager: ConfigManager::shared().downgrade(), - } - .start() - } - - pub fn create(delegate: WeakRecipient) -> Builder { - Builder::new(delegate) - } - - fn start_listener(&mut self, _ctx: &mut ::Context) { - use ListenerMessage::StartListening; - - println!("[NetworkManager] got Listen message"); - - if let Some(addr) = self.listener_addr.as_ref() { - addr.do_send(StartListening); - } - } - - fn stop_listener(&mut self, _ctx: &mut ::Context) { - use ListenerMessage::StopListening; - if let Some(addr) = self.listener_addr.as_ref() { - addr.do_send(StopListening); - } - } - - /// Handles a new connection from the Listener. - /// This creates a new ConnectionInitaliser. - /// This completes the first part of the protocol. - #[inline] - fn new_connection( - &mut self, - ctx: &mut ::Context, - connection: Addr, - ) { - println!("[NetworkManager] Got new connection"); - - let init = ConnectionInitiator::new( - ctx.address().recipient().downgrade(), - connection, - ); - self.initiators.push(init); - } - - #[inline] - fn remove_initiator(&mut self, sender: WeakAddr) { - if let Some(sender) = sender.upgrade() { - let index = self.initiators.iter().position(|i| *i == sender).unwrap(); - println!("[NetworkManager] removed initiator at:{}", index); - let _ = self.initiators.remove(index); - } - } - - /// handles a initiator client request - /// this will, forward the conenction and client details - /// to the server actor to be dispatched to the appropriate - /// manager - #[inline] - fn client_request( - &mut self, - _ctx: &mut ::Context, - sender: WeakAddr, - connection: Addr, - client_details: ClientDetails, - ) { - use NetworkOutput::NewClient; - println!("[NetworkManager] recieved client request"); - if let Some(delegate) = self.delegate.upgrade() { - delegate.do_send(NewClient(connection, client_details)); - } - self.remove_initiator(sender); - } - - /// This sends the connection to the server - /// which will in turn take over the protocol by sending - /// the servers infomation. - #[inline] - fn info_request( - &mut self, - _ctx: &mut ::Context, - sender: WeakAddr, - connection: Addr, - ) { - use NetworkOutput::InfoRequested; - println!("[NetworkManager] Got recieved info request"); - if let Some(delegate) = self.delegate.upgrade() { - delegate.do_send(InfoRequested(connection)); - } - self.remove_initiator(sender); - } -} - -impl Actor for NetworkManager { - type Context = Context; - - fn started(&mut self, ctx: &mut Self::Context) { - println!("[NetworkManager] Starting"); - let config_mgr = self.config_manager.clone().upgrade(); - - if let Some(config_mgr) = config_mgr { - let fut = wrap_future(config_mgr.send( - ConfigManagerDataMessage::GetValue("Network.Port".to_owned()), - )) - .map( - |out, actor: &mut NetworkManager, ctx: &mut Context| { - use crate::config_manager::ConfigManagerDataResponse::GotValue; - - println!("[NetworkManager] got config manager value {:?}", out); - - let recipient = ctx.address().recipient(); - - let port = if let Ok(GotValue(Some(ConfigValue::Number(port)))) = out - { - port - } else { - 5600 - }; - println!("[NetworkManager] got port: {:?}", port); - let nl = NetworkListener::new( - format!("0.0.0.0:{}", port), - recipient.downgrade(), - ); - nl.do_send(ListenerMessage::StartListening); - actor.listener_addr.replace(nl); - }, - ); - ctx.spawn(fut); - } - } - - fn stopped(&mut self, ctx: &mut Self::Context) { - println!("[NetworkManager] network manager stopped"); - } -} - -impl Handler for NetworkManager { - type Result = (); - fn handle( - &mut self, - msg: NetworkMessage, - ctx: &mut ::Context, - ) -> >::Result { - use NetworkMessage::{StartListening, StopListening}; - match msg { - StartListening => self.start_listener(ctx), - StopListening => self.stop_listener(ctx), - } - } -} - -impl Handler for NetworkManager { - type Result = NetworkDataOutput; - - fn handle( - &mut self, - msg: NetworkDataMessage, - _ctx: &mut Self::Context, - ) -> Self::Result { - match msg { - NetworkDataMessage::IsListening => { - NetworkDataOutput::IsListening(self.listener_addr.is_some()) - } - } - } -} - -impl Handler for NetworkManager { - type Result = (); - fn handle( - &mut self, - msg: ListenerOutput, - ctx: &mut Self::Context, - ) -> Self::Result { - use ListenerOutput::NewConnection; - match msg { - NewConnection(connection) => { - println!("new connection"); - self.new_connection(ctx, connection) - } - }; - } -} - -impl Handler for NetworkManager { - type Result = (); - fn handle( - &mut self, - msg: InitiatorOutput, - ctx: &mut Self::Context, - ) -> Self::Result { - use InitiatorOutput::{ClientRequest, InfoRequest}; - match msg { - ClientRequest(sender, addr, client_details) => { - self.client_request(ctx, sender, addr, client_details) - } - InfoRequest(sender, addr) => self.info_request(ctx, sender, addr), - } - } -} - -impl From for NetworkManager { - fn from(builder: Builder) -> Self { - Self { - listener_addr: None, - delegate: builder.delegate, - - initiators: Vec::default(), - config_manager: ConfigManager::shared().downgrade(), - } - } -} diff --git a/server/src/network/network_manager/builder.rs b/server/src/network/network_manager/builder.rs deleted file mode 100644 index 2656ffc..0000000 --- a/server/src/network/network_manager/builder.rs +++ /dev/null @@ -1,20 +0,0 @@ -use actix::{Actor, Addr, WeakRecipient}; - -use crate::network::{ - network_manager::messages::NetworkOutput, - NetworkManager, -}; - -pub struct Builder { - pub(super) delegate: WeakRecipient, -} - -impl Builder { - pub(super) fn new(delegate: WeakRecipient) -> Self { - Self { delegate } - } - - pub fn build(self) -> Addr { - NetworkManager::from(self).start() - } -} diff --git a/server/src/network/network_manager/messages.rs b/server/src/network/network_manager/messages.rs deleted file mode 100644 index 8c740b2..0000000 --- a/server/src/network/network_manager/messages.rs +++ /dev/null @@ -1,29 +0,0 @@ -use actix::{Addr, Message, MessageResponse}; -use foundation::ClientDetails; - -use crate::network::Connection; - -#[derive(Message, Debug, Ord, PartialOrd, Eq, PartialEq)] -#[rtype(result = "()")] -pub enum NetworkMessage { - StartListening, - StopListening, -} - -#[derive(Message)] -#[rtype(result = "()")] -pub enum NetworkOutput { - NewClient(Addr, ClientDetails), - InfoRequested(Addr), -} - -#[derive(Message, Debug, Ord, PartialOrd, Eq, PartialEq)] -#[rtype(result = "NetworkDataOutput")] -pub enum NetworkDataMessage { - IsListening, -} - -#[derive(MessageResponse)] -pub enum NetworkDataOutput { - IsListening(bool), -} diff --git a/server/src/network/network_manager/mod.rs b/server/src/network/network_manager/mod.rs deleted file mode 100644 index ddfbf1b..0000000 --- a/server/src/network/network_manager/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! # network_manager -//! This module contains the network manager actor -//! it's role involves handling new oncomming network connections - -mod actor; -mod builder; -mod messages; - -pub(crate) use actor::NetworkManager; -pub(crate) use builder::*; -pub(crate) use messages::{ - NetworkDataMessage, - NetworkDataOutput, - NetworkMessage, - NetworkOutput, -}; diff --git a/server/src/network/protobuf/mod.rs b/server/src/network/protobuf/mod.rs new file mode 100644 index 0000000..ef4a749 --- /dev/null +++ b/server/src/network/protobuf/mod.rs @@ -0,0 +1,4 @@ +pub mod protobuf_client_reader; +pub mod protobuf_client_writer; +pub mod protobuf_listener; +pub mod protobuf_network_connection; diff --git a/server/src/network/protobuf/protobuf_client_reader.rs b/server/src/network/protobuf/protobuf_client_reader.rs new file mode 100644 index 0000000..8809a05 --- /dev/null +++ b/server/src/network/protobuf/protobuf_client_reader.rs @@ -0,0 +1,118 @@ +use std::{io, net::SocketAddr}; + +use foundation::{ + networking::protobuf::read_message, + prelude::{ + connected_client_message, + ConnectedClientMessage, + Disconnect, + GetClients, + GetGlobalMessages, + SendGlobalMessage, + SendPrivateMessage, + }, +}; +use tokio::{io::ReadHalf, net::TcpStream, sync::mpsc::UnboundedSender}; +use uuid::Uuid; + +use crate::{ + connection::connection_manager::ConnectionManagerMessage, + network::ClientReader, +}; + +pub struct ProtobufClientReader { + reader: ReadHalf, + addr: SocketAddr, + uuid: Uuid, +} + +impl ProtobufClientReader { + pub fn new( + reader: ReadHalf, + addr: SocketAddr, + uuid: Uuid, + ) -> Self { + Self { reader, addr, uuid } + } + + // move to other one + pub async fn get_message(&mut self) -> io::Result { + read_message::>( + &mut self.reader, + ) + .await + } + + pub fn handle_message( + &self, + + msg: ConnectedClientMessage, + channel: &UnboundedSender, + ) { + use connected_client_message::Message; + + println!("[ProtobufClientReader:{}] got message", self.addr); + + let uuid = self.uuid; + + _ = match msg { + ConnectedClientMessage { + message: Some(Message::GetClients(GetClients {})), + } => channel.send(ConnectionManagerMessage::SendClientsTo { uuid }), + ConnectedClientMessage { + message: Some(Message::GetGlobalMessage(GetGlobalMessages {})), + } => channel.send(ConnectionManagerMessage::SendGlobalMessages { uuid }), + ConnectedClientMessage { + message: + Some(Message::SendPrivateMessage(SendPrivateMessage { + uuid: message_uuid, + to, + content, + })), + } => channel.send(ConnectionManagerMessage::SendPrivateMessage { + uuid: message_uuid.parse().unwrap(), + from: uuid, + to: to.parse().unwrap(), + content, + }), + ConnectedClientMessage { + message: Some(Message::SendGlobalMessage(SendGlobalMessage { content })), + } => channel.send(ConnectionManagerMessage::BroadcastGlobalMessage { + from: uuid, + content, + }), + ConnectedClientMessage { + message: Some(Message::Disconnect(Disconnect {})), + } => channel.send(ConnectionManagerMessage::Disconnect { uuid }), + ConnectedClientMessage { message: None } => unimplemented!(), + }; + } +} + +impl ClientReader for ProtobufClientReader { + fn start_run( + mut self: Box, + uuid: Uuid, + channel: UnboundedSender, + ) -> tokio::task::JoinHandle<()> { + tokio::spawn(async move { + loop { + let msg = self.get_message().await; + + let Ok(msg) = msg else { + let error = msg.unwrap_err(); + println!( + "[ProtobufClientReader:{}] errored with '{}' disconnecting", + self.addr, error + ); + + _ = channel.send(ConnectionManagerMessage::Disconnected { uuid }); + + return; + }; + + self.handle_message(msg, &channel); + } + }) + } +} diff --git a/server/src/network/protobuf/protobuf_client_writer.rs b/server/src/network/protobuf/protobuf_client_writer.rs new file mode 100644 index 0000000..51aa04a --- /dev/null +++ b/server/src/network/protobuf/protobuf_client_writer.rs @@ -0,0 +1,175 @@ +use std::net::SocketAddr; + +use async_trait::async_trait; +use foundation::{ + networking::protobuf::write_message, + prelude::{ + connected_server_message, + ClientConnected, + ClientDetails, + ClientDisconnected, + ConnectedClients, + ConnectedServerMessage, + Disconnected, + GlobalMessage, + GlobalMessages, + PrivateMessage, + }, +}; +use tokio::{io::WriteHalf, net::TcpStream}; +use uuid::Uuid; + +use crate::network::ClientWriter; + +#[allow(dead_code)] +pub struct ProtobufClientWriter { + writer: WriteHalf, + addr: SocketAddr, + uuid: Uuid, +} + +impl ProtobufClientWriter { + pub fn new( + writer: WriteHalf, + addr: SocketAddr, + uuid: Uuid, + ) -> Self { + Self { writer, addr, uuid } + } + + #[deprecated] + pub async fn send_clients(&mut self, clients: Vec) { + let message = ConnectedServerMessage { + message: Some(connected_server_message::Message::ConnectedClients( + ConnectedClients { clients }, + )), + }; + println!("[ProtobufClientWriter:{}] sending clients", self.addr); + write_message(&mut self.writer, message).await.unwrap(); + } + + #[deprecated] + pub async fn send_global_messages(&mut self, messages: Vec) { + let message = ConnectedServerMessage { + message: Some(connected_server_message::Message::GlobalMessages( + GlobalMessages { messages }, + )), + }; + println!( + "[ProtobufClientWriter:{}] sending global messages", + self.addr + ); + write_message(&mut self.writer, message).await.unwrap(); + } + + #[deprecated] + pub async fn send_private_message(&mut self, message: PrivateMessage) { + let message = ConnectedServerMessage { + message: Some(connected_server_message::Message::PrivateMessage(message)), + }; + println!( + "[ProtobufClientWriter:{}] sending private message", + self.addr + ); + write_message(&mut self.writer, message).await.unwrap(); + } + #[deprecated] + pub async fn send_disconnect(&mut self) { + let message = ConnectedServerMessage { + message: Some(connected_server_message::Message::Disconnected( + Disconnected { + reason: "shutting down".into(), + }, + )), + }; + println!("[ProtobufClientWriter:{}] sending disconnect", self.addr); + write_message(&mut self.writer, message).await.unwrap(); + } +} + +#[async_trait] +impl ClientWriter for ProtobufClientWriter { + async fn send_clients(&mut self, clients: Vec) { + let message = ConnectedServerMessage { + message: Some(connected_server_message::Message::ConnectedClients( + ConnectedClients { clients }, + )), + }; + println!("[ProtobufClientWriter:{}] sending clients", self.addr); + write_message(&mut self.writer, message).await.unwrap(); + } + + async fn send_client_joined(&mut self, details: ClientDetails) { + let message = ConnectedServerMessage { + message: Some(connected_server_message::Message::ClientConnected( + ClientConnected { + details: Some(details), + }, + )), + }; + println!( + "[ProtobufClientWriter:{}] sending client connected message", + self.addr + ); + write_message(&mut self.writer, message).await.unwrap(); + } + + async fn send_client_left(&mut self, uuid: Uuid) { + let message = ConnectedServerMessage { + message: Some(connected_server_message::Message::ClientDisconnected( + ClientDisconnected { + uuid: uuid.to_string(), + }, + )), + }; + println!( + "[ProtobufClientWriter:{}] sending client connected message", + self.addr + ); + write_message(&mut self.writer, message).await.unwrap(); + } + + async fn send_global_messages(&mut self, messages: Vec) { + let message = ConnectedServerMessage { + message: Some(connected_server_message::Message::GlobalMessages( + GlobalMessages { messages }, + )), + }; + println!( + "[ProtobufClientWriter:{}] sending global messages", + self.addr + ); + write_message(&mut self.writer, message).await.unwrap(); + } + + async fn send_global_message(&mut self, message: GlobalMessage) { + let message = ConnectedServerMessage { + message: Some(connected_server_message::Message::GlobalMessage(message)), + }; + println!("[ProtobufClientWriter:{}] sending disconnect", self.addr); + write_message(&mut self.writer, message).await.unwrap(); + } + + async fn send_private_message(&mut self, message: PrivateMessage) { + let message = ConnectedServerMessage { + message: Some(connected_server_message::Message::PrivateMessage(message)), + }; + println!( + "[ProtobufClientWriter:{}] sending private message", + self.addr + ); + write_message(&mut self.writer, message).await.unwrap(); + } + + async fn send_disconnect(&mut self) { + let message = ConnectedServerMessage { + message: Some(connected_server_message::Message::Disconnected( + Disconnected { + reason: "shutting down".into(), + }, + )), + }; + println!("[ProtobufClientWriter:{}] sending disconnect", self.addr); + write_message(&mut self.writer, message).await.unwrap(); + } +} diff --git a/server/src/network/protobuf/protobuf_listener.rs b/server/src/network/protobuf/protobuf_listener.rs new file mode 100644 index 0000000..67c3554 --- /dev/null +++ b/server/src/network/protobuf/protobuf_listener.rs @@ -0,0 +1,55 @@ +use async_trait::async_trait; +use tokio::{net::TcpListener, sync::mpsc::UnboundedSender, task::JoinHandle}; + +use crate::{ + network::{ConnectionType, NetworkListener}, + server_va::ServerMessages, +}; + +/// # Listener Manager +/// This stores and awaits for connections from listeners. +/// When a connection is received, it is passed to the server +pub struct ProtobufListener { + protobuf_listener: TcpListener, + sender: UnboundedSender, +} + +#[async_trait] +impl NetworkListener for ProtobufListener { + /// Binds listeners and stores them in the ListenerManager + async fn new(channel: UnboundedSender) -> Self { + println!("[ProtobufListener] setting up listeners"); + let protobuf_listener = TcpListener::bind("0.0.0.0:6500") + .await + .expect("[ProtobufListener] failed to bind to 0.0.0.0:6500"); + + Self { + protobuf_listener, + sender: channel, + } + } + + async fn run(&self) { + loop { + println!("[ProtobufListener] waiting for connection"); + let accept_protobuf = self.protobuf_listener.accept().await; + let Ok((stream, addr)) = accept_protobuf else { + println!("[ProtobufListener] accept failed"); + continue; + }; + + let msg = ServerMessages::NewConnection( + ConnectionType::ProtobufConnection(stream, addr), + ); + + println!("[ProtobufListener] passing message to server"); + _ = self.sender.send(msg); + } + } + + fn start_run(sender: UnboundedSender) -> JoinHandle<()> { + tokio::spawn(async move { + ProtobufListener::new(sender).await.run().await; + }) + } +} diff --git a/server/src/network/protobuf/protobuf_network_connection.rs b/server/src/network/protobuf/protobuf_network_connection.rs new file mode 100644 index 0000000..b1f6cae --- /dev/null +++ b/server/src/network/protobuf/protobuf_network_connection.rs @@ -0,0 +1,174 @@ +use std::{io, net::SocketAddr}; + +use async_trait::async_trait; +use foundation::{ + networking::protobuf::{read_message, write_message}, + prelude::{ + network_client_message, + network_server_message, + Connect, + Connected, + GetInfo, + Info, + NetworkClientMessage, + NetworkServerMessage, + Request, + }, +}; +use tokio::{io::split, net::TcpStream}; +use uuid::Uuid; + +use crate::network::{ + protobuf::{ + protobuf_client_reader::ProtobufClientReader, + protobuf_client_writer::ProtobufClientWriter, + }, + ClientReader, + ClientWriter, + NetworkConnection, + ServerRequest, +}; + +pub struct ProtobufNetworkConnection { + pub(super) stream: TcpStream, + pub(super) addr: SocketAddr, +} + +impl ProtobufNetworkConnection { + pub fn new(stream: TcpStream, addr: SocketAddr) -> Self { + Self { stream, addr } + } + + pub async fn get_request(&mut self) -> io::Result { + let message = NetworkServerMessage { + message: Some(network_server_message::Message::Request(Request {})), + }; + + println!("[ProtobufNetworkConnection] sending request"); + write_message(&mut self.stream, message).await.unwrap(); + + println!("[ProtobufNetworkConnection] waiting for response"); + let request = + read_message::(&mut self.stream) + .await + .unwrap(); + + println!("[ProtobufNetworkConnection] returning request"); + match request { + NetworkClientMessage { + message: Some(network_client_message::Message::GetInfo(GetInfo {})), + } => Ok(ServerRequest::GetInfo), + NetworkClientMessage { + message: + Some(network_client_message::Message::Connect(Connect { + username, + uuid, + })), + } => Ok(ServerRequest::Connect { + username, + uuid: uuid.parse().unwrap(), + addr: self.addr, + }), + _ => Ok(ServerRequest::Ignore), + } + } + + pub async fn send_info(mut self, name: String, owner: String) { + let message = NetworkServerMessage { + message: Some(network_server_message::Message::GotInfo(Info { + server_name: name, + owner, + })), + }; + println!("[ProtobufNetworkConnection] Sending info to client"); + write_message(&mut self.stream, message).await.unwrap(); + println!("[ProtobufNetworkConnection] droping connection"); + } + + pub async fn send_connected( + mut self, + uuid: Uuid, + ) -> (ProtobufClientWriter, ProtobufClientReader) { + let message = NetworkServerMessage { + message: Some(network_server_message::Message::Connected(Connected {})), + }; + + write_message(&mut self.stream, message).await.unwrap(); + + self.into(uuid) + } + + fn into(self, uuid: Uuid) -> (ProtobufClientWriter, ProtobufClientReader) { + let (read, write) = split(self.stream); + + let writer = ProtobufClientWriter::new(write, self.addr, uuid); + let reader = ProtobufClientReader::new(read, self.addr, uuid); + (writer, reader) + } +} + +#[async_trait] +impl NetworkConnection for ProtobufNetworkConnection { + async fn get_request(&mut self) -> io::Result { + let message = NetworkServerMessage { + message: Some(network_server_message::Message::Request(Request {})), + }; + + println!("[ProtobufNetworkConnection] sending request"); + write_message(&mut self.stream, message).await.unwrap(); + + println!("[ProtobufNetworkConnection] waiting for response"); + let request = + read_message::(&mut self.stream) + .await + .unwrap(); + + println!("[ProtobufNetworkConnection] returning request"); + match request { + NetworkClientMessage { + message: Some(network_client_message::Message::GetInfo(GetInfo {})), + } => Ok(ServerRequest::GetInfo), + NetworkClientMessage { + message: + Some(network_client_message::Message::Connect(Connect { + username, + uuid, + })), + } => Ok(ServerRequest::Connect { + username, + uuid: uuid.parse().unwrap(), + addr: self.addr, + }), + _ => Ok(ServerRequest::Ignore), + } + } + + async fn send_info(mut self: Box, name: String, owner: String) { + let message = NetworkServerMessage { + message: Some(network_server_message::Message::GotInfo(Info { + server_name: name, + owner, + })), + }; + println!("[ProtobufNetworkConnection] Sending info to client"); + write_message(&mut self.stream, message).await.unwrap(); + println!("[ProtobufNetworkConnection] droping connection"); + } + + async fn send_connected( + mut self: Box, + uuid: Uuid, + ) -> (Box, Box) { + let message = NetworkServerMessage { + message: Some(network_server_message::Message::Connected(Connected {})), + }; + + write_message(&mut self.stream, message).await.unwrap(); + + let (read, write) = split(self.stream); + + let writer = Box::new(ProtobufClientWriter::new(write, self.addr, uuid)); + let reader = Box::new(ProtobufClientReader::new(read, self.addr, uuid)); + (writer, reader) + } +} diff --git a/server/src/os_signal_manager.rs b/server/src/os_signal_manager.rs new file mode 100644 index 0000000..f1f7c0f --- /dev/null +++ b/server/src/os_signal_manager.rs @@ -0,0 +1,27 @@ +use tokio::sync::mpsc::UnboundedSender; + +use crate::server_va::ServerMessages; + +pub struct OSSignalManager { + server_channel: UnboundedSender, +} + +impl OSSignalManager { + pub fn new(channel: UnboundedSender) -> Self { + Self { + server_channel: channel, + } + } + + pub async fn run(&self) { + loop { + println!("[OSSignalManager] waiting for ctrl+c"); + tokio::signal::ctrl_c().await.unwrap(); + println!("[OSSignalManager] ctrl+c received, closing down server"); + self + .server_channel + .send(ServerMessages::Exit) + .expect("[OSSignalManager] server channel closed"); + } + } +} diff --git a/server/src/prelude/mod.rs b/server/src/prelude/mod.rs deleted file mode 100644 index 6c2d7f9..0000000 --- a/server/src/prelude/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! # prelude -//! A module that coalesces different types into one module of defined structure - -mod observer; - -#[allow(unused_imports)] -pub mod actors { - //! exports all actors used in the program. - pub use crate::server::Server; - pub(crate) use crate::{ - client_management::{client::Client, ClientManager}, - network::{Connection, ConnectionInitiator, NetworkManager}, - }; -} - -#[allow(unused_imports)] -pub mod messages { - //! exports all messages used in the program. - pub(crate) use super::observer::ObservableMessage; - pub(crate) use crate::{ - client_management::{ClientManagerMessage, ClientManagerOutput}, - network::{ - ConnectionMessage, - ConnectionObservableOutput, - NetworkMessage, - NetworkOutput, - }, - }; -} diff --git a/server/src/prelude/observer.rs b/server/src/prelude/observer.rs deleted file mode 100644 index dd8334c..0000000 --- a/server/src/prelude/observer.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! # observer.rs -//! crates a message type for the observer pattern. - -use actix::{Message, WeakRecipient}; - -/// # ObservableMessage -/// represents common messages for observers -#[derive(Message)] -#[rtype(result = "()")] -pub enum ObservableMessage -where - M: Message + Send, - M::Result: Send, -{ - Subscribe(WeakRecipient), - Unsubscribe(WeakRecipient), -} diff --git a/server/src/rhai/builder.rs b/server/src/rhai/builder.rs deleted file mode 100644 index 04239a9..0000000 --- a/server/src/rhai/builder.rs +++ /dev/null @@ -1,62 +0,0 @@ -use actix::{Actor, Addr, WeakAddr}; -use rhai::{Engine, Scope}; - -use crate::{ - client_management::ClientManager, - network::NetworkManager, - rhai::rhai_manager::RhaiManager, - Server, -}; - -pub struct Builder { - engine: Engine, - server: WeakAddr, - network_manager: WeakAddr, - client_manager: WeakAddr, - scope: Scope<'static>, -} - -impl Builder { - pub(super) fn new( - server: WeakAddr, - network_manager: WeakAddr, - client_manager: WeakAddr, - ) -> Self { - Builder { - engine: Engine::new(), - server, - network_manager, - client_manager, - scope: Default::default(), - } - } - - pub fn scope_object(mut self, name: &str, obj: T) -> Self - where - T: Clone, - { - self.engine.register_type::(); - self.scope.set_value(name, obj); - self - } - - // not sure what this is for? - // pub fn scope_fn(mut self, name: &str, func: F) -> Self - // where - // F: RegisterNativeFunction, - // { - // self.engine.register_fn(name, func); - // self - // } - - pub(crate) fn build(self) -> Addr { - RhaiManager { - engine: self.engine, - _scope: self.scope, - _server: self.server, - _network_manager: self.network_manager, - _client_manager: self.client_manager, - } - .start() - } -} diff --git a/server/src/rhai/mod.rs b/server/src/rhai/mod.rs deleted file mode 100644 index 7c00b43..0000000 --- a/server/src/rhai/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod builder; -mod rhai_manager; - -pub use rhai_manager::RhaiManager; diff --git a/server/src/rhai/rhai_manager.rs b/server/src/rhai/rhai_manager.rs deleted file mode 100644 index cf1e75d..0000000 --- a/server/src/rhai/rhai_manager.rs +++ /dev/null @@ -1,47 +0,0 @@ -use actix::{Actor, Context, WeakAddr}; -use rhai::{Engine, Scope}; - -use crate::{ - client_management::ClientManager, - network::NetworkManager, - rhai::builder::Builder, - Server, -}; - -pub struct RhaiManager { - pub(super) engine: Engine, - pub(super) _scope: Scope<'static>, - pub(super) _server: WeakAddr, - pub(super) _network_manager: WeakAddr, - pub(super) _client_manager: WeakAddr, -} - -impl RhaiManager { - pub fn create( - server: WeakAddr, - network_manager: WeakAddr, - client_manager: WeakAddr, - ) -> Builder { - Builder::new( - server.clone(), - network_manager.clone(), - client_manager.clone(), - ) - .scope_object("server", server) - } -} - -impl Actor for RhaiManager { - type Context = Context; - - fn started(&mut self, _ctx: &mut Self::Context) { - self - .engine - .run( - r#" - print("hello rhai") - "#, - ) - .unwrap(); - } -} diff --git a/server/src/scripting/mod.rs b/server/src/scripting/mod.rs deleted file mode 100644 index d984769..0000000 --- a/server/src/scripting/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub(crate) mod scriptable_client; -pub(crate) mod scriptable_client_manager; -pub(crate) mod scriptable_network_manager; -pub(crate) mod scriptable_server; diff --git a/server/src/scripting/scriptable_client.rs b/server/src/scripting/scriptable_client.rs deleted file mode 100644 index b07ef98..0000000 --- a/server/src/scripting/scriptable_client.rs +++ /dev/null @@ -1,60 +0,0 @@ -use actix::Addr; -use mlua::{Error, UserData, UserDataMethods}; - -use crate::client_management::client::{ - Client, - ClientDataMessage, - ClientDataResponse, - ClientDataResponse::{Username, Uuid}, -}; - -#[derive(Clone)] -pub(crate) struct ScriptableClient { - addr: Addr, -} - -impl UserData for ScriptableClient { - fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_async_method("username", |_lua, obj, ()| async move { - let name: Option = - obj.addr.send(ClientDataMessage::Username).await.ok(); - if let Some(Username(name)) = name { - Ok(name) - } else { - Err(Error::RuntimeError( - "Name returned null or other value".to_string(), - )) - } - }); - - methods.add_async_method("uuid", |_lua, obj, ()| async move { - let uuid: Option = - obj.addr.send(ClientDataMessage::Uuid).await.ok(); - if let Some(Uuid(uuid)) = uuid { - Ok(uuid.to_string()) - } else { - Err(Error::RuntimeError( - "Uuid returned null or other value".to_string(), - )) - } - }); - - methods.add_async_method("address", |_lua, obj, ()| async move { - let address: Option = - obj.addr.send(ClientDataMessage::Address).await.ok(); - if let Some(Username(address)) = address { - Ok(address) - } else { - Err(Error::RuntimeError( - "address returned null or other value".to_string(), - )) - } - }); - } -} - -impl From> for ScriptableClient { - fn from(addr: Addr) -> Self { - Self { addr } - } -} diff --git a/server/src/scripting/scriptable_client_manager.rs b/server/src/scripting/scriptable_client_manager.rs deleted file mode 100644 index 1bf3521..0000000 --- a/server/src/scripting/scriptable_client_manager.rs +++ /dev/null @@ -1,43 +0,0 @@ -use actix::Addr; -use mlua::{Error, UserData, UserDataMethods}; - -use crate::{ - client_management::{ - ClientManager, - ClientManagerDataMessage, - ClientManagerDataResponse::Clients, - }, - scripting::scriptable_client::ScriptableClient, -}; - -#[derive(Clone)] -pub(crate) struct ScriptableClientManager { - addr: Addr, -} - -impl UserData for ScriptableClientManager { - fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_async_method("clients", |_lua, obj, ()| async move { - let res = obj.addr.send(ClientManagerDataMessage::Clients).await; - if let Ok(Clients(clients)) = res { - let clients: Vec = clients - .into_iter() - .filter_map(|a| a.upgrade()) - .map(ScriptableClient::from) - .collect(); - - Ok(clients) - } else { - Err(Error::RuntimeError( - "clients returned null or other value".to_string(), - )) - } - }) - } -} - -impl From> for ScriptableClientManager { - fn from(addr: Addr) -> Self { - Self { addr } - } -} diff --git a/server/src/scripting/scriptable_network_manager.rs b/server/src/scripting/scriptable_network_manager.rs deleted file mode 100644 index d1ba282..0000000 --- a/server/src/scripting/scriptable_network_manager.rs +++ /dev/null @@ -1,35 +0,0 @@ -use actix::Addr; -use mlua::{Error, UserData, UserDataMethods}; - -use crate::network::{ - NetworkDataMessage, - NetworkDataOutput::IsListening, - NetworkManager, -}; - -#[derive(Clone)] -pub(crate) struct ScriptableNetworkManager { - addr: Addr, -} - -impl UserData for ScriptableNetworkManager { - fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_async_method("Listening", |_lua, obj, ()| async move { - let is_listening = - obj.addr.send(NetworkDataMessage::IsListening).await.ok(); - if let Some(IsListening(is_listening)) = is_listening { - Ok(is_listening) - } else { - Err(Error::RuntimeError( - "Uuid returned null or other value".to_string(), - )) - } - }); - } -} - -impl From> for ScriptableNetworkManager { - fn from(addr: Addr) -> Self { - Self { addr } - } -} diff --git a/server/src/scripting/scriptable_server.rs b/server/src/scripting/scriptable_server.rs deleted file mode 100644 index 02265b4..0000000 --- a/server/src/scripting/scriptable_server.rs +++ /dev/null @@ -1,55 +0,0 @@ -use actix::WeakAddr; -use mlua::{Error, UserData, UserDataMethods}; - -use crate::server::{ServerDataResponse::Name, *}; - -#[derive(Clone)] -pub(crate) struct ScriptableServer { - pub(super) addr: WeakAddr, -} - -impl UserData for ScriptableServer { - fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_async_method("name", |_lua, obj, ()| async move { - let Some(send_fut) = obj.addr.upgrade().map(|addr| addr.send(ServerDataMessage::Name)) else { - return Err(Error::RuntimeError( - "[ScriptableServer:name] Server doesn't exist. Dunno how you got here".to_string(), - )) - }; - - let name: Option = send_fut.await.ok(); - - let Some(Name(name)) = name else { - return Err(Error::RuntimeError( - "[ScriptableServer:name] Name returned nil".to_string(), - )) - }; - - Ok(name) - }); - - methods.add_async_method("owner", |_lua, obj, ()| async move { - let Some(send_fut) = obj.addr.upgrade().map(|addr| addr.send(ServerDataMessage::Owner)) else { - return Err(Error::RuntimeError( - "[ScriptableServer:owner] Server doesn't exist. Dunno how you got here".to_string(), - )) - }; - - let owner: Option = send_fut.await.ok(); - - let Some(Name(owner)) = owner else { - return Err(Error::RuntimeError( - "[ScriptableServer:owner] Owner returned nil".to_string(), - )) - }; - - Ok(owner) - }); - } -} - -impl From> for ScriptableServer { - fn from(addr: WeakAddr) -> Self { - Self { addr } - } -} diff --git a/server/src/server/builder.rs b/server/src/server/builder.rs deleted file mode 100644 index b994842..0000000 --- a/server/src/server/builder.rs +++ /dev/null @@ -1,31 +0,0 @@ -use actix::{Actor, Addr}; - -use super::*; - -pub struct ServerBuilder { - pub(super) name: String, - pub(super) owner: String, -} - -impl<'rhai> ServerBuilder { - pub(super) fn new() -> Self { - Self { - name: "".into(), - owner: "".into(), - } - } - - pub fn name(mut self, name: String) -> Self { - self.name = name; - self - } - - pub fn owner(mut self, owner: String) -> Self { - self.owner = owner; - self - } - - pub fn build(self) -> Addr { - Server::from(self).start() - } -} diff --git a/server/src/server/messages.rs b/server/src/server/messages.rs deleted file mode 100644 index 215b3ff..0000000 --- a/server/src/server/messages.rs +++ /dev/null @@ -1,21 +0,0 @@ -use actix::{Addr, Message, MessageResponse}; - -use crate::{client_management::ClientManager, network::NetworkManager}; - -#[derive(Message, Clone)] -#[rtype(result = "ServerDataResponse")] -pub enum ServerDataMessage { - Name, - Owner, - ClientManager, - NetworkManager, -} - -#[derive(MessageResponse, Clone)] -pub enum ServerDataResponse { - Name(String), - Port(u16), - Owner(String), - ClientManager(Option>), - NetworkManager(Option>), -} diff --git a/server/src/server/mod.rs b/server/src/server/mod.rs deleted file mode 100644 index 6e7470f..0000000 --- a/server/src/server/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! # actix_server -//! this holds the server actor -//! the server acts as teh main actor -//! and supervisor to the actor system. - -mod server; - -mod builder; -mod messages; - -pub use builder::ServerBuilder; -pub use messages::*; -pub use server::Server; diff --git a/server/src/server/server.rs b/server/src/server/server.rs deleted file mode 100644 index 9529aa9..0000000 --- a/server/src/server/server.rs +++ /dev/null @@ -1,211 +0,0 @@ -//! This crate holds the implementations and functions for the server -//! including server boot procedures - -use actix::{ - fut::wrap_future, - Actor, - ActorFutureExt, - Addr, - AsyncContext, - Context, - Handler, -}; -use foundation::{messages::network::NetworkSockOut::GotInfo, ClientDetails}; - -use crate::{ - client_management::{ - client::Client, - ClientManager, - ClientManagerMessage::AddClient, - ClientManagerOutput, - }, - config_manager::{ - ConfigManager, - ConfigManagerDataMessage, - ConfigManagerDataResponse, - ConfigValue, - }, - lua::LuaManager, - network::{ - Connection, - ConnectionMessage::{CloseConnection, SendData}, - NetworkManager, - NetworkOutput, - NetworkOutput::{InfoRequested, NewClient}, - }, - prelude::messages::NetworkMessage, - rhai::RhaiManager, - server::{builder, ServerBuilder, ServerDataMessage, ServerDataResponse}, -}; - -/// This struct is the main actor of the server. -/// all other actors are ran through here. -pub struct Server { - name: String, - owner: String, - - network_manager: Option>, - client_manager: Option>, - rhai_manager: Option>, - lua_manager: Option>, -} - -impl Server { - pub(crate) fn create() -> builder::ServerBuilder { - ServerBuilder::new() - } - - pub(crate) fn client_request( - &mut self, - _ctx: &mut ::Context, - addr: Addr, - details: ClientDetails, - ) { - if let Some(mgr) = self.client_manager.as_ref() { - let client = Client::new(addr, details.clone()); - mgr.do_send(AddClient(details.uuid, client)); - } - } - - pub(crate) fn info_request( - &mut self, - ctx: &mut ::Context, - sender: Addr, - ) { - let fut = wrap_future( - sender.send(SendData( - serde_json::to_string(&GotInfo { - server_name: self.name.clone(), - server_owner: self.owner.clone(), - }) - .expect("Failed to serialise"), - )), - ) - // equivalent to using .then() in js - .map(move |_out, _act: &mut Self, _ctx| { - sender.do_send(CloseConnection); - }); - ctx.spawn(fut); - } -} - -impl Actor for Server { - type Context = Context; - - fn started(&mut self, ctx: &mut Self::Context) { - use ConfigManagerDataMessage::GetValue; - use ConfigManagerDataResponse::GotValue; - - let addr = ctx.address().downgrade(); - - let nm = NetworkManager::create(addr.clone().recipient()).build(); - let cm = ClientManager::new(addr.recipient()); - let rm = RhaiManager::create( - ctx.address().downgrade(), - nm.downgrade(), - cm.downgrade(), - ) - .build(); - let lm = LuaManager::create( - ctx.address().downgrade(), - nm.downgrade(), - cm.downgrade(), - ) - .build(); - - self.network_manager.replace(nm.clone()); - self.client_manager.replace(cm.clone()); - self.rhai_manager.replace(rm); - self.lua_manager.replace(lm); - - nm.do_send(NetworkMessage::StartListening); - - let name_fut = wrap_future( - ConfigManager::shared().send(GetValue("Server.Name".to_owned())), - ) - .map(|out, actor: &mut Server, _ctx| { - if let Ok(GotValue(Some(ConfigValue::String(val)))) = out { - actor.name = val - } - }); - - let owner_fut = wrap_future( - ConfigManager::shared().send(GetValue("Server.Owner".to_owned())), - ) - .map(|out, actor: &mut Server, _ctx| { - if let Ok(GotValue(Some(ConfigValue::String(val)))) = out { - actor.owner = val - } - }); - - ctx.spawn(name_fut); - ctx.spawn(owner_fut); - } -} - -impl Handler for Server { - type Result = ServerDataResponse; - - fn handle( - &mut self, - msg: ServerDataMessage, - _ctx: &mut Self::Context, - ) -> Self::Result { - println!("[Server] got data message"); - match msg { - ServerDataMessage::Name => ServerDataResponse::Name(self.name.clone()), - ServerDataMessage::Owner => ServerDataResponse::Owner(self.owner.clone()), - ServerDataMessage::ClientManager => { - ServerDataResponse::ClientManager(self.client_manager.clone()) - } - ServerDataMessage::NetworkManager => { - ServerDataResponse::NetworkManager(self.network_manager.clone()) - } - } - } -} - -impl Handler for Server { - type Result = (); - fn handle( - &mut self, - msg: NetworkOutput, - ctx: &mut Self::Context, - ) -> Self::Result { - println!("[ServerActor] received message"); - match msg { - // This uses promise like funcionality to queue - // a set of async operations, - // so they occur in the right order - InfoRequested(sender) => self.info_request(ctx, sender), - // A new client is to be added - NewClient(addr, details) => self.client_request(ctx, addr, details), - }; - } -} - -impl Handler for Server { - type Result = (); - - fn handle( - &mut self, - _msg: ClientManagerOutput, - _ctx: &mut Self::Context, - ) -> Self::Result { - todo!() - } -} - -impl From for Server { - fn from(builder: ServerBuilder) -> Self { - Server { - name: builder.name, - owner: builder.owner, - - network_manager: None, - client_manager: None, - rhai_manager: None, - lua_manager: None, - } - } -} diff --git a/server/src/server_va.rs b/server/src/server_va.rs new file mode 100644 index 0000000..8c4cc19 --- /dev/null +++ b/server/src/server_va.rs @@ -0,0 +1,183 @@ +use foundation::prelude::GlobalMessage; +use tokio::{ + sync::{ + mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + Mutex, + }, + task::JoinHandle, +}; +use uuid::Uuid; + +use crate::{ + chat::ChatManager, + connection::connection_manager::{ + ConnectionManager, + ConnectionManagerMessage, + }, + network::{ + json::{ + json_listener::JSONListener, + json_network_connection::JSONNetworkConnection, + }, + protobuf::{ + protobuf_listener::ProtobufListener, + protobuf_network_connection::ProtobufNetworkConnection, + }, + ConnectionType, + NetworkConnection, + NetworkListener, + ServerRequest, + }, + os_signal_manager::OSSignalManager, +}; + +/// # Server +/// Manages communication between components in the server +/// Main functions being the handling of new connections, and setting them up. +pub struct Server { + connection_manager_sender: UnboundedSender, + + chat_manager: ChatManager, + + connection_manager_task: JoinHandle<()>, + listener_task: JoinHandle<()>, + json_listener_task: JoinHandle<()>, + + os_event_manager_task: JoinHandle<()>, + + receiver: Mutex>, +} + +impl Server { + /// Loops the future, reading messages from the servers channel. + /// if exit is received, deconstructs all sub-tasks and exits the loop. + pub async fn run(&mut self) { + loop { + let mut lock = self.receiver.lock().await; + let msg = lock.recv().await; + drop(lock); + + match msg { + Some(ServerMessages::Exit) | None => { + println!("[Server] Shutting down"); + self.shutdown(); + return; + } + Some(ServerMessages::NewConnection( + ConnectionType::ProtobufConnection(stream, addr), + )) => { + let conn = Box::new(ProtobufNetworkConnection::new(stream, addr)); + println!("[Server] New protobuf connection"); + self.handle_protobuf_connection(conn).await; + } + Some(ServerMessages::NewConnection( + ConnectionType::JsonConnection(stream, addr), + )) => { + let conn = Box::new(JSONNetworkConnection::new(stream, addr)); + println!("[Server] New protobuf connection"); + self.handle_protobuf_connection(conn).await; + } + Some(ServerMessages::SendGlobalMessages(uuid)) => { + let messages = self.chat_manager.get_messages(); + println!("[Server] Sending Global Messages"); + _ = self.connection_manager_sender.send( + ConnectionManagerMessage::SendGlobalMessagesTo { uuid, messages }, + ); + } + Some(ServerMessages::AddGlobalMessage(message)) => { + self.chat_manager.add_message(message); + } + }; + } + } + + async fn handle_protobuf_connection( + &self, + mut conn: Box, + ) { + println!("[Server] Getting request"); + let req = conn.get_request().await; + + let Ok(req) = req else { + println!("[Server] Got invalid request"); + return; + }; + + match req { + ServerRequest::GetInfo => { + conn + .send_info("test server".into(), "mickyb18a@gmail.com".into()) + .await + } + ServerRequest::Connect { + username, + uuid, + addr, + } => { + println!("[Server] sending connectionn and info to conneciton manager"); + _ = self.connection_manager_sender.send( + ConnectionManagerMessage::AddClient { + conn, + uuid, + username, + addr, + }, + ); + } + ServerRequest::Ignore => todo!(), + } + } + + fn shutdown(&self) { + self.os_event_manager_task.abort(); + self.connection_manager_task.abort(); + self.json_listener_task.abort(); + self.listener_task.abort(); + } +} + +impl Default for Server { + fn default() -> Self { + let (tx, rx) = unbounded_channel(); + let tx1 = tx.clone(); + let tx2 = tx.clone(); + let tx3 = tx.clone(); + let tx4 = tx.clone(); + + let os_event_manager_task = tokio::spawn(async move { + OSSignalManager::new(tx1).run().await; + }); + + let listener_task = ProtobufListener::start_run(tx2); + let json_listener_task = JSONListener::start_run(tx3); + + let mut connection_manager = ConnectionManager::new(tx4); + let connection_manager_sender = connection_manager.get_sender(); + let connection_manager_task = tokio::spawn(async move { + connection_manager.run().await; + }); + + let chat_manager = ChatManager::new(); + + Self { + chat_manager, + + os_event_manager_task, + connection_manager_task, + connection_manager_sender, + + json_listener_task, + receiver: Mutex::new(rx), + listener_task, + } + } +} + +/// # ServerMessage +/// enum describing all messages that the server can handle +pub enum ServerMessages { + Exit, + AddGlobalMessage(GlobalMessage), + SendGlobalMessages(Uuid), + NewConnection(ConnectionType), +} diff --git a/serverctl/Cargo.toml b/serverctl/Cargo.toml deleted file mode 100644 index 4293dee..0000000 --- a/serverctl/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "serverctl" -version = "0.1.0" -authors = ["michael-bailey "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/serverctl/src/main.rs b/serverctl/src/main.rs deleted file mode 100644 index a30eb95..0000000 --- a/serverctl/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -}