diff --git a/src/check.rs b/src/check.rs new file mode 100644 index 0000000..cbd52e4 --- /dev/null +++ b/src/check.rs @@ -0,0 +1,90 @@ +use std::cell::RefCell; + +use crate::config::Player; + +// this global stuff is so that we can load up the config and check stuff without +// being part of the Game struct +thread_local! { + pub static NAME_MIN: RefCell = RefCell::new(0); + pub static NAME_MAX: RefCell = RefCell::new(0); + pub static NAME_CHARS: RefCell<&'static str> = RefCell::new(""); + pub static PASS_MIN: RefCell = RefCell::new(0); + pub static PASS_MAX: RefCell = RefCell::new(0); + pub static PASS_CHARS: RefCell<&'static str> = RefCell::new(""); +} + +/// Return value for check functions +#[derive(Debug)] +pub enum PlayerCheck { + /// The value checks out + Ok(String), + + /// There is one or more issues + Err(Vec), +} + +pub fn init(config: &Player) { + NAME_MIN.with(|n| *n.borrow_mut() = config.name_min); + NAME_MAX.with(|n| *n.borrow_mut() = config.name_max); + NAME_CHARS.with(|n| *n.borrow_mut() = Box::leak(config.name_chars.clone().into_boxed_str())); + PASS_MIN.with(|p| *p.borrow_mut() = config.pass_min); + PASS_MAX.with(|p| *p.borrow_mut() = config.pass_max); + PASS_CHARS.with(|p| *p.borrow_mut() = Box::leak(config.pass_chars.clone().into_boxed_str())); +} + +/// Check the player name to ensure it meets guidelines +pub fn player_name>(name: S) -> PlayerCheck { + let name = name.into(); + let name = name.trim(); + let mut err_vec = Vec::::new(); + + if name.len() < NAME_MIN.with(|n| *n.borrow()) { + err_vec.push(format!("{} character minimum", NAME_MIN.with(|n| *n.borrow()))); + } else if name.len() > NAME_MAX.with(|n| *n.borrow()) { + err_vec.push(format!("{} character maximum", NAME_MAX.with(|n| *n.borrow()))); + } + + for c in name.chars() { + if !NAME_CHARS.with(|n| *n.borrow()).contains(&c.to_string()) { + err_vec.push(format!( + "Allowed characters are: {}", + NAME_CHARS.with(|n| *n.borrow()) + )); + break; + } + } + + if !err_vec.is_empty() { + PlayerCheck::Err(err_vec) + } else { + PlayerCheck::Ok(name.into()) + } +} + +/// Check the password to ensure it meets password requirements +pub fn player_password>(password: S) -> PlayerCheck { + let password = password.into(); + let mut err_vec = Vec::::new(); + + if password.len() < PASS_MIN.with(|p| *p.borrow()) { + err_vec.push(format!("{} character minimum", PASS_MIN.with(|p| *p.borrow()))); + } else if password.len() > PASS_MAX.with(|p| *p.borrow()) { + err_vec.push(format!("{} character maximum", PASS_MAX.with(|p| *p.borrow()))); + } + + for c in password.chars() { + if !PASS_CHARS.with(|p| *p.borrow()).contains(&c.to_string()) { + err_vec.push(format!( + "Allowed characters are: {}", + PASS_CHARS.with(|p| *p.borrow()) + )); + break; + } + } + + if err_vec.is_empty() { + PlayerCheck::Ok(password.into()) + } else { + PlayerCheck::Err(err_vec) + } +} diff --git a/src/game/check_player.rs b/src/game/check_player.rs deleted file mode 100644 index 3f5968d..0000000 --- a/src/game/check_player.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::game::Game; - -/// Return value for check functions -#[derive(Debug)] -pub enum PlayerCheck { - /// The value checks out - Ok(String), - - /// There is one or more issues - Err(Vec), -} - -impl Game { - /// Check the player name to ensure it meets guidelines - pub fn check_player_name>(&self, name: S) -> PlayerCheck { - let name = name.into(); - let name = name.trim(); - let mut err_vec = Vec::::new(); - - if name.len() < self.config.player.name_min { - err_vec.push(format!("{} character minimum", self.config.player.name_min)); - } else if name.len() > self.config.player.name_max { - err_vec.push(format!("{} character maximum", self.config.player.name_max)); - } - - for c in name.chars() { - if !self.config.player.name_chars.contains(&c.to_string()) { - err_vec.push(format!( - "Allowed characters are: {}", - self.config.player.name_chars - )); - break; - } - } - - if !err_vec.is_empty() { - PlayerCheck::Err(err_vec) - } else { - PlayerCheck::Ok(name.into()) - } - } - - /// Check the password to ensure it meets password requirements - pub fn check_player_password>(&self, password: S) -> PlayerCheck { - let password = password.into(); - let mut err_vec = Vec::::new(); - - if password.len() < self.config.player.pass_min { - err_vec.push(format!("{} character minimum", self.config.player.pass_min)); - } else if password.len() > self.config.player.pass_max { - err_vec.push(format!("{} character maximum", self.config.player.pass_max)); - } - - for c in password.chars() { - if !self.config.player.pass_chars.contains(&c.to_string()) { - err_vec.push(format!( - "Allowed characters are: {}", - self.config.player.pass_chars - )); - break; - } - } - - if err_vec.is_empty() { - PlayerCheck::Ok(password.into()) - } else { - PlayerCheck::Err(err_vec) - } - } -} diff --git a/src/game/mod.rs b/src/game/mod.rs index 2d5902c..1929e22 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,4 +1,3 @@ -mod check_player; #[allow(clippy::module_inception)] mod game; mod handle_events; @@ -7,5 +6,5 @@ mod new; mod process_recv_message; mod state; -pub use check_player::*; +//pub use check_player::*; pub use game::Game; diff --git a/src/game/new.rs b/src/game/new.rs index 2bb1b08..2908952 100644 --- a/src/game/new.rs +++ b/src/game/new.rs @@ -3,6 +3,7 @@ use std::collections::{HashMap, VecDeque}; use log::{debug, info}; use mio::{Events, Interest, Poll, Token}; +use crate::check; use crate::client::Client; use crate::config::Config; use crate::database::Db; @@ -21,6 +22,9 @@ impl Game { try_print(logger::init(log_level), "Unable to initialize logger")?; debug!("Initialized logging facility"); + debug!("Setting up check config"); + check::init(&config.player); + debug!("Opening database"); let db = Db::open(&config.database)?; diff --git a/src/game/state/login.rs b/src/game/state/login.rs index 4c616e1..ea07507 100644 --- a/src/game/state/login.rs +++ b/src/game/state/login.rs @@ -1,8 +1,9 @@ use chrono::Utc; use mio::Token; +use crate::check; use crate::command::Command; -use crate::game::{Game, PlayerCheck}; +use crate::game::Game; use crate::log_error; use crate::password::Password; use crate::player::Player; @@ -19,8 +20,8 @@ impl Game { if message.is_empty() { send_queue.push(token, "\n\nUsername: ", false, None); } else { - match self.check_player_name(message) { - PlayerCheck::Ok(name) => match self.db.find_player_by_name(&name) { + match check::player_name(message) { + check::PlayerCheck::Ok(name) => match self.db.find_player_by_name(&name) { Ok(Some(_)) => { send_queue.push( token, @@ -41,7 +42,7 @@ impl Game { send_queue.push(token, "\nError\n\nUsername: ", false, None); } }, - PlayerCheck::Err(err) => { + check::PlayerCheck::Err(err) => { send_queue.push(token, "\nInvalid username:\n", false, None); for line in err { send_queue.push(token, format!("{}\n", line), false, None); @@ -81,8 +82,8 @@ impl Game { Some(State::Login(Login::Username)), ); } else { - match self.check_player_password(message) { - PlayerCheck::Ok(pass) => { + match check::player_password(message) { + check::PlayerCheck::Ok(pass) => { match Password::new(pass) { Ok(password) => send_queue.push( token, @@ -105,7 +106,7 @@ impl Game { }, }; } - PlayerCheck::Err(err) => { + check::PlayerCheck::Err(err) => { send_queue.push(token, "\nInvalid password:\n", false, None); for line in err { send_queue.push(token, format!("{}\n", line), false, None); diff --git a/src/lib.rs b/src/lib.rs index 8d80e8f..34fece4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ //! The Rude Mud +pub mod check; pub mod client; pub mod command; pub mod config; diff --git a/src/main.rs b/src/main.rs index 612cddc..c5502ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod check; mod client; mod command; mod config;