use bevy::prelude::*; use clap::Parser; use std::net::SocketAddr; use std::path::PathBuf; #[derive(Parser, Debug, Clone, Resource)] #[command(version, about, long_about = None)] pub struct Args { #[clap(long)] pub debug_match: bool, #[clap(long)] pub synctest: bool, #[clap(long, default_value_t = 2)] pub input_delay: usize, #[clap(long, default_value_t = 2)] pub check_distance: usize, #[clap(long, default_value_t = 1)] pub players: usize, /// UDP port to bind for a direct peer-to-peer session. #[clap(long, default_value_t = 6969)] pub local_port: u16, /// Player ownership declarations in the form HANDLE=local or HANDLE=ADDRESS. #[clap(long = "net-player", value_name = "HANDLE=local|ADDRESS")] pub net_players: Vec, /// Record a replay to this path. #[clap(long, conflicts_with = "play_replay")] pub record_replay: Option, /// Play a replay from this path. #[clap(long, conflicts_with = "record_replay")] pub play_replay: Option, /// Replay output format. JSON is useful for inspection; binary is compact. #[clap(long, value_enum, default_value_t = crate::replay::ReplayFormat::Binary)] pub replay_format: crate::replay::ReplayFormat, } pub fn parse_socket_addr(value: &str) -> Result { let address = value .parse::() .map_err(|_| format!("invalid socket address: {value}"))?; if address.port() == 0 { return Err(format!("socket address must use a nonzero port: {value}")); } Ok(address) } #[cfg(test)] mod tests { use super::*; #[test] fn parses_network_options() { let args = Args::try_parse_from([ "game", "--local-port", "7000", "--net-player", "0=local", "--net-player", "1=127.0.0.1:7001", ]) .unwrap(); assert_eq!(args.local_port, 7000); assert_eq!(args.net_players, ["0=local", "1=127.0.0.1:7001"]); } #[test] fn rejects_invalid_socket_addresses() { assert!(parse_socket_addr("127.0.0.1:0").is_err()); assert!(parse_socket_addr("not-an-address").is_err()); } }