mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Server server::cmd depends on common::cmd
This commit is contained in:
parent
307e478671
commit
7ecea34f85
@ -1,14 +1,10 @@
|
||||
use crate::{assets, comp::Player, state::State};
|
||||
use lazy_static::lazy_static;
|
||||
use specs::prelude::{Join, WorldExt};
|
||||
|
||||
|
||||
/// Struct representing a command that a user can run from server chat.
|
||||
pub struct ChatCommand {
|
||||
/// The keyword used to invoke the command, omitting the leading '/'.
|
||||
pub keyword: &'static str,
|
||||
pub struct ChatCommandData {
|
||||
/// A format string for parsing arguments.
|
||||
pub args: Vec<ArgumentSyntax>,
|
||||
pub args: Vec<ArgumentSpec>,
|
||||
/// A one-line message that explains what the command does
|
||||
pub description: &'static str,
|
||||
/// A boolean that is used to check whether the command requires
|
||||
@ -16,15 +12,9 @@ pub struct ChatCommand {
|
||||
pub needs_admin: bool,
|
||||
}
|
||||
|
||||
impl ChatCommand {
|
||||
pub fn new(
|
||||
keyword: &'static str,
|
||||
args: Vec<ArgumentSyntax>,
|
||||
description: &'static str,
|
||||
needs_admin: bool,
|
||||
) -> Self {
|
||||
impl ChatCommandData {
|
||||
pub fn new(args: Vec<ArgumentSpec>, description: &'static str, needs_admin: bool) -> Self {
|
||||
Self {
|
||||
keyword,
|
||||
args,
|
||||
description,
|
||||
needs_admin,
|
||||
@ -32,17 +22,267 @@ impl ChatCommand {
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref CHAT_COMMANDS: Vec<ChatCommand> = {
|
||||
use ArgumentSyntax::*;
|
||||
vec![
|
||||
ChatCommand::new("help", vec![Command(true)], "Display information about commands", false),
|
||||
]
|
||||
};
|
||||
// Please keep this sorted alphabetically :-)
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ChatCommand {
|
||||
Adminify,
|
||||
Alias,
|
||||
Build,
|
||||
Debug,
|
||||
DebugColumn,
|
||||
Explosion,
|
||||
GiveExp,
|
||||
GiveItem,
|
||||
Goto,
|
||||
Health,
|
||||
Help,
|
||||
Jump,
|
||||
Kill,
|
||||
KillNpcs,
|
||||
Lantern,
|
||||
Light,
|
||||
Object,
|
||||
Players,
|
||||
RemoveLights,
|
||||
SetLevel,
|
||||
Spawn,
|
||||
Sudo,
|
||||
Tell,
|
||||
Time,
|
||||
Tp,
|
||||
Version,
|
||||
Waypoint,
|
||||
}
|
||||
|
||||
// Thank you for keeping this sorted alphabetically :-)
|
||||
pub static CHAT_COMMANDS: &'static [ChatCommand] = &[
|
||||
ChatCommand::Adminify,
|
||||
ChatCommand::Alias,
|
||||
ChatCommand::Build,
|
||||
ChatCommand::Debug,
|
||||
ChatCommand::DebugColumn,
|
||||
ChatCommand::Explosion,
|
||||
ChatCommand::GiveExp,
|
||||
ChatCommand::GiveItem,
|
||||
ChatCommand::Goto,
|
||||
ChatCommand::Health,
|
||||
ChatCommand::Help,
|
||||
ChatCommand::Jump,
|
||||
ChatCommand::Kill,
|
||||
ChatCommand::KillNpcs,
|
||||
ChatCommand::Lantern,
|
||||
ChatCommand::Light,
|
||||
ChatCommand::Object,
|
||||
ChatCommand::Players,
|
||||
ChatCommand::RemoveLights,
|
||||
ChatCommand::SetLevel,
|
||||
ChatCommand::Spawn,
|
||||
ChatCommand::Sudo,
|
||||
ChatCommand::Tell,
|
||||
ChatCommand::Time,
|
||||
ChatCommand::Tp,
|
||||
ChatCommand::Version,
|
||||
ChatCommand::Waypoint,
|
||||
];
|
||||
|
||||
impl ChatCommand {
|
||||
pub fn data(&self) -> ChatCommandData {
|
||||
use ArgumentSpec::*;
|
||||
let cmd = ChatCommandData::new;
|
||||
match self {
|
||||
ChatCommand::Adminify => cmd(
|
||||
vec![PlayerName(false)],
|
||||
"Temporarily gives a player admin permissions or removes them",
|
||||
true,
|
||||
),
|
||||
ChatCommand::Alias => cmd(vec![Any("name", false)], "Change your alias", false),
|
||||
ChatCommand::Build => cmd(vec![], "Toggles build mode on and off", true),
|
||||
ChatCommand::Debug => cmd(vec![], "Place all debug items into your pack.", true),
|
||||
ChatCommand::DebugColumn => cmd(
|
||||
vec![Float("x", f32::NAN, false), Float("y", f32::NAN, false)],
|
||||
"Prints some debug information about a column",
|
||||
false,
|
||||
),
|
||||
ChatCommand::Explosion => cmd(
|
||||
vec![Float("radius", 5.0, false)],
|
||||
"Explodes the ground around you",
|
||||
true,
|
||||
),
|
||||
ChatCommand::GiveExp => cmd(
|
||||
vec![Integer("amount", 50, false)],
|
||||
"Give experience to yourself",
|
||||
true,
|
||||
),
|
||||
ChatCommand::GiveItem => cmd(
|
||||
vec![ItemSpec(false), Integer("num", 1, true)],
|
||||
"Give yourself some items",
|
||||
true,
|
||||
),
|
||||
ChatCommand::Goto => cmd(
|
||||
vec![
|
||||
Float("x", 0.0, false),
|
||||
Float("y", 0.0, false),
|
||||
Float("z", 0.0, false),
|
||||
],
|
||||
"Teleport to a position",
|
||||
true,
|
||||
),
|
||||
ChatCommand::Health => cmd(
|
||||
vec![Integer("hp", 100, false)],
|
||||
"Set your current health",
|
||||
true,
|
||||
),
|
||||
ChatCommand::Help => ChatCommandData::new(
|
||||
vec![Command(true)],
|
||||
"Display information about commands",
|
||||
false,
|
||||
),
|
||||
ChatCommand::Jump => cmd(
|
||||
vec![
|
||||
Float("x", 0.0, false),
|
||||
Float("y", 0.0, false),
|
||||
Float("z", 0.0, false),
|
||||
],
|
||||
"Offset your current position",
|
||||
true,
|
||||
),
|
||||
ChatCommand::Kill => cmd(vec![], "Kill yourself", false),
|
||||
ChatCommand::KillNpcs => cmd(vec![], "Kill the NPCs", true),
|
||||
ChatCommand::Lantern => cmd(
|
||||
vec![
|
||||
Float("strength", 5.0, false),
|
||||
Float("r", 1.0, true),
|
||||
Float("g", 1.0, true),
|
||||
Float("b", 1.0, true),
|
||||
],
|
||||
"Change your lantern's strength and color",
|
||||
true,
|
||||
),
|
||||
ChatCommand::Light => cmd(
|
||||
vec![
|
||||
Float("r", 1.0, true),
|
||||
Float("g", 1.0, true),
|
||||
Float("b", 1.0, true),
|
||||
Float("x", 0.0, true),
|
||||
Float("y", 0.0, true),
|
||||
Float("z", 0.0, true),
|
||||
Float("strength", 5.0, true),
|
||||
],
|
||||
"Spawn entity with light",
|
||||
true,
|
||||
),
|
||||
ChatCommand::Object => cmd(vec![/*TODO*/], "Spawn an object", true),
|
||||
ChatCommand::Players => cmd(vec![], "Lists players currently online", false),
|
||||
ChatCommand::RemoveLights => cmd(
|
||||
vec![Float("radius", 20.0, true)],
|
||||
"Removes all lights spawned by players",
|
||||
true,
|
||||
),
|
||||
ChatCommand::SetLevel => {
|
||||
cmd(vec![Integer("level", 10, false)], "Set player Level", true)
|
||||
},
|
||||
ChatCommand::Spawn => cmd(vec![/*TODO*/], "Spawn a test entity", true),
|
||||
ChatCommand::Sudo => cmd(
|
||||
vec![PlayerName(false), Command(false), Message /* TODO */],
|
||||
"Run command as if you were another player",
|
||||
true,
|
||||
),
|
||||
ChatCommand::Tell => cmd(
|
||||
vec![PlayerName(false), Message],
|
||||
"Send a message to another player",
|
||||
false,
|
||||
),
|
||||
ChatCommand::Time => cmd(vec![/*TODO*/], "Set the time of day", true),
|
||||
ChatCommand::Tp => cmd(vec![PlayerName(true)], "Teleport to another player", true),
|
||||
ChatCommand::Version => cmd(vec![], "Prints server version", false),
|
||||
ChatCommand::Waypoint => {
|
||||
cmd(vec![], "Set your waypoint to your current position", true)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keyword(&self) -> &'static str {
|
||||
match self {
|
||||
ChatCommand::Adminify => "adminify",
|
||||
ChatCommand::Alias => "alias",
|
||||
ChatCommand::Build => "build",
|
||||
ChatCommand::Debug => "debug",
|
||||
ChatCommand::DebugColumn => "debug_column",
|
||||
ChatCommand::Explosion => "explosion",
|
||||
ChatCommand::GiveExp => "give_exp",
|
||||
ChatCommand::GiveItem => "give_item",
|
||||
ChatCommand::Goto => "goto",
|
||||
ChatCommand::Health => "health",
|
||||
ChatCommand::Help => "help",
|
||||
ChatCommand::Jump => "jump",
|
||||
ChatCommand::Kill => "kill",
|
||||
ChatCommand::KillNpcs => "kill_npcs",
|
||||
ChatCommand::Lantern => "lantern",
|
||||
ChatCommand::Light => "light",
|
||||
ChatCommand::Object => "object",
|
||||
ChatCommand::Players => "players",
|
||||
ChatCommand::RemoveLights => "remove_lights",
|
||||
ChatCommand::SetLevel => "set_level",
|
||||
ChatCommand::Spawn => "spawn",
|
||||
ChatCommand::Sudo => "sudo",
|
||||
ChatCommand::Tell => "tell",
|
||||
ChatCommand::Time => "time",
|
||||
ChatCommand::Tp => "tp",
|
||||
ChatCommand::Version => "version",
|
||||
ChatCommand::Waypoint => "waypoint",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn help_string(&self) -> String {
|
||||
let data = self.data();
|
||||
let usage = std::iter::once(format!("/{}", self.keyword()))
|
||||
.chain(data.args.iter().map(|arg| arg.usage_string()))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
format!("{}: {}", usage, data.description)
|
||||
}
|
||||
|
||||
pub fn needs_admin(&self) -> bool { self.data().needs_admin }
|
||||
|
||||
pub fn arg_fmt(&self) -> String {
|
||||
self.data()
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| match arg {
|
||||
ArgumentSpec::PlayerName(_) => "{}",
|
||||
ArgumentSpec::ItemSpec(_) => "{}",
|
||||
ArgumentSpec::Float(_, _, _) => "{f}",
|
||||
ArgumentSpec::Integer(_, _, _) => "{d}",
|
||||
ArgumentSpec::Any(_, _) => "{}",
|
||||
ArgumentSpec::Command(_) => "{}",
|
||||
ArgumentSpec::Message => "{/.*/}",
|
||||
ArgumentSpec::OneOf(_, _, _, _) => "{}", // TODO
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for ChatCommand {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(keyword: &str) -> Result<ChatCommand, ()> {
|
||||
let kwd = if keyword.chars().next() == Some('/') {
|
||||
&keyword[1..]
|
||||
} else {
|
||||
&keyword[..]
|
||||
};
|
||||
for c in CHAT_COMMANDS {
|
||||
if kwd == c.keyword() {
|
||||
return Ok(*c);
|
||||
}
|
||||
}
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation for chat command arguments
|
||||
pub enum ArgumentSyntax {
|
||||
pub enum ArgumentSpec {
|
||||
/// The argument refers to a player by alias
|
||||
PlayerName(bool),
|
||||
/// The argument refers to an item asset by path
|
||||
@ -56,61 +296,74 @@ pub enum ArgumentSyntax {
|
||||
/// * label
|
||||
/// * default tab-completion
|
||||
/// * whether it's optional
|
||||
Integer(&'static str, f32, bool),
|
||||
Integer(&'static str, i32, bool),
|
||||
/// The argument is any string that doesn't contain spaces
|
||||
Any(&'static str, bool),
|
||||
/// The argument is a command name
|
||||
Command(bool),
|
||||
/// This is the final argument, consuming all characters until the end of input.
|
||||
/// This is the final argument, consuming all characters until the end of
|
||||
/// input.
|
||||
Message,
|
||||
/// The argument is likely an enum. The associated values are
|
||||
/// * label
|
||||
/// * Predefined string completions
|
||||
/// * Other completion types
|
||||
/// * whether it's optional
|
||||
OneOf(&'static str, &'static [&'static str], Vec<Box<ArgumentSyntax>>, bool),
|
||||
OneOf(
|
||||
&'static str,
|
||||
&'static [&'static str],
|
||||
Vec<Box<ArgumentSpec>>,
|
||||
bool,
|
||||
),
|
||||
}
|
||||
|
||||
impl ArgumentSyntax {
|
||||
pub fn help_string(arg: &ArgumentSyntax) -> String {
|
||||
match arg {
|
||||
ArgumentSyntax::PlayerName(optional) => {
|
||||
impl ArgumentSpec {
|
||||
pub fn usage_string(&self) -> String {
|
||||
match self {
|
||||
ArgumentSpec::PlayerName(optional) => {
|
||||
if *optional {
|
||||
"[player]".to_string()
|
||||
} else {
|
||||
"<player>".to_string()
|
||||
}
|
||||
},
|
||||
ArgumentSyntax::ItemSpec(optional) => {
|
||||
ArgumentSpec::ItemSpec(optional) => {
|
||||
if *optional {
|
||||
"[item]".to_string()
|
||||
} else {
|
||||
"<item>".to_string()
|
||||
}
|
||||
},
|
||||
ArgumentSyntax::Float(label, _, optional) => {
|
||||
ArgumentSpec::Float(label, _, optional) => {
|
||||
if *optional {
|
||||
format!("[{}]", label)
|
||||
} else {
|
||||
format!("<{}>", label)
|
||||
}
|
||||
},
|
||||
ArgumentSyntax::Integer(label, _, optional) => {
|
||||
ArgumentSpec::Integer(label, _, optional) => {
|
||||
if *optional {
|
||||
format!("[{}]", label)
|
||||
} else {
|
||||
format!("<{}>", label)
|
||||
}
|
||||
},
|
||||
ArgumentSyntax::Command(optional) => {
|
||||
ArgumentSpec::Any(label, optional) => {
|
||||
if *optional {
|
||||
format!("[{}]", label)
|
||||
} else {
|
||||
format!("<{}>", label)
|
||||
}
|
||||
},
|
||||
ArgumentSpec::Command(optional) => {
|
||||
if *optional {
|
||||
"[[/]command]".to_string()
|
||||
} else {
|
||||
"<[/]command>".to_string()
|
||||
}
|
||||
},
|
||||
ArgumentSyntax::Message => {
|
||||
"<message>".to_string()
|
||||
},
|
||||
ArgumentSyntax::OneOf(label, _, _, optional) => {
|
||||
ArgumentSpec::Message => "<message>".to_string(),
|
||||
ArgumentSpec::OneOf(label, _, _, optional) => {
|
||||
if *optional {
|
||||
format! {"[{}]", label}
|
||||
} else {
|
||||
@ -122,25 +375,26 @@ impl ArgumentSyntax {
|
||||
|
||||
pub fn complete(&self, state: &State, part: &String) -> Vec<String> {
|
||||
match self {
|
||||
ArgumentSyntax::PlayerName(_) => (&state.ecs().read_storage::<Player>())
|
||||
ArgumentSpec::PlayerName(_) => (&state.ecs().read_storage::<Player>())
|
||||
.join()
|
||||
.filter(|player| player.alias.starts_with(part))
|
||||
.map(|player| player.alias.clone())
|
||||
.collect(),
|
||||
ArgumentSyntax::ItemSpec(_) => assets::iterate()
|
||||
ArgumentSpec::ItemSpec(_) => assets::iterate()
|
||||
.filter(|asset| asset.starts_with(part))
|
||||
.map(|c| c.to_string())
|
||||
.collect(),
|
||||
ArgumentSyntax::Float(_, x, _) => vec![format!("{}", x)],
|
||||
ArgumentSyntax::Integer(_, x, _) => vec![format!("{}", x)],
|
||||
ArgumentSyntax::Command(_) => CHAT_COMMANDS
|
||||
ArgumentSpec::Float(_, x, _) => vec![format!("{}", x)],
|
||||
ArgumentSpec::Integer(_, x, _) => vec![format!("{}", x)],
|
||||
ArgumentSpec::Any(_, _) => vec![],
|
||||
ArgumentSpec::Command(_) => CHAT_COMMANDS
|
||||
.iter()
|
||||
.map(|com| com.keyword.clone())
|
||||
.map(|com| com.keyword())
|
||||
.filter(|kwd| kwd.starts_with(part) || format!("/{}", kwd).starts_with(part))
|
||||
.map(|c| c.to_string())
|
||||
.collect(),
|
||||
ArgumentSyntax::Message => vec![],
|
||||
ArgumentSyntax::OneOf(_, strings, alts, _) => {
|
||||
ArgumentSpec::Message => vec![],
|
||||
ArgumentSpec::OneOf(_, strings, alts, _) => {
|
||||
let string_completions = strings
|
||||
.iter()
|
||||
.filter(|string| string.starts_with(part))
|
||||
@ -150,7 +404,7 @@ impl ArgumentSyntax {
|
||||
.flat_map(|b| (*b).complete(&state, part))
|
||||
.map(|c| c.to_string());
|
||||
string_completions.chain(alt_completions).collect()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,9 @@
|
||||
use crate::{Server, StateExt};
|
||||
use chrono::{NaiveTime, Timelike};
|
||||
use common::{
|
||||
assets, comp,
|
||||
assets,
|
||||
cmd::{ChatCommand, CHAT_COMMANDS},
|
||||
comp,
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{PlayerListUpdate, ServerMsg},
|
||||
npc::{self, get_npc_name},
|
||||
@ -20,274 +22,70 @@ use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
|
||||
use vek::*;
|
||||
use world::util::Sampler;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use log::error;
|
||||
use scan_fmt::{scan_fmt, scan_fmt_some};
|
||||
|
||||
/// Struct representing a command that a user can run from server chat.
|
||||
pub struct ChatCommand {
|
||||
/// The keyword used to invoke the command, omitting the leading '/'.
|
||||
pub keyword: &'static str,
|
||||
/// A format string for parsing arguments.
|
||||
arg_fmt: &'static str,
|
||||
/// A message that explains how the command is used.
|
||||
help_string: &'static str,
|
||||
/// A boolean that is used to check whether the command requires
|
||||
/// administrator permissions or not.
|
||||
needs_admin: bool,
|
||||
/// Handler function called when the command is executed.
|
||||
/// # Arguments
|
||||
/// * `&mut Server` - the `Server` instance executing the command.
|
||||
/// * `EcsEntity` - an `Entity` corresponding to the player that invoked the
|
||||
/// command.
|
||||
/// * `EcsEntity` - an `Entity` for the player on whom the command is
|
||||
/// invoked. This differs from the previous argument when using /sudo
|
||||
/// * `String` - a `String` containing the part of the command after the
|
||||
/// keyword.
|
||||
/// * `&ChatCommand` - the command to execute with the above arguments.
|
||||
/// Handler functions must parse arguments from the the given `String`
|
||||
/// (`scan_fmt!` is included for this purpose).
|
||||
handler: fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand),
|
||||
pub trait ChatCommandExt {
|
||||
fn execute(&self, server: &mut Server, entity: EcsEntity, args: String);
|
||||
}
|
||||
|
||||
impl ChatCommand {
|
||||
/// Creates a new chat command.
|
||||
pub fn new(
|
||||
keyword: &'static str,
|
||||
arg_fmt: &'static str,
|
||||
help_string: &'static str,
|
||||
needs_admin: bool,
|
||||
handler: fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand),
|
||||
) -> Self {
|
||||
Self {
|
||||
keyword,
|
||||
arg_fmt,
|
||||
help_string,
|
||||
needs_admin,
|
||||
handler,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the contained handler function, passing `&self` as the last
|
||||
/// argument.
|
||||
pub fn execute(&self, server: &mut Server, entity: EcsEntity, args: String) {
|
||||
if self.needs_admin {
|
||||
if !server.entity_is_admin(entity) {
|
||||
server.notify_client(
|
||||
entity,
|
||||
ServerMsg::private(format!(
|
||||
"You don't have permission to use '/{}'.",
|
||||
self.keyword
|
||||
)),
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
(self.handler)(server, entity, entity, args, self);
|
||||
}
|
||||
impl ChatCommandExt for ChatCommand {
|
||||
fn execute(&self, server: &mut Server, entity: EcsEntity, args: String) {
|
||||
let cmd_data = self.data();
|
||||
if cmd_data.needs_admin && !server.entity_is_admin(entity) {
|
||||
server.notify_client(
|
||||
entity,
|
||||
ServerMsg::private(format!(
|
||||
"You don't have permission to use '/{}'.",
|
||||
self.keyword()
|
||||
)),
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
(self.handler)(server, entity, entity, args, self);
|
||||
get_handler(self)(server, entity, entity, args, &self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Static list of chat commands available to the server.
|
||||
pub static ref CHAT_COMMANDS: Vec<ChatCommand> = vec![
|
||||
ChatCommand::new(
|
||||
"give_item",
|
||||
"{} {d}",
|
||||
"/give_item <path to item> [num]\n\
|
||||
Example items: common/items/apple, common/items/debug/boost",
|
||||
true,
|
||||
handle_give,),
|
||||
ChatCommand::new(
|
||||
"jump",
|
||||
"{d} {d} {d}",
|
||||
"/jump <dx> <dy> <dz> : Offset your current position",
|
||||
true,
|
||||
handle_jump,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"goto",
|
||||
"{d} {d} {d}",
|
||||
"/goto <x> <y> <z> : Teleport to a position",
|
||||
true,
|
||||
handle_goto,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"alias",
|
||||
"{}",
|
||||
"/alias <name> : Change your alias",
|
||||
false,
|
||||
handle_alias,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"tp",
|
||||
"{}",
|
||||
"/tp <alias> : Teleport to another player",
|
||||
true,
|
||||
handle_tp,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"kill",
|
||||
"{}",
|
||||
"/kill : Kill yourself",
|
||||
false,
|
||||
handle_kill,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"time",
|
||||
"{} {s}",
|
||||
"/time <XY:XY> or [Time of day] : Set the time of day",
|
||||
true,
|
||||
handle_time,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"spawn",
|
||||
"{} {} {d}",
|
||||
"/spawn <alignment> <entity> [amount] : Spawn a test entity",
|
||||
true,
|
||||
handle_spawn,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"players",
|
||||
"{}",
|
||||
"/players : Lists players currently online",
|
||||
false,
|
||||
handle_players,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"help", "", "/help: Display this message", false, handle_help),
|
||||
ChatCommand::new(
|
||||
"health",
|
||||
"{}",
|
||||
"/health : Set your current health",
|
||||
true,
|
||||
handle_health,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"build",
|
||||
"",
|
||||
"/build : Toggles build mode on and off",
|
||||
true,
|
||||
handle_build,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"tell",
|
||||
"{}",
|
||||
"/tell <alias> <message>: Send a message to another player",
|
||||
false,
|
||||
handle_tell,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"killnpcs",
|
||||
"{}",
|
||||
"/killnpcs : Kill the NPCs",
|
||||
true,
|
||||
handle_killnpcs,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"object",
|
||||
"{}",
|
||||
"/object [Name]: Spawn an object",
|
||||
true,
|
||||
handle_object,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"light",
|
||||
"{} {} {} {} {} {} {}",
|
||||
"/light <opt: <<cr> <cg> <cb>> <<ox> <oy> <oz>> <<strength>>>: Spawn entity with light",
|
||||
true,
|
||||
handle_light,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"lantern",
|
||||
"{} {} {} {}",
|
||||
"/lantern <strength> [<r> <g> <b>]: Change your lantern's strength and color",
|
||||
true,
|
||||
handle_lantern,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"explosion",
|
||||
"{}",
|
||||
"/explosion <radius> : Explodes the ground around you",
|
||||
true,
|
||||
handle_explosion,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"waypoint",
|
||||
"{}",
|
||||
"/waypoint : Set your waypoint to your current position",
|
||||
true,
|
||||
handle_waypoint,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"adminify",
|
||||
"{}",
|
||||
"/adminify <playername> : Temporarily gives a player admin permissions or removes them",
|
||||
true,
|
||||
handle_adminify,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"debug_column",
|
||||
"{} {}",
|
||||
"/debug_column <x> <y> : Prints some debug information about a column",
|
||||
false,
|
||||
handle_debug_column,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"give_exp",
|
||||
"{d} {}",
|
||||
"/give_exp <amount> <playername?> : Give experience to yourself or specify a target player",
|
||||
true,
|
||||
handle_exp,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"set_level",
|
||||
"{d} {}",
|
||||
"/set_level <level> <playername?> : Set own Level or specify a target player",
|
||||
true,
|
||||
handle_level
|
||||
),
|
||||
ChatCommand::new(
|
||||
"removelights",
|
||||
"{}",
|
||||
"/removelights [radius] : Removes all lights spawned by players",
|
||||
true,
|
||||
handle_remove_lights,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"debug",
|
||||
"",
|
||||
"/debug : Place all debug items into your pack.",
|
||||
true,
|
||||
handle_debug,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"sudo",
|
||||
"{} {} {/.*/}",
|
||||
"/sudo <player> /<command> [args...] : Run command as if you were another player",
|
||||
true,
|
||||
handle_sudo,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"version",
|
||||
"",
|
||||
"/version : Prints server version",
|
||||
false,
|
||||
handle_version,
|
||||
),
|
||||
];
|
||||
type CommandHandler = fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand);
|
||||
fn get_handler(cmd: &ChatCommand) -> CommandHandler {
|
||||
match cmd {
|
||||
ChatCommand::Adminify => handle_adminify,
|
||||
ChatCommand::Alias => handle_alias,
|
||||
ChatCommand::Build => handle_build,
|
||||
ChatCommand::Debug => handle_debug,
|
||||
ChatCommand::DebugColumn => handle_debug_column,
|
||||
ChatCommand::Explosion => handle_explosion,
|
||||
ChatCommand::GiveExp => handle_give_exp,
|
||||
ChatCommand::GiveItem => handle_give_item,
|
||||
ChatCommand::Goto => handle_goto,
|
||||
ChatCommand::Health => handle_health,
|
||||
ChatCommand::Help => handle_help,
|
||||
ChatCommand::Jump => handle_jump,
|
||||
ChatCommand::Kill => handle_kill,
|
||||
ChatCommand::KillNpcs => handle_kill_npcs,
|
||||
ChatCommand::Lantern => handle_lantern,
|
||||
ChatCommand::Light => handle_light,
|
||||
ChatCommand::Object => handle_object,
|
||||
ChatCommand::Players => handle_players,
|
||||
ChatCommand::RemoveLights => handle_remove_lights,
|
||||
ChatCommand::SetLevel => handle_set_level,
|
||||
ChatCommand::Spawn => handle_spawn,
|
||||
ChatCommand::Sudo => handle_sudo,
|
||||
ChatCommand::Tell => handle_tell,
|
||||
ChatCommand::Time => handle_time,
|
||||
ChatCommand::Tp => handle_tp,
|
||||
ChatCommand::Version => handle_version,
|
||||
ChatCommand::Waypoint => handle_waypoint,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_give(
|
||||
fn handle_give_item(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
target: EcsEntity,
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
if let (Some(item_name), give_amount_opt) = scan_fmt_some!(&args, action.arg_fmt, String, u32) {
|
||||
if let (Some(item_name), give_amount_opt) = scan_fmt_some!(&args, &action.arg_fmt(), String, u32) {
|
||||
let give_amount = give_amount_opt.unwrap_or(1);
|
||||
if let Ok(item) = assets::load_cloned(&item_name) {
|
||||
let mut item: Item = item;
|
||||
@ -346,7 +144,7 @@ fn handle_give(
|
||||
);
|
||||
}
|
||||
} else {
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string())));
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,7 +155,7 @@ fn handle_jump(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) {
|
||||
if let Ok((x, y, z)) = scan_fmt!(&args, &action.arg_fmt(), f32, f32, f32) {
|
||||
match server.state.read_component_cloned::<comp::Pos>(target) {
|
||||
Some(current_pos) => {
|
||||
server
|
||||
@ -380,7 +178,7 @@ fn handle_goto(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) {
|
||||
if let Ok((x, y, z)) = scan_fmt!(&args, &action.arg_fmt(), f32, f32, f32) {
|
||||
if server
|
||||
.state
|
||||
.read_component_cloned::<comp::Pos>(target)
|
||||
@ -397,7 +195,10 @@ fn handle_goto(
|
||||
);
|
||||
}
|
||||
} else {
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerMsg::private(String::from(action.help_string())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -432,7 +233,7 @@ fn handle_time(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
let time = scan_fmt_some!(&args, action.arg_fmt, String);
|
||||
let time = scan_fmt_some!(&args, &action.arg_fmt(), String);
|
||||
let new_time = match time.as_ref().map(|s| s.as_str()) {
|
||||
Some("midnight") => NaiveTime::from_hms(0, 0, 0),
|
||||
Some("night") => NaiveTime::from_hms(20, 0, 0),
|
||||
@ -490,7 +291,7 @@ fn handle_health(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) {
|
||||
if let Ok(hp) = scan_fmt!(&args, &action.arg_fmt(), u32) {
|
||||
if let Some(stats) = server
|
||||
.state
|
||||
.ecs()
|
||||
@ -519,7 +320,7 @@ fn handle_alias(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
|
||||
if let Ok(alias) = scan_fmt!(&args, &action.arg_fmt(), String) {
|
||||
server
|
||||
.state
|
||||
.ecs_mut()
|
||||
@ -540,7 +341,10 @@ fn handle_alias(
|
||||
server.state.notify_registered_clients(msg);
|
||||
}
|
||||
} else {
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerMsg::private(String::from(action.help_string())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -551,7 +355,7 @@ fn handle_tp(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
|
||||
if let Ok(alias) = scan_fmt!(&args, &action.arg_fmt(), String) {
|
||||
let ecs = server.state.ecs();
|
||||
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
|
||||
.join()
|
||||
@ -576,7 +380,7 @@ fn handle_tp(
|
||||
);
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerMsg::private(String::from(action.help_string)),
|
||||
ServerMsg::private(String::from(action.help_string())),
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -585,7 +389,10 @@ fn handle_tp(
|
||||
},
|
||||
}
|
||||
} else {
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerMsg::private(String::from(action.help_string())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -596,7 +403,7 @@ fn handle_spawn(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
match scan_fmt_some!(&args, action.arg_fmt, String, npc::NpcBody, String) {
|
||||
match scan_fmt_some!(&args, &action.arg_fmt(), String, npc::NpcBody, String) {
|
||||
(Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount) => {
|
||||
if let Some(alignment) = parse_alignment(target, &opt_align) {
|
||||
let amount = opt_amount
|
||||
@ -659,7 +466,10 @@ fn handle_spawn(
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerMsg::private(String::from(action.help_string())),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -733,12 +543,16 @@ fn handle_help(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
_target: EcsEntity,
|
||||
_args: String,
|
||||
_action: &ChatCommand,
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
for cmd in CHAT_COMMANDS.iter() {
|
||||
if !cmd.needs_admin || server.entity_is_admin(client) {
|
||||
server.notify_client(client, ServerMsg::private(String::from(cmd.help_string)));
|
||||
if let Some(cmd) = scan_fmt_some!(&args, &action.arg_fmt(), ChatCommand) {
|
||||
server.notify_client(client, ServerMsg::private(String::from(cmd.help_string())));
|
||||
} else {
|
||||
for cmd in CHAT_COMMANDS.iter() {
|
||||
if !cmd.needs_admin() || server.entity_is_admin(client) {
|
||||
server.notify_client(client, ServerMsg::private(String::from(cmd.help_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -753,7 +567,7 @@ fn parse_alignment(owner: EcsEntity, alignment: &str) -> Option<comp::Alignment>
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_killnpcs(
|
||||
fn handle_kill_npcs(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
_target: EcsEntity,
|
||||
@ -781,9 +595,9 @@ fn handle_object(
|
||||
client: EcsEntity,
|
||||
target: EcsEntity,
|
||||
args: String,
|
||||
_action: &ChatCommand,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
let obj_type = scan_fmt!(&args, _action.arg_fmt, String);
|
||||
let obj_type = scan_fmt!(&args, &action.arg_fmt(), String);
|
||||
|
||||
let pos = server
|
||||
.state
|
||||
@ -894,7 +708,7 @@ fn handle_light(
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
let (opt_r, opt_g, opt_b, opt_x, opt_y, opt_z, opt_s) =
|
||||
scan_fmt_some!(&args, action.arg_fmt, f32, f32, f32, f32, f32, f32, f32);
|
||||
scan_fmt_some!(&args, &action.arg_fmt(), f32, f32, f32, f32, f32, f32, f32);
|
||||
|
||||
let mut light_emitter = comp::LightEmitter::default();
|
||||
let mut light_offset_opt = None;
|
||||
@ -955,7 +769,8 @@ fn handle_lantern(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
if let (Some(s), r, g, b) = scan_fmt_some!(&args, action.arg_fmt, f32, f32, f32, f32) {
|
||||
println!("args: '{}', fmt: '{}'", &args, &action.arg_fmt());
|
||||
if let (Some(s), r, g, b) = scan_fmt_some!(&args, &action.arg_fmt(), f32, f32, f32, f32) {
|
||||
if let Some(light) = server
|
||||
.state
|
||||
.ecs()
|
||||
@ -987,7 +802,10 @@ fn handle_lantern(
|
||||
);
|
||||
}
|
||||
} else {
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerMsg::private(String::from(action.help_string())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -998,7 +816,7 @@ fn handle_explosion(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
let power = scan_fmt!(&args, action.arg_fmt, f32).unwrap_or(8.0);
|
||||
let power = scan_fmt!(&args, &action.arg_fmt(), f32).unwrap_or(8.0);
|
||||
|
||||
if power > 512.0 {
|
||||
server.notify_client(
|
||||
@ -1062,7 +880,7 @@ fn handle_adminify(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
|
||||
if let Ok(alias) = scan_fmt!(&args, &action.arg_fmt(), String) {
|
||||
let ecs = server.state.ecs();
|
||||
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
|
||||
.join()
|
||||
@ -1082,11 +900,17 @@ fn handle_adminify(
|
||||
client,
|
||||
ServerMsg::private(format!("Player '{}' not found!", alias)),
|
||||
);
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerMsg::private(String::from(action.help_string())),
|
||||
);
|
||||
},
|
||||
}
|
||||
} else {
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerMsg::private(String::from(action.help_string())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1104,7 +928,7 @@ fn handle_tell(
|
||||
);
|
||||
return;
|
||||
}
|
||||
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
|
||||
if let Ok(alias) = scan_fmt!(&args, &action.arg_fmt(), String) {
|
||||
let ecs = server.state.ecs();
|
||||
let msg = &args[alias.len()..args.len()];
|
||||
if let Some(player) = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
|
||||
@ -1152,7 +976,10 @@ fn handle_tell(
|
||||
);
|
||||
}
|
||||
} else {
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerMsg::private(String::from(action.help_string())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1180,7 +1007,7 @@ fn handle_debug_column(
|
||||
) {
|
||||
let sim = server.world.sim();
|
||||
let sampler = server.world.sample_columns();
|
||||
if let Ok((x, y)) = scan_fmt!(&args, action.arg_fmt, i32, i32) {
|
||||
if let Ok((x, y)) = scan_fmt!(&args, &action.arg_fmt(), i32, i32) {
|
||||
let wpos = Vec2::new(x, y);
|
||||
/* let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| {
|
||||
e / sz as i32
|
||||
@ -1244,7 +1071,10 @@ spawn_rate {:?} "#,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerMsg::private(String::from(action.help_string())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1264,14 +1094,14 @@ fn find_target(
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_exp(
|
||||
fn handle_give_exp(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
target: EcsEntity,
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
let (a_exp, a_alias) = scan_fmt_some!(&args, action.arg_fmt, i64, String);
|
||||
let (a_exp, a_alias) = scan_fmt_some!(&args, &action.arg_fmt(), i64, String);
|
||||
|
||||
if let Some(exp) = a_exp {
|
||||
let ecs = server.state.ecs_mut();
|
||||
@ -1298,14 +1128,14 @@ fn handle_exp(
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_level(
|
||||
fn handle_set_level(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
target: EcsEntity,
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
let (a_lvl, a_alias) = scan_fmt_some!(&args, action.arg_fmt, u32, String);
|
||||
let (a_lvl, a_alias) = scan_fmt_some!(&args, &action.arg_fmt(), u32, String);
|
||||
|
||||
if let Some(lvl) = a_lvl {
|
||||
let ecs = server.state.ecs_mut();
|
||||
@ -1378,7 +1208,7 @@ fn handle_remove_lights(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
let opt_radius = scan_fmt_some!(&args, action.arg_fmt, f32);
|
||||
let opt_radius = scan_fmt_some!(&args, &action.arg_fmt(), f32);
|
||||
let opt_player_pos = server.state.read_component_cloned::<comp::Pos>(target);
|
||||
let mut to_delete = vec![];
|
||||
|
||||
@ -1430,7 +1260,7 @@ fn handle_sudo(
|
||||
action: &ChatCommand,
|
||||
) {
|
||||
if let (Some(player_alias), Some(cmd), cmd_args) =
|
||||
scan_fmt_some!(&args, action.arg_fmt, String, String, String)
|
||||
scan_fmt_some!(&args, &action.arg_fmt(), String, String, String)
|
||||
{
|
||||
let cmd_args = cmd_args.unwrap_or(String::from(""));
|
||||
let cmd = if cmd.chars().next() == Some('/') {
|
||||
@ -1438,14 +1268,14 @@ fn handle_sudo(
|
||||
} else {
|
||||
cmd
|
||||
};
|
||||
if let Some(action) = CHAT_COMMANDS.iter().find(|c| c.keyword == cmd) {
|
||||
if let Some(action) = CHAT_COMMANDS.iter().find(|c| c.keyword() == cmd) {
|
||||
let ecs = server.state.ecs();
|
||||
let entity_opt = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
|
||||
.join()
|
||||
.find(|(_, player)| player.alias == player_alias)
|
||||
.map(|(entity, _)| entity);
|
||||
if let Some(entity) = entity_opt {
|
||||
(action.handler)(server, client, entity, cmd_args, action);
|
||||
get_handler(action)(server, client, entity, cmd_args, action);
|
||||
} else {
|
||||
server.notify_client(
|
||||
client,
|
||||
@ -1459,7 +1289,10 @@ fn handle_sudo(
|
||||
);
|
||||
}
|
||||
} else {
|
||||
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerMsg::private(String::from(action.help_string())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1479,3 +1312,4 @@ fn handle_version(
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -22,11 +22,12 @@ use crate::{
|
||||
auth_provider::AuthProvider,
|
||||
chunk_generator::ChunkGenerator,
|
||||
client::{Client, RegionSubscription},
|
||||
cmd::CHAT_COMMANDS,
|
||||
cmd::ChatCommandExt,
|
||||
state_ext::StateExt,
|
||||
sys::sentinel::{DeletedEntities, TrackedComps},
|
||||
};
|
||||
use common::{
|
||||
cmd::ChatCommand,
|
||||
comp,
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{ClientMsg, ClientState, ServerInfo, ServerMsg},
|
||||
@ -534,18 +535,16 @@ impl Server {
|
||||
};
|
||||
|
||||
// Find the command object and run its handler.
|
||||
let action_opt = CHAT_COMMANDS.iter().find(|x| x.keyword == kwd);
|
||||
match action_opt {
|
||||
Some(action) => action.execute(self, entity, args),
|
||||
// Unknown command
|
||||
None => {
|
||||
if let Some(client) = self.state.ecs().write_storage::<Client>().get_mut(entity) {
|
||||
client.notify(ServerMsg::private(format!(
|
||||
"Unknown command '/{}'.\nType '/help' for available commands",
|
||||
kwd
|
||||
)));
|
||||
}
|
||||
},
|
||||
if let Ok(command) = kwd.parse::<ChatCommand>() {
|
||||
command.execute(self, entity, args);
|
||||
} else {
|
||||
self.notify_client(
|
||||
entity,
|
||||
ServerMsg::private(format!(
|
||||
"Unknown command '/{}'.\nType '/help' for available commands",
|
||||
kwd
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user