#![warn(clippy::pedantic)] // Be annoying, and disable specific irritating lints if needed
#![deny(
clippy::allow_attributes_without_reason,
clippy::assertions_on_result_states
)]
#![warn(clippy::if_then_some_else_none)]
#![allow(clippy::type_complexity, reason = "Bevy makes this a nightmare")]
#![allow(clippy::needless_pass_by_value, reason = "Bevy makes this a nightmare")]
#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "We cast ints to floats a lot"
)]
#![allow(clippy::missing_panics_doc, reason = "Gamedev! We panic a lot")]
#![allow(clippy::too_many_arguments, reason = "Le Bevy:tm:")]
#![allow(
clippy::too_many_lines,
reason = "With the three of us, this is impossible"
)]
pub mod attachment;
pub mod client;
pub mod client_plugins;
pub mod config;
pub mod ecs;
#[cfg(all(not(target_arch = "wasm32"), feature = "particle_editor"))]
pub mod particle_editor;
pub mod particles;
#[cfg(all(not(target_arch = "wasm32"), feature = "native"))]
pub mod server;
#[cfg(all(not(target_arch = "wasm32"), feature = "native"))]
pub mod server_plugins;
pub mod shared_plugins;
pub mod physics;
pub mod prelude;
#[cfg(target_arch = "wasm32")]
pub mod wasm_entrypoint;
#[cfg(target_arch = "wasm32")]
pub use wasm_entrypoint::*;
use bevy::log::{tracing_subscriber, LogPlugin};
use crate::prelude::*;
use clap::Parser;
use crate::client_plugins::ClientPluginGroup;
#[cfg(not(target_arch = "wasm32"))]
use crate::server_plugins::ServerPluginGroup;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::process::exit;
use std::str::FromStr;
use std::time::Duration;
use bevy::app::ScheduleRunnerPlugin;
use bevy::diagnostic::FrameCountPlugin;
use bevy::state::app::StatesPlugin;
use bevy::time::TimePlugin;
use bevy::ui::UiPlugin;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::filter::Directive;
use tracing_subscriber::util::SubscriberInitExt;
use crate::shared_plugins::SharedPluginGroup;
#[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: f32,
#[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(
DefaultPlugins.build()
.disable::<LogPlugin>()
.disable::<UiPlugin>()
);
app.add_plugins(ClientPluginGroup { server: Some(server) });
app.add_plugins(SharedPluginGroup);
}
#[cfg(not(target_arch = "wasm32"))]
Cli::Server {
bind,
tick_rate,
max_clients,
hotpatching_enabled,
with_client
} => {
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);
}
if with_client {
app.add_plugins(
DefaultPlugins.build()
.disable::<LogPlugin>()
.disable::<UiPlugin>()
);
app.add_plugins(|app: &mut App| {
app.add_systems(Startup, crate::server::player::join::ls_magically_invent_player);
});
} else {
app
.add_plugins(AssetPlugin::default())
.add_plugins(StatesPlugin)
.add_plugins(TaskPoolPlugin::default())
.add_plugins(FrameCountPlugin)
.add_plugins(TimePlugin)
.add_plugins(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f32(
1.0 / tick_rate,
)));
}
app.add_plugins(SharedPluginGroup);
let mut pg = ServerPluginGroup {
bind,
tick_rate,
max_clients,
}.build();
if with_client {
pg = pg.add_group(ClientPluginGroup {
server: None
});
}
app.add_plugins(pg);
}
#[cfg(all(not(target_arch = "wasm32"), feature = "particle_editor"))]
Cli::ParticleEditor {} => {
app.add_plugins(crate::particle_editor::particle_editor_plugin);
}
}
app.run()
}
#[cfg(feature = "wasm")]
fn main() {
// noop
}
#[cfg(feature = "native")]
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();
#[cfg(feature = "native")] {
ctrlc::set_handler(|| {
info!("caught ^C, ciao!");
exit(0);
}).unwrap();
}
match cli {
Cli::Client { .. } => { run(cli) },
#[cfg(feature = "native")]
Cli::ParticleEditor { .. } => { run(cli) },
#[cfg(feature = "native")]
Cli::Server { with_client, bind, hotpatching_enabled, .. } => {
run(cli)
/*
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 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
}
}*/
}
}
}