#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]
use core::str::FromStr;
use core::time::Duration;
use bevy::log::{Level, LogPlugin};
use bevy::prelude::*;
use bevy::DefaultPlugins;
use bevy::diagnostic::DiagnosticsPlugin;
use bevy::state::app::StatesPlugin;
use clap::{Parser, Subcommand};
#[cfg(feature = "client")]
use crate::client::{ClientTransports, ExampleClient, connect};
#[cfg(all(any(feature = "gui2d", feature = "gui3d"), feature = "client"))]
use crate::client_renderer::ExampleClientRendererPlugin;
#[cfg(feature = "server")]
use crate::server::{ExampleServer, ServerTransports, WebTransportCertificateSettings, start};
#[cfg(all(any(feature = "gui2d", feature = "gui3d"), feature = "server"))]
use crate::server_renderer::ExampleServerRendererPlugin;
use crate::shared::{CLIENT_PORT, SERVER_ADDR, SERVER_PORT, SHARED_SETTINGS, STEAM_APP_ID};
use lightyear::link::RecvLinkConditioner;
#[cfg(feature = "client")]
use lightyear::prelude::client::*;
use lightyear::prelude::*;
#[cfg(any(feature = "gui2d", feature = "gui3d"))]
use {
bevy::window::PresentMode,
bevy::winit::{UpdateMode, WinitSettings},
};
#[derive(Parser, Debug)]
#[command(version, about)]
pub struct Cli {
#[command(subcommand)]
pub mode: Option<Mode>,
}
impl Cli {
pub fn client_id(&self) -> Option<u64> {
match &self.mode {
#[cfg(feature = "client")]
Some(Mode::Client { client_id }) => *client_id,
#[cfg(all(feature = "client", feature = "server"))]
Some(Mode::Separate { client_id }) => *client_id,
#[cfg(all(feature = "client", feature = "server"))]
Some(Mode::HostClient { client_id }) => *client_id,
_ => None,
}
}
pub fn create_app(add_inspector: bool) -> App {
#[cfg(any(feature = "gui2d", feature = "gui3d"))]
let app = new_gui_app(add_inspector);
#[cfg(not(any(feature = "gui2d", feature = "gui3d")))]
let app = new_headless_app();
app
}
pub fn build_app(&self, tick_duration: Duration, add_inspector: bool) -> App {
let mut app = Cli::create_app(add_inspector);
match self.mode {
#[cfg(feature = "client")]
Some(Mode::Client { client_id }) => {
#[cfg(feature = "steam")]
app.add_steam_resources(STEAM_APP_ID);
app.add_plugins((
lightyear::prelude::client::ClientPlugins { tick_duration },
#[cfg(any(feature = "gui2d", feature = "gui3d"))]
ExampleClientRendererPlugin::new(format!("Client {client_id:?}")),
));
app
}
#[cfg(feature = "server")]
Some(Mode::Server) => {
#[cfg(feature = "steam")]
app.add_steam_resources(STEAM_APP_ID);
app.add_plugins((
lightyear::prelude::server::ServerPlugins { tick_duration },
#[cfg(any(feature = "gui2d", feature = "gui3d"))]
ExampleServerRendererPlugin::new("Server".to_string()),
));
app
}
#[cfg(all(feature = "client", feature = "server"))]
Some(Mode::HostClient { client_id }) => {
#[cfg(feature = "steam")]
app.add_steam_resources(STEAM_APP_ID);
app.add_plugins((
lightyear::prelude::client::ClientPlugins { tick_duration },
lightyear::prelude::server::ServerPlugins { tick_duration },
#[cfg(any(feature = "gui2d", feature = "gui3d"))]
ExampleClientRendererPlugin::new(format!("Host-Client {client_id:?}")),
#[cfg(any(feature = "gui2d", feature = "gui3d"))]
ExampleServerRendererPlugin::new("Host-Server".to_string()),
));
app
}
None => {
panic!("Mode is required");
}
_ => {
todo!()
}
}
}
pub fn spawn_connections(&self, app: &mut App) {
let conditioner = LinkConditionerConfig::average_condition();
match self.mode {
#[cfg(feature = "client")]
Some(Mode::Client { client_id }) => {
let client = app
.world_mut()
.spawn(ExampleClient {
client_id: client_id.expect("You need to specify a client_id via `-c ID`"),
client_port: CLIENT_PORT,
server_addr: SERVER_ADDR,
conditioner: Some(RecvLinkConditioner::new(conditioner.clone())),
transport: ClientTransports::WebTransport,
shared: SHARED_SETTINGS,
})
.id();
app.add_systems(Startup, connect);
}
#[cfg(feature = "server")]
Some(Mode::Server) => {
let server = app
.world_mut()
.spawn(ExampleServer {
conditioner: None,
transport: ServerTransports::WebTransport {
local_port: SERVER_PORT,
certificate: WebTransportCertificateSettings::FromFile {
cert: "../../certificates/cert.pem".to_string(),
key: "../../certificates/key.pem".to_string(),
},
},
shared: SHARED_SETTINGS,
})
.id();
app.add_systems(Startup, start);
}
#[cfg(all(feature = "client", feature = "server"))]
Some(Mode::HostClient { client_id }) => {
let server = app
.world_mut()
.spawn(ExampleServer {
conditioner: None,
transport: ServerTransports::WebTransport {
local_port: SERVER_PORT,
certificate: WebTransportCertificateSettings::FromFile {
cert: "../../certificates/cert.pem".to_string(),
key: "../../certificates/key.pem".to_string(),
},
},
shared: SHARED_SETTINGS,
})
.id();
let client = app
.world_mut()
.spawn((
Client::default(),
Name::new("HostClient"),
LinkOf { server },
))
.id();
app.add_systems(Startup, (start, connect).chain());
}
_ => {}
}
}
}
#[derive(Subcommand, Debug)]
pub enum Mode {
#[cfg(feature = "client")]
Client {
#[arg(short, long, default_value = None)]
client_id: Option<u64>,
},
#[cfg(feature = "server")]
Server,
#[cfg(all(feature = "client", feature = "server"))]
Separate {
#[arg(short, long, default_value = None)]
client_id: Option<u64>,
},
#[cfg(all(feature = "client", feature = "server"))]
HostClient {
#[arg(short, long, default_value = None)]
client_id: Option<u64>,
},
}
impl Default for Mode {
fn default() -> Self {
cfg_if::cfg_if! {
if #[cfg(all(feature = "client", feature = "server"))] {
Mode::HostClient { client_id: None }
} else if #[cfg(feature = "server")] {
Mode::Server
} else {
Mode::Client { client_id: None }
}
}
}
}
struct SendApp(App);
unsafe impl Send for SendApp {}
impl SendApp {
fn run(&mut self) {
self.0.run();
}
}
impl Default for Cli {
fn default() -> Self {
cli()
}
}
pub fn cli() -> Cli {
cfg_if::cfg_if! {
if #[cfg(target_family = "wasm")] {
let client_id = rand::random::<u64>();
Cli {
mode: Some(Mode::Client {
client_id: Some(client_id),
})
}
} else {
Cli::parse()
}
}
}
#[cfg(any(feature = "gui2d", feature = "gui3d"))]
pub fn window_plugin() -> WindowPlugin {
WindowPlugin {
primary_window: Some(Window {
title: format!("Lightyear Example: {}", env!("CARGO_PKG_NAME")),
resolution: (1024, 768).into(),
present_mode: PresentMode::AutoVsync,
prevent_default_event_handling: true,
..Default::default()
}),
..default()
}
}
pub fn log_plugin() -> LogPlugin {
LogPlugin {
level: Level::INFO,
filter: "wgpu=error,bevy_render=info,bevy_ecs=warn,bevy_time=warn,naga=warn,bevy_enhanced_input::action::fns=error".to_string(),
..default()
}
}
#[cfg(any(feature = "gui2d", feature = "gui3d"))]
pub fn new_gui_app(add_inspector: bool) -> App {
let mut app = App::new();
app.add_plugins(
DefaultPlugins
.build()
.set(AssetPlugin {
meta_check: bevy::asset::AssetMetaCheck::Never,
..default()
})
.set(log_plugin())
.set(window_plugin()),
);
app.insert_resource(WinitSettings::continuous());
#[cfg(feature = "debug")]
app.add_plugins(DebugUIPlugin);
if add_inspector {
app.add_plugins(bevy_inspector_egui::bevy_egui::EguiPlugin::default());
app.add_plugins(bevy_inspector_egui::quick::WorldInspectorPlugin::new());
}
app
}
pub fn new_headless_app() -> App {
let mut app = App::new();
app.add_plugins((
MinimalPlugins,
log_plugin(),
StatesPlugin,
DiagnosticsPlugin,
));
app
}