diff --git a/server/src/cmd.rs b/server/src/cmd.rs new file mode 100644 index 0000000000..1e1cfbfec3 --- /dev/null +++ b/server/src/cmd.rs @@ -0,0 +1,161 @@ +use crate::Server; +use common::{comp, msg::ServerMsg}; +use specs::{Entity as EcsEntity, join::Join}; +use vek::*; + +use scan_fmt::scan_fmt; +use lazy_static::lazy_static; + +pub struct ChatCommand { + pub keyword: &'static str, + arg_fmt: &'static str, + help_string: &'static str, + handler: fn(&mut Server, EcsEntity, String, &ChatCommand), +} + +impl ChatCommand { + pub fn new( + keyword: &'static str, + arg_fmt: &'static str, + help_string: &'static str, + handler: fn(&mut Server, EcsEntity, String, &ChatCommand), + ) -> Self { + Self { + keyword, + arg_fmt, + help_string, + handler, + } + } + pub fn execute(&self, server: &mut Server, entity: EcsEntity, args: String) { + (self.handler)(server, entity, args, self); + } +} + +lazy_static! { + pub static ref CHAT_COMMANDS: Vec = vec![ + ChatCommand::new( + "jump", + "{d} {d} {d}", + "jump: offset your current position by a vector\n + Usage: /jump [x] [y] [z]", + handle_jump + ), + ChatCommand::new( + "goto", + "{d} {d} {d}", + "goto: teleport to a given position\n + Usage: /goto [x] [y] [z]", + handle_goto + ), + ChatCommand::new( + "alias", + "{}", + "alias: change your player name (cannot contain spaces)\n + Usage: /alias [name]", + handle_alias + ), + ChatCommand::new( + "tp", + "{}", + "tp: teleport to a named player\n + Usage: /tp [name]", + handle_tp + ), + ChatCommand::new("help", "", "help: display this message", handle_help) + ]; +} + +fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { + let (opt_x, opt_y, opt_z) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32); + match (opt_x, opt_y, opt_z) { + (Some(x), Some(y), Some(z)) => { + if let Some(current_pos) = server + .state + .read_component_cloned::(entity) + { + server + .state + .write_component(entity, comp::phys::Pos(current_pos.0 + Vec3::new(x, y, z))) + } else { + server.clients.notify( + entity, + ServerMsg::Chat(String::from("Command 'jump' invalid in current state")), + ) + } + } + _ => server + .clients + .notify(entity, ServerMsg::Chat(String::from(action.help_string))), + } +} + +fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { + let (opt_x, opt_y, opt_z) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32); + match (opt_x, opt_y, opt_z) { + (Some(x), Some(y), Some(z)) => server + .state + .write_component(entity, comp::phys::Pos(Vec3::new(x, y, z))), + _ => server + .clients + .notify(entity, ServerMsg::Chat(String::from(action.help_string))), + } +} + +fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { + let opt_alias = scan_fmt!(&args, action.arg_fmt, String); + match opt_alias { + Some(alias) => server + .state + .write_component(entity, comp::player::Player { alias }), + None => server + .clients + .notify(entity, ServerMsg::Chat(String::from(action.help_string))), + } +} + +fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { + let opt_alias = scan_fmt!(&args, action.arg_fmt, String); + match opt_alias { + Some(alias) => { + let ecs = server.state.ecs().internal(); + let opt_player = (&ecs.entities(), &ecs.read_storage::()) + .join() + .find(|(_, player)| player.alias == alias) + .map(|(entity, _)| entity); + match opt_player { + Some(player) => match server + .state + .read_component_cloned::(player) + { + Some(pos) => server.state.write_component(entity, pos), + None => server.clients.notify( + entity, + ServerMsg::Chat(format!("Unable to teleport to player '{}'", alias)), + ), + }, + + None => { + server.clients.notify( + entity, + ServerMsg::Chat(format!("Player '{}' not found!", alias)), + ); + server + .clients + .notify(entity, ServerMsg::Chat(String::from(action.help_string))); + } + } + } + None => server + .clients + .notify(entity, ServerMsg::Chat(String::from(action.help_string))), + } +} + +fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { + for cmd in CHAT_COMMANDS.iter() { + server + .clients + .notify(entity, ServerMsg::Chat(String::from(cmd.help_string))); + } +} \ No newline at end of file diff --git a/server/src/lib.rs b/server/src/lib.rs index 881215744f..a1eb2fa86c 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -3,11 +3,12 @@ pub mod client; pub mod error; pub mod input; +pub mod cmd; // Reexports pub use crate::{error::Error, input::Input}; -use crate::client::{Client, ClientState, Clients}; +use crate::{client::{Client, ClientState, Clients}, cmd::CHAT_COMMANDS}; use common::{ comp, msg::{ClientMsg, ServerMsg}, @@ -24,162 +25,8 @@ use threadpool::ThreadPool; use vek::*; use world::World; -use lazy_static::lazy_static; -use scan_fmt::scan_fmt; - const CLIENT_TIMEOUT: f64 = 5.0; // Seconds -struct ChatCommand { - keyword: &'static str, - arg_fmt: &'static str, - help_string: &'static str, - handler: fn(&mut Server, EcsEntity, String, &ChatCommand), -} - -impl ChatCommand { - pub fn new( - keyword: &'static str, - arg_fmt: &'static str, - help_string: &'static str, - handler: fn(&mut Server, EcsEntity, String, &ChatCommand), - ) -> Self { - Self { - keyword, - arg_fmt, - help_string, - handler, - } - } -} - -lazy_static! { - static ref CHAT_COMMANDS: Vec = vec![ - ChatCommand::new( - "jump", - "{d} {d} {d}", - "jump: offset your current position by a vector\n - Usage: /jump [x] [y] [z]", - handle_jump - ), - ChatCommand::new( - "goto", - "{d} {d} {d}", - "goto: teleport to a given position\n - Usage: /goto [x] [y] [z]", - handle_goto - ), - ChatCommand::new( - "alias", - "{}", - "alias: change your player name (cannot contain spaces)\n - Usage: /alias [name]", - handle_alias - ), - ChatCommand::new( - "tp", - "{}", - "tp: teleport to a named player\n - Usage: /tp [name]", - handle_tp - ), - ChatCommand::new("help", "", "help: display this message", handle_help) - ]; -} - -fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - let (opt_x, opt_y, opt_z) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32); - match (opt_x, opt_y, opt_z) { - (Some(x), Some(y), Some(z)) => { - if let Some(current_pos) = server - .state - .read_component_cloned::(entity) - { - server - .state - .write_component(entity, comp::phys::Pos(current_pos.0 + Vec3::new(x, y, z))) - } else { - server.clients.notify( - entity, - ServerMsg::Chat(String::from("Command 'jump' invalid in current state")), - ) - } - } - _ => server - .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))), - } -} - -fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - let (opt_x, opt_y, opt_z) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32); - match (opt_x, opt_y, opt_z) { - (Some(x), Some(y), Some(z)) => server - .state - .write_component(entity, comp::phys::Pos(Vec3::new(x, y, z))), - _ => server - .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))), - } -} - -fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - let opt_alias = scan_fmt!(&args, action.arg_fmt, String); - match opt_alias { - Some(alias) => server - .state - .write_component(entity, comp::player::Player { alias }), - None => server - .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))), - } -} - -fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - let opt_alias = scan_fmt!(&args, action.arg_fmt, String); - match opt_alias { - Some(alias) => { - let ecs = server.state.ecs().internal(); - let opt_player = (&ecs.entities(), &ecs.read_storage::()) - .join() - .find(|(_, player)| player.alias == alias) - .map(|(entity, _)| entity); - match opt_player { - Some(player) => match server - .state - .read_component_cloned::(player) - { - Some(pos) => server.state.write_component(entity, pos), - None => server.clients.notify( - entity, - ServerMsg::Chat(format!("Unable to teleport to player '{}'", alias)), - ), - }, - - None => { - server.clients.notify( - entity, - ServerMsg::Chat(format!("Player '{}' not found!", alias)), - ); - server - .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))); - } - } - } - None => server - .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))), - } -} - -fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { - for cmd in CHAT_COMMANDS.iter() { - server - .clients - .notify(entity, ServerMsg::Chat(String::from(cmd.help_string))); - } -} - pub enum Event { ClientConnected { entity: EcsEntity }, ClientDisconnected { entity: EcsEntity }, @@ -523,15 +370,18 @@ impl Server { } } - fn process_chat_cmd<'a>(&mut self, entity: EcsEntity, cmd: String) { + fn process_chat_cmd(&mut self, entity: EcsEntity, cmd: String) { + // separate string into keyword and arguments let sep = cmd.find(' '); let (kwd, args) = match sep { Some(i) => (cmd[..i].to_string(), cmd[(i + 1)..].to_string()), None => (cmd, "".to_string()), }; + + // find command object and run its handler let action_opt = CHAT_COMMANDS.iter().find(|x| x.keyword == kwd); match action_opt { - Some(action) => (action.handler)(self, entity, args, action), + Some(action) => action.execute(self, entity, args), // unknown command None => { self.clients.notify(