use std::{ collections::HashSet, net::{TcpListener, TcpStream}, path::{Path, PathBuf}, sync::{ Arc, Mutex, atomic::{AtomicBool, Ordering}, }, }; const WELCOME: &str = " $$\\ $$\\ $$$$$$\\ $$\\ $$\\ $$$$$$\\ $$ | $$ |$$ __$$\\ $$ | $$ |$$ __$$\\ $$ | $$ |$$ / $$ |\\$$\\ $$ |$$ / $$ | \\$$\\ $$ |$$ | $$ | \\$$$$ / $$$$$$$$ | \\$$\\$$ / $$ | $$ | $$ $$< $$ __$$ | \\$$$ / $$ | $$ |$$ /\\$$\\ $$ | $$ | \\$ / $$$$$$ |$$ / $$ |$$ | $$ | \\_/ \\______/ \\__| \\__|\\__| \\__| "; use crate::{ cli, logger, plugin::{Plugin, loader::PluginLoader, types::LoaderMessage}, types::{self, message::WsMessage}, utils::{self, auth, client::Client, voice::Voice}, }; #[derive(serde::Serialize, serde::Deserialize)] pub struct ServerConfig { pub server_name: String, pub server_id: String, pub server_key: String, pub port: u16, pub channels: Vec, } pub struct Server { pub root: PathBuf, pub config: ServerConfig, pub clients: Mutex>, pub plugins: Mutex>, pub db: utils::database::Database, pub shutting_down: AtomicBool, pub indicators: Mutex>, pub voice: Mutex, } impl Default for ServerConfig { fn default() -> Self { Self { port: 7080, server_name: format!("Server Name"), server_id: format!("important"), server_key: format!("important"), channels: Vec::new(), } } } impl ServerConfig { pub fn build(self, root: &Path) -> Arc { Server::new_config(root, self) } pub fn from_str(s: &str) -> std::result::Result { serde_json::from_str(s) } } impl Server { logger!(LOGGER "Server"); pub fn new_config(root: &Path, config: ServerConfig) -> Arc { Arc::new(Self { db: utils::database::Database::new(&config).unwrap(), root: root.to_path_buf(), config, clients: Mutex::new(HashSet::new()), plugins: Mutex::new(Vec::new()), shutting_down: AtomicBool::new(false), indicators: Mutex::new(Vec::new()), voice: Mutex::new(Voice::new()), }) } pub fn run(self: &Arc) -> crate::Result<()> { // Start plugin loader let plugin_loader = PluginLoader::new(); Self::LOGGER.info("Starting loader"); plugin_loader.start_server(); // Load plugins plugin_loader.load_all(self); // Initialize plugins Self::LOGGER.info("Initializing plugins"); Self::LOGGER.info("Authenticating"); auth::test(self)?; // Initialize indicators Self::LOGGER.info("Initializing indicators"); self.spawn_indicator_thread(); // Initialize CLI Self::LOGGER.info("Initializing CLI"); cli::start_cli(self.clone(), plugin_loader); // Start server let listener = TcpListener::bind(format!("0.0.0.0:{}", self.config.port))?; Self::LOGGER.info(format!("Server listening at 0.0.0.0:{}", self.config.port)); println!( "{WELCOME}\nversion {}\nType 'help' to see available commands.", env!("CARGO_PKG_VERSION") ); for stream in listener.incoming() { match stream { Ok(stream) => { std::thread::spawn({ let srv = self.clone(); move || { let Some(client) = Self::LOGGER .extract(srv.init_client(stream), "Failed to initialize client") else { return; }; Self::LOGGER.extract( srv.wrap_err(&client, srv.handle_client(&client)), "Client handler failed", ); } }); } Err(e) => { Self::LOGGER.error(format!("Connection failed: {e}")); } } } Ok(()) } fn init_client(self: &Arc, stream: TcpStream) -> crate::Result { Self::LOGGER.info(format!("New connection: {}", stream.peer_addr()?)); // Initialize client let mut client = Client::new(stream)?; // Initialize handshake self.wrap_err( &client, client.send(types::handshake::ServerDetails { name: self.config.server_name.clone(), id: self.config.server_id.clone(), version: format!("0.0.1"), channels: self.config.channels.clone(), }), )?; match self.wrap_err(&client, client.read_t::())? { Some(types::message::WsMessage::Message(types::handshake::ClientDetails { auth_token, .. })) => { let auth_res = utils::auth::auth(self, &mut client, &auth_token); let uuid = self.wrap_err(&client, auth_res)?; self.wrap_err( &client, client.send(types::message::ServerMessage::Authenticated { uuid, indicators: self.indicators.lock().unwrap().clone(), voice_chat: self.voice.lock().unwrap().get_connections(), }), )?; } Some(v) => { self.wrap_err( &client, client.send(types::message::ResponseError::InvalidHandshake(format!( "Invalid handshake: {v:?}" ))), )?; } None => {} } // Insert to the set of all connected clients self.clients.lock().unwrap().insert(client.clone()); Ok(client) } fn handle_client(self: &Arc, client: &Client) -> crate::Result<()> { // The main req/res loop while !self.shutting_down.load(Ordering::SeqCst) { let req = client.read()?; if let Some(r) = &req { match r { WsMessage::Binary(_) => { // ignore binary } _ => { self.send_plugin_message(&LoaderMessage::Request { user_id: client.get_uuid().unwrap_or_default(), msg: r.clone(), })?; } } self.wrap_err(&client, self.call_request(r, &client))?; } } Ok(()) } /// When there is a error it removes the client pub fn wrap_err( self: &Arc, client: &Client, res: std::result::Result, ) -> std::result::Result { if let Err(e) = &res { self.clients.lock().unwrap().remove(&client); if client .send(types::message::ResponseError::InternalError(e.to_string())) .is_err() {} } res } pub fn send_plugin_message(self: &Arc, msg: &LoaderMessage) -> crate::Result<()> { for p in self.plugins.lock().unwrap().iter_mut() { p.send(msg)?; } Ok(()) } pub fn shutdown(self: &Arc) { Self::LOGGER.info("Server shutting down..."); // Signal shutdown self.shutting_down.store(true, Ordering::SeqCst); // Disconnect clients let clients = self.clients.lock().unwrap(); for client in clients.iter() { let _ = client.send(types::message::ServerMessage::Shutdown { message: format!("Server shutting down... we'll be back shortly"), }); let _ = client.close(); } // Stop plugins for plugin in self.plugins.lock().unwrap().iter_mut() { if let Err(e) = plugin.stop() { Self::LOGGER.warn(e.context("Couldn't stop plugin")); } } Self::LOGGER.info("Shutdown complete"); Self::LOGGER.info("Exiting process.."); std::process::exit(0); } }