use std::io::Read;
use bevy::log::tracing_subscriber;
use bevy::prelude::*;
use clap::Parser;
use starkingdoms::client_plugins::ClientPluginGroup;
#[cfg(not(target_arch = "wasm32"))]
use starkingdoms::server_plugins::ServerPluginGroup;
use starkingdoms::shared_plugins::SharedPluginGroup;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::process::exit;
use std::str::FromStr;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::filter::Directive;
use tracing_subscriber::util::SubscriberInitExt;
#[derive(Parser, Debug, Clone)]
#[command(version, about)]
enum Cli {
Client {
#[arg(short, long)]
server: String,
#[arg(long, action)]
hotpatching_enabled: bool,
},
#[cfg(not(target_arch = "wasm32"))]
Server {
#[arg(short = 'b', long)]
bind: SocketAddr,
#[arg(short = 'r', long)]
tick_rate: f64,
#[arg(short = 'C', long)]
max_clients: usize,
#[arg(long, action)]
with_client: bool,
#[arg(long, action)]
hotpatching_enabled: bool
},
#[cfg(all(not(target_arch = "wasm32"), feature = "particle_editor"))]
ParticleEditor {},
}
fn run(cli: Cli) -> AppExit {
let mut app = App::new();
match cli {
Cli::Client { server, hotpatching_enabled } => {
if hotpatching_enabled {
warn!("-+-+-+-+-+-+- Starting with hotpatching enabled -+-+-+-+-+-+-");
warn!("This can result in segfaults and inconsistent behavior! If there is weirdness, try disabling it.");
warn!("-+-+-+-+-+-+- Starting with hotpatching enabled -+-+-+-+-+-+-");
}
app.add_plugins(ClientPluginGroup { server });
app.add_plugins(SharedPluginGroup);
}
#[cfg(not(target_arch = "wasm32"))]
Cli::Server {
bind,
tick_rate,
max_clients,
hotpatching_enabled,
..
} => {
if hotpatching_enabled {
warn!("-+-+-+-+-+-+- Starting with hotpatching enabled -+-+-+-+-+-+-");
warn!("This can result in segfaults and inconsistent behavior! If there is weirdness, try disabling it.");
warn!("-+-+-+-+-+-+- Starting with hotpatching enabled -+-+-+-+-+-+-");
}
if cfg!(target_family = "wasm") {
eprintln!("the server cannot run on webassembly");
exit(1);
}
app.add_plugins(ServerPluginGroup {
bind,
tick_rate,
max_clients,
});
app.add_plugins(SharedPluginGroup);
}
#[cfg(all(not(target_arch = "wasm32"), feature = "particle_editor"))]
Cli::ParticleEditor {} => {
app.add_plugins(starkingdoms::particle_editor::particle_editor_plugin);
}
}
app.run()
}
fn main() -> AppExit {
let cli = Cli::parse();
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::from_default_env().add_directive(Directive::from_str("naga=error").unwrap()),
)
.finish()
.init();
ctrlc::set_handler(|| {
info!("caught ^C, ciao!");
exit(0);
}).unwrap();
match cli {
Cli::Client { .. } => { run(cli) },
Cli::ParticleEditor { .. } => { run(cli) },
Cli::Server { with_client, bind, hotpatching_enabled, .. } => {
if !with_client {
run(cli)
} else {
warn!("-----------------------------------------");
warn!("RUNNING IN EXPERIMENTAL LISTENSERVER MODE");
warn!("-----------------------------------------");
warn!("This mode is HIGHLY EXPERIMENTAL, relies on janky threading, and may or may not work at all.");
warn!("Use at your own risk. If weird things happen, try running separately.");
warn!("-----------------------------------------");
warn!("RUNNING IN EXPERIMENTAL LISTENSERVER MODE");
warn!("-----------------------------------------");
let scli_clone = cli.clone();
let server_thread = std::thread::Builder::new()
.name("server".to_string())
.spawn(move || {
info!("starting server thread...");
run(scli_clone)
})
.unwrap();
let mut clt_exit;
info!("starting client thread...");
let is_multicast = bind.ip().is_unspecified();
let target_ip = if is_multicast {
if bind.ip().is_ipv4() { IpAddr::V4(Ipv4Addr::LOCALHOST) } else { IpAddr::V6(Ipv6Addr::LOCALHOST) }
} else {
bind.ip()
};
let target_port = bind.port();
let target_ip_str = match target_ip {
IpAddr::V4(a) => a.to_string(),
IpAddr::V6(a) => format!("[{a}]"),
};
let target_url = format!("ws://{target_ip_str}:{target_port}");
info!("starting the client with autocalculated target server url {target_url}");
let cli2 = Cli::Client { server: target_url, hotpatching_enabled };
clt_exit = run(cli2);
if let AppExit::Error(c) = clt_exit {
error!("client exited with error {c}");
}
info!("-------- CLIENT HAS EXITED --------");
info!(" ^C to stop the server");
let srv_exit = server_thread.join().unwrap();
match srv_exit {
AppExit::Error(c) => AppExit::Error(c),
_ => match clt_exit {
AppExit::Error(c) => AppExit::Error(c),
_ => AppExit::Success
}
}
}
}
}
}