veloren/server-cli/src/cmd.rs
Imbris 8d0b776f18 Move server-cli commands to separate file, tracy profiling par_join improvements, misc improvements
- remove overwritten logging setting in server-cli
- add server-cli command to load a random area for testing without a client
- make admin add/remove commands modify ingame players instead of needing to reconnect
- add spans to par_join jobs
- added test command that loads up an area of the world
- add tracy-world-server alias
- set debug directives to info for logging
2021-03-13 02:28:59 -05:00

141 lines
4.4 KiB
Rust

use core::time::Duration;
use std::sync::mpsc::Sender;
use tracing::{error, info, warn};
#[derive(Debug, Clone)]
pub enum Message {
AbortShutdown,
Shutdown { grace_period: Duration },
Quit,
AddAdmin(String),
RemoveAdmin(String),
LoadArea(u32),
}
struct Command<'a> {
name: &'a str,
description: &'a str,
// Whether or not the command splits the arguments on whitespace
split_spaces: bool,
args: usize,
cmd: fn(Vec<String>, &mut Sender<Message>),
}
// TODO: maybe we could be using clap here?
const COMMANDS: [Command; 6] = [
Command {
name: "quit",
description: "Closes the server",
split_spaces: true,
args: 0,
cmd: |_, sender| sender.send(Message::Quit).unwrap(),
},
Command {
name: "shutdown",
description: "Initiates a graceful shutdown of the server, waiting the specified number \
of seconds before shutting down",
split_spaces: true,
args: 1,
cmd: |args, sender| {
if let Ok(grace_period) = args.first().unwrap().parse::<u64>() {
sender
.send(Message::Shutdown {
grace_period: Duration::from_secs(grace_period),
})
.unwrap()
} else {
error!("Grace period must an integer")
}
},
},
Command {
name: "loadarea",
description: "Loads up the chunks in a random area and adds a entity that mimics a player \
to keep them from despawning",
split_spaces: true,
args: 1,
cmd: |args, sender| {
if let Ok(view_distance) = args.first().unwrap().parse::<u32>() {
sender.send(Message::LoadArea(view_distance)).unwrap();
} else {
error!("View distance must be an integer");
}
},
},
Command {
name: "abortshutdown",
description: "Aborts a shutdown if one is in progress",
split_spaces: false,
args: 0,
cmd: |_, sender| sender.send(Message::AbortShutdown).unwrap(),
},
Command {
name: "admin",
description: "Add or remove an admin via \'admin add/remove <username>\'",
split_spaces: true,
args: 2,
cmd: |args, sender| match args.get(..2) {
Some([op, username]) if op == "add" => {
sender.send(Message::AddAdmin(username.clone())).unwrap()
},
Some([op, username]) if op == "remove" => {
sender.send(Message::RemoveAdmin(username.clone())).unwrap()
},
Some(_) => error!("First arg must be add or remove"),
_ => error!("Not enough args, should be unreachable"),
},
},
Command {
name: "help",
description: "List all command available",
split_spaces: true,
args: 0,
cmd: |_, _| {
info!("===== Help =====");
for command in COMMANDS.iter() {
info!("{} - {}", command.name, command.description)
}
info!("================");
},
},
];
pub fn parse_command(input: &str, msg_s: &mut Sender<Message>) {
let mut args = input.split_whitespace();
if let Some(cmd_name) = args.next() {
if let Some(cmd) = COMMANDS.iter().find(|cmd| cmd.name == cmd_name) {
let args = args.collect::<Vec<_>>();
let (arg_len, args) = if cmd.split_spaces {
(
args.len(),
args.into_iter()
.map(|s| s.to_string())
.collect::<Vec<String>>(),
)
} else {
(0, vec![args.into_iter().collect::<String>()])
};
use core::cmp::Ordering;
match arg_len.cmp(&cmd.args) {
Ordering::Less => error!("{} takes {} arguments", cmd_name, cmd.args),
Ordering::Greater => {
warn!("{} only takes {} arguments", cmd_name, cmd.args);
let cmd = cmd.cmd;
cmd(args, msg_s)
},
Ordering::Equal => {
let cmd = cmd.cmd;
cmd(args, msg_s)
},
}
} else {
error!("{} not found", cmd_name);
}
}
}