diff --git a/.gitignore b/.gitignore index a6db884..713b207 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /.direnv *.pem *.p12 +/result diff --git a/Cargo.lock b/Cargo.lock index 6553e46..776fc2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,12 +32,55 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3aa2999eb46af81abb65c2d30d446778d7e613b60bbf4e174a027e80f90a3c14" +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.94" @@ -166,9 +209,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -193,9 +236,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.4" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" dependencies = [ "shlex", ] @@ -219,6 +262,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -227,11 +271,24 @@ version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ + "anstream", "anstyle", "clap_lex", "strsim", ] +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "clap_lex" version = "0.7.4" @@ -248,6 +305,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "core-foundation" version = "0.10.0" @@ -294,9 +357,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5a32d755fe20281b46118ee4b507233311fb7a48a0cfd42f554b93640521a2f" +checksum = "4d44ff199ff93242c3afe480ab588d544dd08d72e92885e152ffebc670f076ad" dependencies = [ "cc", "cxxbridge-cmd", @@ -308,9 +371,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11645536ada5d1c8804312cbffc9ab950f2216154de431de930da47ca6955199" +checksum = "66fd8f17ad454fc1e4f4ab83abffcc88a532e90350d3ffddcb73030220fcbd52" dependencies = [ "cc", "codespan-reporting", @@ -322,9 +385,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcc9c78e3c7289665aab921a2b394eaffe8bdb369aa18d81ffc0f534fd49385" +checksum = "4717c9c806a9e07fdcb34c84965a414ea40fafe57667187052cf1eb7f5e8a8a9" dependencies = [ "clap", "codespan-reporting", @@ -335,15 +398,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a22a87bd9e78d7204d793261470a4c9d585154fddd251828d8aefbb5f74c3bf" +checksum = "2f6515329bf3d98f4073101c7866ff2bec4e635a13acb82e3f3753fff0bf43cb" [[package]] name = "cxxbridge-macro" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dfdb020ff8787c5daf6e0dca743005cc8782868faeadfbabb8824ede5cb1c72" +checksum = "fb93e6a7ce8ec985c02bbb758237a31598b340acbbc3c19c5a4fa6adaaac92ab" dependencies = [ "proc-macro2", "quote", @@ -421,9 +484,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "form_urlencoded" @@ -518,6 +581,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "httlib-huffman" version = "0.3.4" @@ -673,6 +742,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itoa" version = "1.0.14" @@ -703,9 +778,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "link-cplusplus" @@ -751,9 +826,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", ] @@ -965,7 +1040,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.7", + "thiserror 2.0.8", "tokio", "tracing", ] @@ -984,7 +1059,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.7", + "thiserror 2.0.8", "tinyvec", "tracing", "web-time", @@ -992,9 +1067,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" dependencies = [ "cfg_aliases", "libc", @@ -1045,9 +1120,9 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54077e1872c46788540de1ea3d7f4ccb1983d12f9aa909b234468676c1a36779" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" dependencies = [ "pem", "ring", @@ -1251,9 +1326,9 @@ checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" [[package]] name = "security-framework" -version = "3.0.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" +checksum = "81d3f8c9bfcc3cbb6b0179eb57042d75b1582bdc65c3cb95f3fa999509c03cbc" dependencies = [ "bitflags", "core-foundation", @@ -1264,9 +1339,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", @@ -1367,6 +1442,7 @@ name = "smo-server" version = "0.1.0" dependencies = [ "anyhow", + "clap", "crc32fast", "glam", "heapless", @@ -1474,11 +1550,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.7" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767" +checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" dependencies = [ - "thiserror-impl 2.0.7", + "thiserror-impl 2.0.8", ] [[package]] @@ -1494,9 +1570,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.7" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36" +checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" dependencies = [ "proc-macro2", "quote", @@ -1556,9 +1632,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -1721,6 +1797,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.11.0" diff --git a/Cargo.toml b/Cargo.toml index fba54cd..c9700cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] anyhow = { version = "1.0.94", features = ["backtrace"] } +clap = { version = "4.5.23", features = ["derive"] } crc32fast = "1.4.2" glam = "0.29.2" heapless = "0.8.0" diff --git a/flake.nix b/flake.nix index f82ac7f..bd18b28 100644 --- a/flake.nix +++ b/flake.nix @@ -7,27 +7,46 @@ inputs.nixpkgs.follows = "nixpkgs"; }; }; - outputs = { self, nixpkgs, flake-utils, fenix }: + outputs = { + self, + nixpkgs, + flake-utils, + fenix, + }: flake-utils.lib.eachDefaultSystem - (system: - let - overlays = [ fenix.overlays.default ]; - pkgs = import nixpkgs { - inherit system overlays; - }; - in - with pkgs; - { + ( + system: let + overlays = [fenix.overlays.default]; + pkgs = import nixpkgs { + inherit system overlays; + }; + in + with pkgs; { devShells.default = mkShell { buildInputs = [ pkgs.fenix.stable.completeToolchain - cmake pkg-config openssl - alsa-lib - ffmpeg ]; }; + formatter = pkgs.alejandra; + packages.default = rustPlatform.buildRustPackage { + name = "smo-server"; + src = ./.; + nativeBuildInputs = [pkg-config cmake]; + buildInputs = [openssl]; + cargoDeps = rustPlatform.importCargoLock { + lockFile = ./Cargo.lock; + outputHashes = { + "newtype-enum-0.1.0" = "sha256-DBFSgtuwn51U3nvSxa0SQZKEHniC4UFVBHIeQjkXAvw="; + }; + }; + }; + # nixosModules.default = { config }: with pkgs.lib { + # options.services.smo-server = { + # enable = mkEnableOption "" + # }; + # } } - ); + ); } diff --git a/src/main.rs b/src/main.rs index 144e50b..6cd4017 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,10 +8,12 @@ pub mod server; use std::{ collections::HashMap, io::Cursor, - net::{SocketAddr, ToSocketAddrs}, - sync::{Arc, LazyLock}, + net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs}, + path::PathBuf, + sync::{Arc, LazyLock, OnceLock}, }; +use clap::Parser; use faker::Faker; use packet::{rw::read_packet, Packet}; use player::PlayerActor; @@ -31,13 +33,26 @@ pub fn clients() -> &'static RwLock> &CLIENTS } - +static MANAGER: OnceLock> = OnceLock::new(); pub fn manager() -> &'static Address { - static MANAGER: LazyLock> = LazyLock::new(web_main); - - &MANAGER + &MANAGER.get().unwrap() } +#[derive(clap::Parser)] +struct Arguments { + #[arg(short, long, default_value_t = 1027)] + tcp_port: u16, + #[arg(short, long, default_value_t = 1027)] + udp_port: u16, + #[arg(short, long, default_value_t = 4433)] + prox_port: u16, + #[arg(long, default_value = "./cert.pem")] + prox_cert: PathBuf, + #[arg(long, default_value = "./key.pem")] + prox_key: PathBuf, + #[arg(long)] + enable_faker: bool, +} #[tokio::main] async fn main() { @@ -46,9 +61,17 @@ async fn main() { *meta.level() < Level::INFO || meta.module_path().unwrap().contains("smo_server") }))) .init(); - let tcp = TcpListener::bind("0.0.0.0:1027").await.unwrap(); + let arguments = Arguments::parse(); - { + MANAGER.set(web_main(arguments.prox_port, arguments.prox_cert, arguments.prox_key).await).unwrap(); + + let tcp = TcpListener::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), arguments.tcp_port)) + .await + .expect("failed to bind tcp listener"); + + if arguments.enable_faker { + // spawn test bot + info!("spawning faker for testing!"); let (address, mailbox) = Mailbox::unbounded(); let address = xtra::spawn_tokio( Faker { @@ -60,35 +83,22 @@ async fn main() { 1, ( MessageChannel::new(address), - "0.0.0.0:1027".to_socket_addrs().unwrap().next().unwrap(), + "0.0.0.0:0".to_socket_addrs().unwrap().next().unwrap(), ), ); } - let socket = Arc::new(UdpSocket::bind("0.0.0.0:1027").await.unwrap()); - tokio::spawn({ - let socket = socket.clone(); - async move { - loop { - let mut packet = [0u8; 256]; - let (size, _addr) = socket.recv_from(&mut packet).await.unwrap(); - // trace!("got packet from {_addr:?}"); - match read_packet(&mut Cursor::new(&mut packet[..size]), true).await { - Ok(packet) => { - // info!("packet from udp {packet:?}"); - if let Some(client) = clients().read().await.get(&packet.user_id) { - let _ = client.0.send(packet).detach().await; - } - } - Err(error) => { - error!("udp packet error: {error:?}"); - } - }; - } - } - }); + let socket = Arc::new( + UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), arguments.udp_port)) + .await + .expect("failed to bind udp socket"), + ); + tokio::spawn(udp_server(socket.clone())); - info!("listening on port 1027"); + info!( + "listening on tcp port {}, udp port {}", + arguments.tcp_port, arguments.udp_port + ); loop { match tcp.accept().await { Ok((stream, addr)) => { @@ -100,3 +110,22 @@ async fn main() { } } } + +async fn udp_server(socket: Arc) { + loop { + let mut packet = [0u8; 256]; + let (size, _addr) = socket.recv_from(&mut packet).await.unwrap(); + // trace!("got packet from {_addr:?}"); + match read_packet(&mut Cursor::new(&mut packet[..size]), true).await { + Ok(packet) => { + // info!("packet from udp {packet:?}"); + if let Some(client) = clients().read().await.get(&packet.user_id) { + let _ = client.0.send(packet).detach().await; + } + } + Err(error) => { + error!("udp packet error: {error:?}"); + } + }; + } +} diff --git a/src/player.rs b/src/player.rs index 009ecfb..cdb6766 100644 --- a/src/player.rs +++ b/src/player.rs @@ -69,6 +69,7 @@ pub struct PlayerActor { connection_kind: ConnectionKind, name: String, write_sender: mpsc::UnboundedSender, + udp_port: u16, } enum WriteMessage { @@ -98,6 +99,8 @@ impl PlayerActor { .await .expect("msfrarausfhsdagsdgkog"); + let udp_port = socket.local_addr().unwrap().port(); + let span = info_span!("Client", player = connect.client_name.to_string()); let (address, mailbox) = Mailbox::bounded(512); let (sender, mut receiver) = mpsc::unbounded_channel(); @@ -138,7 +141,7 @@ impl PlayerActor { } } WriteMessage::SetUdp(port) => { - trace!("set udp port, connected on udp!"); + trace!("now connected on udp!"); addr = Some(SocketAddr::new(ip, port)) } } @@ -177,6 +180,7 @@ impl PlayerActor { connection_kind: connect.kind, name: connect.client_name, write_sender: sender, + udp_port }, ) .instrument(span) @@ -204,7 +208,7 @@ impl Actor for PlayerActor { .send(WriteMessage::Data(Packet { user_id: 0, udp: false, - data: PacketData::UdpInit(UdpInit { port: 1027 }), + data: PacketData::UdpInit(UdpInit { port: self.udp_port }), })) .map_err(drop)?; @@ -226,14 +230,12 @@ impl Handler for PlayerActor { async fn handle(&mut self, packet: Packet, _: &mut xtra::Context) { if matches!(packet.data, PacketData::HolePunch(..)) { - trace!("hole puncher"); return; } if packet.user_id == self.id { match packet.data { PacketData::UdpInit(UdpInit { port }) => { - trace!("hole puncher"); let _ = self.write_sender.send(WriteMessage::SetUdp(port)); let _ = self.write_sender.send(WriteMessage::Data(Packet { user_id: 0, diff --git a/src/server/mod.rs b/src/server/mod.rs index 52a09c3..76f6874 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,10 +1,10 @@ mod prox; -use std::{collections::HashMap, sync::LazyLock}; +use std::{collections::HashMap, path::PathBuf, sync::LazyLock}; use prox::{packet::HYP_UUID_SIZE, ProximityPlayer}; use tokio::sync::RwLock; -use tracing::{error, info_span, Instrument}; +use tracing::{info, info_span, Instrument}; use wtransport::{Endpoint, Identity, ServerConfig}; use xtra::{Actor, Address, Handler, Mailbox}; use zerocopy::{FromZeros, Immutable, IntoBytes}; @@ -22,8 +22,16 @@ fn listeners() -> &'static RwLock>> &LISTENERS } -pub fn web_main() -> Address { - let span = info_span!("wt"); +pub async fn web_main( + port: u16, + cert: PathBuf, + key: PathBuf, +) -> Address { + let span = info_span!("prox"); + info!(parent: &span, "reading pems from {{ cert: {cert:?}, key: {key:?} }}"); + let identity = Identity::load_pemfiles(cert, key).await.expect("failed to create identity from proximity pems"); + let config = ServerConfig::builder().with_bind_default(port).with_identity(identity).build(); + let endpoint = Endpoint::server(config).expect("failed to build proximity endpoint"); let manager = xtra::spawn_tokio( Manager { @@ -35,8 +43,10 @@ pub fn web_main() -> Address { tokio::spawn({ let manager = manager.clone(); async move { - if let Err(result) = webtransport_server(manager).await { - error!("{:?}", result); + loop { + let connection = endpoint.accept().await; + + ProximityPlayer::spawn(connection, manager.clone()); } } .instrument(span) @@ -44,21 +54,6 @@ pub fn web_main() -> Address { manager } -async fn webtransport_server(manager: Address) -> anyhow::Result<()> { - // let identity = Identity::self_signed(["localhost", "127.0.0.1", "::1"]).unwrap(); - let identity = Identity::load_pemfiles("./cert.pem", "./key.pem").await.unwrap(); - - let config = ServerConfig::builder().with_bind_default(4433).with_identity(identity).build(); - - let endpoint = Endpoint::server(config)?; - - loop { - let connection = endpoint.accept().await; - - ProximityPlayer::spawn(connection, manager.clone()); - } -} - #[derive(Actor)] pub struct Manager { players: HashMap,