diff --git a/desktop/Cargo.lock b/desktop/Cargo.lock index 980d814..954969a 100644 --- a/desktop/Cargo.lock +++ b/desktop/Cargo.lock @@ -273,6 +273,7 @@ dependencies = [ "axum", "axum-core", "bytes", + "cookie", "futures-util", "headers", "http", @@ -677,6 +678,7 @@ dependencies = [ "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] @@ -764,6 +766,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.10.0" @@ -4560,6 +4573,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +dependencies = [ + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thin-slice" version = "0.1.1" @@ -4973,7 +4996,6 @@ name = "twwe-desktop" version = "0.1.0" dependencies = [ "env_logger 0.10.2", - "platform-dirs", "serde", "serde_json", "tauri", @@ -5001,9 +5023,11 @@ dependencies = [ "futures-util", "image", "itertools 0.11.0", + "lazy_static", "log", "ndarray", "parking_lot", + "platform-dirs", "rand 0.8.5", "regex", "sanitize-filename", diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index 8895b0e..75871ac 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -19,7 +19,6 @@ twwe-server = { path = "../server", features = ["bridge_out"] } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } tokio = "1.32.0" -platform-dirs = "0.3.0" env_logger = "0.10.2" tauri = { version = "2", features = [] } tauri-plugin-fs = "2" diff --git a/desktop/src/main.rs b/desktop/src/main.rs index 710cb2f..c64e6e3 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -3,43 +3,7 @@ use std::{collections::BTreeSet, path::PathBuf, sync::Arc}; -use platform_dirs::AppDirs; - -fn get_data_dirs() -> Vec { - // like ddnet's storage.cfg, the last path has the highest priority. - let mut data_dirs = BTreeSet::new(); - - // ddnet's $USERDIR - if let Some(dirs) = AppDirs::new(Some("ddnet"), false) { - data_dirs.insert(dirs.config_dir); - data_dirs.insert(dirs.data_dir); - } - // ddnet's $DATADIR - let known_ddnets = [ - "/usr/share/ddnet/data", - "/usr/share/games/ddnet/data", - "/usr/local/share/ddnet/data", - "/usr/local/share/games/ddnet/data", - "/usr/pkg/share/ddnet/data", - "/usr/pkg/share/games/ddnet/data", - "/opt/ddnet/data", - ]; - known_ddnets.iter().for_each(|str| { - let path = PathBuf::from(str); - data_dirs.insert(path); - }); - // ddnet's $CURRENTDIR - if let Ok(dir) = std::env::current_dir() { - data_dirs.insert(dir.join("data")); - data_dirs.insert(dir); - } - - let maps_dirs = data_dirs - .into_iter() - .filter(|path| path.join("maps").is_dir()) - .collect(); - maps_dirs -} +use twwe_server::find_data_dirs; #[tokio::main] async fn server_main() { @@ -47,7 +11,7 @@ async fn server_main() { addr: "127.0.0.1:16800".to_string(), cert: None, key: None, - data_dirs: get_data_dirs(), + data_dirs: find_data_dirs(), maps_dirs: vec![], static_dir: None, rpp_path: None, diff --git a/server/Cargo.lock b/server/Cargo.lock index dc44e8d..2d654b4 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -492,6 +492,7 @@ dependencies = [ "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] @@ -683,6 +684,27 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs-next" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf36e65a80337bea855cd4ef9b8401ffce06a7baedf2e85ec467b1ac3f6e82b6" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dunce" version = "1.0.5" @@ -1346,6 +1368,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + [[package]] name = "libz-sys" version = "1.1.20" @@ -1648,6 +1680,15 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "platform-dirs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e188d043c1a692985f78b5464853a263f1a27e5bd6322bad3a4078ee3c998a38" +dependencies = [ + "dirs-next", +] + [[package]] name = "png" version = "0.17.14" @@ -1808,6 +1849,17 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.11.0" @@ -2187,6 +2239,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "terminal_size" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +dependencies = [ + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "1.0.64" @@ -2509,6 +2571,7 @@ dependencies = [ "log", "ndarray", "parking_lot", + "platform-dirs", "rand", "regex", "sanitize-filename", @@ -2745,6 +2808,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" diff --git a/server/Cargo.toml b/server/Cargo.toml index 748f6c8..dc20b62 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -22,7 +22,7 @@ twmap = "0.12" env_logger = "0.11" log = "0.4" parking_lot = "0.12" -clap = { version = "4.5", features = ["derive"] } +clap = { version = "4.5", features = ["derive", "wrap_help"] } regex = "1.6" axum = { version = "0.7", features = ["tokio", "multipart", "ws"] } axum-server = { version = "0.7", features = ["tls-rustls"] } @@ -44,12 +44,12 @@ tower_governor = { version = "0.4.3", features = ["axum"] } tokio-tungstenite = { version = "0.24.0", features = ["rustls-tls-webpki-roots"] } bcrypt = "0.15.1" lazy_static = "1.5.0" +platform-dirs = "0.3.0" [lib] [features] -default = [] -bridge = [] -bridge_out = ["bridge"] -bridge_in = ["bridge"] +default = ["bridge_in"] +bridge_out = [] +bridge_in = [] diff --git a/server/src/bridge_in.rs b/server/src/bridge_in.rs new file mode 100644 index 0000000..54bcb72 --- /dev/null +++ b/server/src/bridge_in.rs @@ -0,0 +1,180 @@ +use std::{collections::HashMap, net::SocketAddr}; + +use axum::extract::ws::{Message as WebSocketMessage, WebSocket}; +use futures::{ + channel::mpsc::{unbounded, UnboundedSender}, + StreamExt, TryStreamExt, +}; +use futures_util::future::Either; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; + +use crate::{error::Error, protocol::*, server::Server, util::*}; + +type Tx = UnboundedSender; + +pub struct Bridge { + pub key: String, + pub map: String, + pub server_tx: Tx, + pub users_tx: HashMap, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct BridgeConfig { + pub map: String, + pub key: String, + pub url: String, +} + +impl Server { + /// A client is connecting to a remote server via the bridge. + pub async fn handle_client_bridge( + &self, + socket: WebSocket, + addr: SocketAddr, + key: String, + ) -> Result<(), Error> { + let (tx, ws_recv) = socket.split(); + + let (ws_send, rx) = unbounded(); + let fut_send = rx.map(Ok).forward(tx); + + let bridge_addr = { + let mut bridges = self.remote_bridges.write().unwrap(); + let (bridge_addr, bridge) = bridges + .iter_mut() + .find(|(_, v)| v.key == key) + .ok_or(Error::BridgeNotFound)?; + + bridge.users_tx.insert(addr, ws_send); + bridge_addr.clone() + }; + + log::info!("client {addr} connected to remote server {bridge_addr}"); + + let addr_msg = WebSocketMessage::Text(serde_json::to_string(&addr).unwrap()); + + let fut_recv = ws_recv + .map_err(|_| Error::BridgeClosed) + .try_for_each(|msg| { + match msg { + WebSocketMessage::Text(msg) => { + let res = || -> Option<()> { + let bridges = self.remote_bridges.read().unwrap(); + let bridge = bridges.get(&bridge_addr)?; + + // forward client requests to the remote with the client addr. + bridge.server_tx.unbounded_send(addr_msg.clone()).ok(); + bridge + .server_tx + .unbounded_send(WebSocketMessage::Text(msg)) + .ok(); + + Some(()) + }(); + futures::future::ready(res.ok_or(Error::BridgeFailure)) + } + WebSocketMessage::Close(_) => futures::future::err(Error::BridgeClosed), + WebSocketMessage::Binary(_) + | WebSocketMessage::Ping(_) + | WebSocketMessage::Pong(_) => futures::future::ok(()), + } + }); + + // wait for either sender or receiver to complete: this means the connection is closed. + let res = match futures::future::select(fut_send, fut_recv).await { + Either::Left((Ok(_), _)) => Ok(()), + Either::Left((Err(_), _)) => Err(Error::BridgeClosed), + Either::Right((res, _)) => res, + }; + + // remove the user, send logout + || -> Option<()> { + let mut bridges = self.remote_bridges.write().unwrap(); + let bridge = bridges.get_mut(&bridge_addr)?; + + let packet = SendPacket { + timestamp: timestamp_now(), + id: None, + content: Message::Request(Request::LeaveMap(bridge.map.clone())), + }; + let logout_msg = serde_json::to_string(&packet).unwrap(); + bridge.server_tx.unbounded_send(addr_msg.clone()).ok(); + bridge + .server_tx + .unbounded_send(WebSocketMessage::Text(logout_msg)) + .ok(); + + bridge.users_tx.remove(&addr); + Some(()) + }(); + + res + } + + /// a remote server is opening a bridge with this server + pub async fn handle_server_bridge( + &self, + socket: WebSocket, + addr: SocketAddr, + ) -> Result<(), Error> { + let (tx, mut ws_recv) = socket.split(); + + let cfg = if let Some(Ok(WebSocketMessage::Text(txt))) = ws_recv.next().await { + serde_json::from_str::(&txt).map_err(|_| Error::BridgeFailure) + } else { + Err(Error::BridgeFailure) + }?; + + let (ws_send, rx) = unbounded(); + let fut_send = rx.map(Ok).forward(tx); + + let bridge = Bridge { + key: cfg.key, + map: cfg.map, + server_tx: ws_send, + users_tx: Default::default(), + }; + + self.remote_bridges.write().unwrap().insert(addr, bridge); + + log::info!("remote server {addr} started bridging"); + + // forward all messages to the right user + let fut_recv = ws_recv + .try_chunks(2) + .map_err(|_| Error::BridgeClosed) + .try_for_each(|v| { + let (addr_msg, payload_msg) = match v.into_iter().collect_tuple() { + Some((WebSocketMessage::Text(m1), WebSocketMessage::Text(m2))) => (m1, m2), + _ => return futures::future::err(Error::BridgeClosed), + }; + let res = move || -> Option<()> { + let user_addr: SocketAddr = serde_json::from_str(&addr_msg).ok()?; + self.remote_bridges + .read() + .unwrap() + .get(&addr)? + .users_tx + .get(&user_addr) + .map(|tx| { + tx.unbounded_send(WebSocketMessage::Text(payload_msg)).ok(); + }); + Some(()) + }(); + futures::future::ready(res.ok_or(Error::BridgeFailure)) + }); + + // wait for either sender or receiver to complete: this means the connection is closed. + let res = match futures::future::select(fut_send, fut_recv).await { + Either::Left((Ok(_), _)) => Ok(()), + Either::Left((Err(_), _)) => Err(Error::BridgeClosed), + Either::Right((res, _)) => res, + }; + + self.remote_bridges.write().unwrap().remove(&addr); + + res + } +} diff --git a/server/src/bridge.rs b/server/src/bridge_out.rs similarity index 52% rename from server/src/bridge.rs rename to server/src/bridge_out.rs index 7eba6c3..65ceecb 100644 --- a/server/src/bridge.rs +++ b/server/src/bridge_out.rs @@ -1,29 +1,21 @@ -use std::{collections::HashMap, net::SocketAddr, sync::Arc}; +use std::{collections::HashMap, sync::Arc}; -use axum::extract::ws::{Message as WebSocketMessage, WebSocket}; -use futures::{ - channel::mpsc::{unbounded, UnboundedSender}, - SinkExt, StreamExt, TryStreamExt, +use axum::{ + extract::{ws::Message as WebSocketMessage, State}, + response::IntoResponse, + Json, }; +use futures::{channel::mpsc::unbounded, SinkExt, StreamExt, TryStreamExt}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use tokio_tungstenite::tungstenite::protocol::{frame::coding::CloseCode, CloseFrame}; use tokio_tungstenite::tungstenite::Message as TungsteniteMessage; -use crate::{error::Error, protocol::*, server::Server, server::User, util::*}; +use crate::{error::Error, protocol::*, server::Server, server::User}; use futures_util::{future::Either, stream}; use tokio_tungstenite::connect_async; -type Tx = UnboundedSender; - -pub struct Bridge { - pub key: String, - pub map: String, - pub server_tx: Tx, - pub users_tx: HashMap, -} - #[derive(Clone, Debug, Serialize, Deserialize)] pub struct BridgeConfig { pub map: String, @@ -32,156 +24,6 @@ pub struct BridgeConfig { } impl Server { - /// A client is connecting to a remote server via the bridge. - pub async fn handle_client_bridge( - &self, - socket: WebSocket, - addr: SocketAddr, - key: String, - ) -> Result<(), Error> { - let (tx, ws_recv) = socket.split(); - - let (ws_send, rx) = unbounded(); - let fut_send = rx.map(Ok).forward(tx); - - let bridge_addr = { - let mut bridges = self.remote_bridges.write().unwrap(); - let (bridge_addr, bridge) = bridges - .iter_mut() - .find(|(_, v)| v.key == key) - .ok_or(Error::BridgeNotFound)?; - - bridge.users_tx.insert(addr, ws_send); - bridge_addr.clone() - }; - - log::info!("client {addr} connected to remote server {bridge_addr}"); - - let addr_msg = WebSocketMessage::Text(serde_json::to_string(&addr).unwrap()); - - let fut_recv = ws_recv - .map_err(|_| Error::BridgeClosed) - .try_for_each(|msg| { - match msg { - WebSocketMessage::Text(msg) => { - let res = || -> Option<()> { - let bridges = self.remote_bridges.read().unwrap(); - let bridge = bridges.get(&bridge_addr)?; - - // forward client requests to the remote with the client addr. - bridge.server_tx.unbounded_send(addr_msg.clone()).ok(); - bridge - .server_tx - .unbounded_send(WebSocketMessage::Text(msg)) - .ok(); - - Some(()) - }(); - futures::future::ready(res.ok_or(Error::BridgeFailure)) - } - WebSocketMessage::Close(_) => futures::future::err(Error::BridgeClosed), - WebSocketMessage::Binary(_) - | WebSocketMessage::Ping(_) - | WebSocketMessage::Pong(_) => futures::future::ok(()), - } - }); - - // wait for either sender or receiver to complete: this means the connection is closed. - let res = match futures::future::select(fut_send, fut_recv).await { - Either::Left((Ok(_), _)) => Ok(()), - Either::Left((Err(_), _)) => Err(Error::BridgeClosed), - Either::Right((res, _)) => res, - }; - - // remove the user, send logout - || -> Option<()> { - let mut bridges = self.remote_bridges.write().unwrap(); - let bridge = bridges.get_mut(&bridge_addr)?; - - let packet = SendPacket { - timestamp: timestamp_now(), - id: None, - content: Message::Request(Request::LeaveMap(bridge.map.clone())), - }; - let logout_msg = serde_json::to_string(&packet).unwrap(); - bridge.server_tx.unbounded_send(addr_msg.clone()).ok(); - bridge - .server_tx - .unbounded_send(WebSocketMessage::Text(logout_msg)) - .ok(); - - bridge.users_tx.remove(&addr); - Some(()) - }(); - - res - } - - /// a remote server is opening a bridge with this server - pub async fn handle_server_bridge( - &self, - socket: WebSocket, - addr: SocketAddr, - ) -> Result<(), Error> { - let (tx, mut ws_recv) = socket.split(); - - let cfg = if let Some(Ok(WebSocketMessage::Text(txt))) = ws_recv.next().await { - serde_json::from_str::(&txt).map_err(|_| Error::BridgeFailure) - } else { - Err(Error::BridgeFailure) - }?; - - let (ws_send, rx) = unbounded(); - let fut_send = rx.map(Ok).forward(tx); - - let bridge = Bridge { - key: cfg.key, - map: cfg.map, - server_tx: ws_send, - users_tx: Default::default(), - }; - - self.remote_bridges.write().unwrap().insert(addr, bridge); - - log::info!("remote server {addr} started bridging"); - - // forward all messages to the right user - let fut_recv = ws_recv - .try_chunks(2) - .map_err(|_| Error::BridgeClosed) - .try_for_each(|v| { - let (addr_msg, payload_msg) = match v.into_iter().collect_tuple() { - Some((WebSocketMessage::Text(m1), WebSocketMessage::Text(m2))) => (m1, m2), - _ => return futures::future::err(Error::BridgeClosed), - }; - let res = move || -> Option<()> { - let user_addr: SocketAddr = serde_json::from_str(&addr_msg).ok()?; - self.remote_bridges - .read() - .unwrap() - .get(&addr)? - .users_tx - .get(&user_addr) - .map(|tx| { - tx.unbounded_send(WebSocketMessage::Text(payload_msg)).ok(); - }); - Some(()) - }(); - futures::future::ready(res.ok_or(Error::BridgeFailure)) - }); - - // wait for either sender or receiver to complete: this means the connection is closed. - let res = match futures::future::select(fut_send, fut_recv).await { - Either::Left((Ok(_), _)) => Ok(()), - Either::Left((Err(_), _)) => Err(Error::BridgeClosed), - Either::Right((res, _)) => res, - }; - - self.remote_bridges.write().unwrap().remove(&addr); - - res - } - /// Open a bridge with a remote server pub async fn open_bridge(server: Arc, cfg: BridgeConfig) -> Result<(), Error> { server.close_bridge(); @@ -317,3 +159,14 @@ impl Server { } } } + +pub(crate) async fn route_open_bridge( + State(server): State>, + Json(cfg): Json, +) -> impl IntoResponse { + Server::open_bridge(server, cfg).await +} + +pub(crate) async fn route_close_bridge(State(server): State>) -> impl IntoResponse { + server.close_bridge(); +} diff --git a/server/src/bridge_router.rs b/server/src/bridge_router.rs index b537e1a..a1d70c1 100644 --- a/server/src/bridge_router.rs +++ b/server/src/bridge_router.rs @@ -15,7 +15,6 @@ use futures_util::StreamExt; use crate::Server; use crate::{ - bridge::BridgeConfig, error::Error, protocol::{self, *}, util::timestamp_now, @@ -99,17 +98,6 @@ pub(crate) async fn route_bridge_list_maps( } } -pub(crate) async fn route_open_bridge( - State(server): State>, - Json(cfg): Json, -) -> impl IntoResponse { - Server::open_bridge(server, cfg).await -} - -pub(crate) async fn route_close_bridge(State(server): State>) -> impl IntoResponse { - server.close_bridge(); -} - pub(crate) async fn route_server_bridge( State(server): State>, ws: WebSocketUpgrade, diff --git a/server/src/cli.rs b/server/src/cli.rs index bdcbc93..5fb2b7e 100644 --- a/server/src/cli.rs +++ b/server/src/cli.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use clap::Parser; -#[derive(Parser)] +#[derive(Debug, Parser)] #[clap(name = "TWWE Server")] #[clap(author = "Mathis Brossier ")] #[clap(version = "0.1")] @@ -28,6 +28,10 @@ pub struct Cli { /// Map will be read in the maps sub-directory, automappers in editor/automap, map /// config is volatile for now. Automappers will be shared between all maps in the /// same data directory. + /// + /// New maps will be created in the first directory provided. + /// + /// If both `--maps` and `--data` are unset, the server will look for the default DDNet data directories. #[arg(name = "data", long)] pub data_dirs: Vec, diff --git a/server/src/lib.rs b/server/src/lib.rs index 437842d..a96b602 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1,7 +1,8 @@ -use std::sync::Arc; +use platform_dirs::AppDirs; +use std::{collections::BTreeSet, path::PathBuf, sync::Arc}; use cli::Cli; - +use room::Room; use server::Server; mod base64; @@ -17,12 +18,48 @@ mod twmap_map_checks; mod twmap_map_edit; mod util; -#[cfg(feature = "bridge")] -mod bridge; -#[cfg(feature = "bridge")] +#[cfg(feature = "bridge_in")] +mod bridge_in; +#[cfg(feature = "bridge_out")] +mod bridge_out; +#[cfg(feature = "bridge_in")] mod bridge_router; -use room::Room; +pub fn find_data_dirs() -> Vec { + // like ddnet's storage.cfg, the last path has the highest priority. + let mut data_dirs = BTreeSet::new(); + + // ddnet's $USERDIR + if let Some(dirs) = AppDirs::new(Some("ddnet"), false) { + data_dirs.insert(dirs.config_dir); + data_dirs.insert(dirs.data_dir); + } + // ddnet's $DATADIR + let known_ddnets = [ + "/usr/share/ddnet/data", + "/usr/share/games/ddnet/data", + "/usr/local/share/ddnet/data", + "/usr/local/share/games/ddnet/data", + "/usr/pkg/share/ddnet/data", + "/usr/pkg/share/games/ddnet/data", + "/opt/ddnet/data", + ]; + known_ddnets.iter().for_each(|str| { + let path = PathBuf::from(str); + data_dirs.insert(path); + }); + // ddnet's $CURRENTDIR + if let Ok(dir) = std::env::current_dir() { + data_dirs.insert(dir.join("data")); + data_dirs.insert(dir); + } + + let maps_dirs = data_dirs + .into_iter() + .filter(|path| path.join("maps").is_dir()) + .collect(); + maps_dirs +} pub fn create_server(cli: &Cli) -> std::io::Result { let server = Server::new(cli); @@ -75,6 +112,7 @@ pub fn create_server(cli: &Cli) -> std::io::Result { } } let rooms = server.rooms().len(); + log::debug!("server config: {cli:#?}"); log::info!("found {rooms} maps"); if rooms > server.max_maps { log::warn!("there are more maps than is allowed by --max-maps"); diff --git a/server/src/main.rs b/server/src/main.rs index f445afe..0570ff2 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -2,13 +2,12 @@ use std::sync::Arc; use clap::Parser; -use twwe_server::{cli::Cli, create_server, router::Router}; +use twwe_server::{cli::Cli, create_server, find_data_dirs, router::Router}; #[tokio::main] -async fn run_server(args: Cli) { +async fn run_server(mut args: Cli) { if args.maps_dirs.is_empty() && args.data_dirs.is_empty() { - log::error!("you should provide a path to your maps. Either with the --maps or --data ."); - panic!("missing argument --maps or --data"); + args.data_dirs = find_data_dirs(); } let server = Arc::new(create_server(&args).expect("failed to create server")); diff --git a/server/src/router.rs b/server/src/router.rs index 3d0e63e..be76907 100644 --- a/server/src/router.rs +++ b/server/src/router.rs @@ -93,21 +93,21 @@ impl Router { let mut router = http_routes; router = router.route("/ws", get(route_websocket)); - #[cfg(feature = "bridge")] + #[cfg(feature = "bridge_in")] { use crate::bridge_router::*; - if cfg!(feature = "bridge_in") { - router = router - .route("/ws/bridge", get(route_server_bridge)) - .route("/bridge/:key/ws", get(route_client_bridge)) - .route("/bridge/:key/maps/:map", get(route_bridge_get_map)) // TODO: add the other bridge http routes - .route("/bridge/:key/maps", get(route_bridge_list_maps)); - } - if cfg!(feature = "bridge_out") { - router = router - .route("/bridge_open", post(route_open_bridge)) - .route("/bridge_close", get(route_close_bridge)) - } + router = router + .route("/ws/bridge", get(route_server_bridge)) + .route("/bridge/:key/ws", get(route_client_bridge)) + .route("/bridge/:key/maps/:map", get(route_bridge_get_map)) // TODO: add the other bridge http routes + .route("/bridge/:key/maps", get(route_bridge_list_maps)); + } + #[cfg(feature = "bridge_out")] + { + use crate::bridge_out::*; + router = router + .route("/bridge_open", post(route_open_bridge)) + .route("/bridge_close", get(route_close_bridge)); } let mut router = router diff --git a/server/src/server.rs b/server/src/server.rs index 2cada44..89f3166 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -25,13 +25,13 @@ use crate::{ util::{macros::apply_partial, *}, }; -#[cfg(feature = "bridge")] -use crate::bridge::Bridge; -#[cfg(feature = "bridge")] +#[cfg(feature = "bridge_in")] +use crate::bridge_in::Bridge; +#[cfg(feature = "bridge_in")] use std::net::SocketAddr; -#[cfg(feature = "bridge")] +#[cfg(feature = "bridge_in")] use std::sync::RwLock; -#[cfg(feature = "bridge")] +#[cfg(feature = "bridge_out")] use tokio::task::JoinHandle; type Tx = UnboundedSender; @@ -88,9 +88,9 @@ impl Server { max_maps: cli.max_maps, max_map_size: cli.max_map_size * 1024, max_users: cli.max_connections, - #[cfg(feature = "bridge")] + #[cfg(feature = "bridge_out")] bridge: Default::default(), - #[cfg(feature = "bridge")] + #[cfg(feature = "bridge_in")] remote_bridges: Default::default(), } }