station/crates/game_server/src/commands.rs

104 lines
3.4 KiB
Rust
Raw Normal View History

//! Server commands, typed into the server console.
use bevy::prelude::*;
use bevy_replicon::prelude::*;
use game_core::{components::Character, protocol::GameLog};
use crate::{config, persistence, players::Players};
/// Who issued a command.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CommandSource {
/// The dedicated server's terminal.
Console,
}
pub const HELP: &[(&str, &str)] = &[
("help", "list commands"),
("players", "list online players"),
("say MESSAGE", "message all players"),
("save", "save the world now"),
(
"config get KEY",
"show a setting, e.g. `config get access.whitelist`",
),
(
"config set KEY VALUE",
"change a setting and write it to the config file",
),
("stop", "save and shut down the server"),
];
/// Runs a command line and returns the reply.
pub fn execute(world: &mut World, _source: CommandSource, line: &str) -> Vec<String> {
let line = line.trim().trim_start_matches('/');
let (command, rest) = line.split_once(' ').unwrap_or((line, ""));
let rest = rest.trim();
match command {
"" => vec![],
"help" => HELP
.iter()
.map(|(usage, what)| format!("{usage:<22} {what}"))
.collect(),
"players" => {
let names: Vec<String> = world
.query::<&Character>()
.iter(world)
.map(|c| c.name.clone())
.collect();
let count = world.resource::<Players>().by_client.len();
vec![format!("{count} online: {}", names.join(", "))]
}
"say" if !rest.is_empty() => {
let text = format!("[server] {rest}");
world.commands().server_trigger(ToClients {
targets: SendTargets::All,
message: GameLog { text: text.clone() },
});
world.flush();
vec![text]
}
"save" => {
if world.contains_resource::<persistence::Storage>() {
persistence::save_world_blocking(world);
vec!["World saved.".into()]
} else {
vec!["Saving is disabled.".into()]
}
}
"config" => config_command(world, rest),
"stop" => {
world.write_message(AppExit::Success);
vec!["Shutting down.".into()]
}
_ => vec![format!(
"Unknown command `{command}`. Type `help` for a list."
)],
}
}
fn config_command(world: &mut World, args: &str) -> Vec<String> {
let mut parts = args.splitn(3, ' ');
match (parts.next(), parts.next(), parts.next()) {
(Some("get"), Some(key), None) => match world.resource::<config::ServerConfig>().get(key) {
Ok(value) => vec![format!("{key} = {value}")],
Err(e) => vec![e.to_string()],
},
(Some("set"), Some(key), Some(value)) => match config::set_runtime(world, key, value) {
Ok(note) => {
let value = world
.resource::<config::ServerConfig>()
.get(key)
.unwrap_or_default();
let mut reply = format!("{key} = {value}");
if let Some(note) = note {
reply.push_str(&format!(" ({note})"));
}
vec![reply]
}
Err(e) => vec![e.to_string()],
},
_ => vec!["Usage: config get KEY | config set KEY VALUE".into()],
}
}