station/crates/game_server/src/main.rs

84 lines
2.9 KiB
Rust
Raw Normal View History

//! Headless dedicated server.
//!
//! ```text
//! game-server [--port 5000] [--database URL | --no-save] [--world NAME|UUID] [--map maps/station.tmx]
//! ```
use std::time::Duration;
use bevy::{app::ScheduleRunnerPlugin, log::LogPlugin, prelude::*, state::app::StatesPlugin};
use bevy_replicon::prelude::*;
use bevy_replicon_renet::RepliconRenetPlugins;
use game_core::CorePlugin;
use game_server::{
GameServerPlugin, NetworkServerPlugin, ServerSettings,
persistence::{StorageSettings, WorldSelector},
};
const DEFAULT_DATABASE: &str = "sqlite://saves/server.sqlite";
const DEFAULT_WORLD: &str = "main";
fn main() -> AppExit {
let mut port = game_core::DEFAULT_PORT;
let mut settings = ServerSettings::default();
let mut database = Some(DEFAULT_DATABASE.to_owned());
let mut world = WorldSelector::Name(DEFAULT_WORLD.to_owned());
let mut args = std::env::args().skip(1);
while let Some(arg) = args.next() {
let mut value = |name: &str| {
args.next()
.unwrap_or_else(|| usage(&format!("{name} needs a value")))
};
match arg.as_str() {
"--port" => {
port = value("--port")
.parse()
.unwrap_or_else(|_| usage("invalid port"))
}
"--database" => database = Some(value("--database")),
"--world" => world = WorldSelector::parse(&value("--world")),
"--no-save" => database = None,
"--map" => settings.map = value("--map"),
"-h" | "--help" => usage(""),
other => usage(&format!("unknown argument `{other}`")),
}
}
settings.storage = database.map(|database_url| StorageSettings {
database_url,
world,
});
App::new()
.add_plugins((
MinimalPlugins.set(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(
1.0 / 60.0,
))),
StatesPlugin,
LogPlugin::default(),
bevy::app::TerminalCtrlCHandlerPlugin,
RepliconPlugins,
RepliconRenetPlugins,
CorePlugin,
GameServerPlugin { settings },
NetworkServerPlugin { port },
))
.run()
}
fn usage(error: &str) -> ! {
if !error.is_empty() {
eprintln!("error: {error}\n");
}
eprintln!(
"usage: game-server [--port PORT] [--database URL | --no-save] [--world NAME|UUID] [--map MAP]\n\n\
--port UDP port to listen on (default {})\n\
--database sqlite:// or postgres:// URL (default {DEFAULT_DATABASE})\n\
--world world name or UUID in the database, created if missing (default {DEFAULT_WORLD})\n\
--no-save don't persist the world\n\
--map Tiled map relative to the assets directory (default {})",
game_core::DEFAULT_PORT,
game_core::DEFAULT_MAP
);
std::process::exit(if error.is_empty() { 0 } else { 2 })
}