summaryrefslogtreecommitdiff
path: root/src/gameserver.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/gameserver.rs')
-rw-r--r--src/gameserver.rs109
1 files changed, 93 insertions, 16 deletions
diff --git a/src/gameserver.rs b/src/gameserver.rs
index c27d5d4..195989f 100644
--- a/src/gameserver.rs
+++ b/src/gameserver.rs
@@ -9,13 +9,20 @@ use unicode_categories::UnicodeCategories;
use crate::{
controls::{Control, Action},
server::Server,
- PlayerId
+ PlayerId,
+ auth::{UserRegistry, LoaderError}
};
+#[derive(Debug, Clone, PartialEq)]
+enum Authentication {
+ Guest,
+ Tilde,
+ Passtoken(String)
+}
#[derive(Debug)]
enum Message {
- Name(String),
+ Auth(String, Authentication),
Chat(String),
Input(Value)
}
@@ -36,15 +43,17 @@ macro_rules! merr {
pub struct GameServer {
players: HashMap<(usize, usize), PlayerId>,
connections: HashMap<PlayerId, (usize, usize)>,
+ users: Box<dyn UserRegistry>,
servers: Vec<Box<dyn Server>>
}
impl GameServer {
- pub fn new(servers: Vec<Box<dyn Server>>) -> GameServer {
+ pub fn new(servers: Vec<Box<dyn Server>>, users: Box<dyn UserRegistry>) -> GameServer {
GameServer {
players: HashMap::new(),
connections: HashMap::new(),
- servers
+ servers,
+ users
}
}
@@ -120,19 +129,14 @@ impl GameServer {
fn handle_message(&mut self, (serverid, connectionid): (usize, usize), msg: Message) -> Result<Option<Action>, MessageError> {
let id = (serverid, connectionid);
match msg {
- Message::Name(name) => {
+ Message::Auth(name, auth) => {
if name.len() > 99 {
return Err(merr!(name, "A name can not be longer than 99 bytes"));
}
if name.len() == 0 {
return Err(merr!(name, "A name must have at least one character"));
}
- let (firstchar, username) = name.split_at(1);
- if firstchar == "~" {
- if Some(username.to_string()) != self.servers[serverid].get_name(connectionid) {
- return Err(merr!(name, "A tilde name must match your username"));
- }
- } else {
+ if auth != Authentication::Tilde {
for chr in name.chars() {
if !(chr.is_letter() || chr.is_number() || chr.is_punctuation_connector()){
return Err(merr!(name, "A name can only contain letters, numbers and underscores"));
@@ -143,16 +147,20 @@ impl GameServer {
return Err(merr!(action, "You can not change your name"));
}
let player = PlayerId{name};
+ self.authenticate(&player, auth, id)?;
if self.connections.contains_key(&player) {
return Err(merr!("nametaken", "Another connection to this player exists already"));
}
self.broadcast_message(&format!("{} connected", player.name));
self.players.insert(id, player.clone());
self.connections.insert(player.clone(), id);
+ if let Err(_) = self.send(&player, json!(["connected", format!("successfully connected as {}", &player.name)])){
+ return Err(merr!("server", "unable to send connected message"))
+ }
Ok(Some(Action::Join(player)))
}
Message::Chat(text) => {
- let player = self.players.get(&id).ok_or(merr!(action, "Set a name before you send any other messages"))?;
+ let player = self.players.get(&id).ok_or(merr!(action, "Set a valid name before you send any other messages"))?;
let name = player.name.clone();
self.broadcast_message(&format!("{}: {}", name, text));
Ok(None)
@@ -164,6 +172,47 @@ impl GameServer {
}
}
}
+
+ fn authenticate(&self, player: &PlayerId, auth: Authentication, (serverid, connectionid): (usize, usize)) -> Result<(), MessageError> {
+ Ok(match auth {
+ Authentication::Guest => {
+ if self.users.user_exists(&player) {
+ return Err(merr!("registered", "This name is registered. Use another name or authenticate for this name"))
+ }
+ ()
+ }
+ Authentication::Tilde => {
+ let (firstchar, username) = player.name.split_at(1);
+ if firstchar == "~" {
+ if Some(username.to_string()) != self.servers[serverid].get_name(connectionid) {
+ return Err(merr!(name, "A tilde name must match your username"));
+ }
+ }
+ }
+ Authentication::Passtoken(token) => {
+ match self.users.load_user(player) {
+ Ok(user) => {
+ if player.name != user.name {
+ println!("Name mismatch: user entry for {:?} has name {}", player, user.name);
+ return Err(merr!("server", "name mismatch"));
+ }
+ if token != user.pass_token {
+ println!("password mismatch: '{}' '{}'", token, user.pass_token);
+ return Err(merr!("invalidtoken", "invalid pass token"));
+ }
+ ()
+ }
+ Err(LoaderError::InvalidResource(err)) => {
+ println!("failed to load user data for user '{}': {}", player.name, err);
+ return Err(merr!("server", "failed to load user data"))
+ }
+ Err(LoaderError::MissingResource(_)) => {
+ return Err(merr!("unregistered", "this name is not registered"))
+ }
+ }
+ }
+ })
+ }
}
@@ -174,18 +223,46 @@ fn parse_message(msg: &str) -> Result<Message, MessageError> {
if arr.len() < 2 {
return Err(merr!(msg, "array not long enough"));
}
+ let arg = &arr[1];
let msgtype = arr[0].as_str().ok_or(merr!(msg, "first message element not a string"))?;
Ok(match msgtype {
"name" => {
- let name = arr[1].as_str().ok_or(merr!(msg, "name not a string"))?;
- Message::Name(name.to_string())
+ let name = arg.as_str().ok_or(merr!(msg, "name not a string"))?.to_string();
+ Message::Auth(
+ name.clone(),
+ if name.starts_with("~") {
+ Authentication::Tilde
+ } else {
+ Authentication::Guest
+ }
+ )
}
"chat" => {
- let text = arr[1].as_str().ok_or(merr!(msg, "chat text not a string"))?;
+ let text = arg.as_str().ok_or(merr!(msg, "chat text not a string"))?;
Message::Chat(text.escape_debug().to_string())
}
"input" => {
- Message::Input(arr[1].clone())
+ Message::Input(arg.clone())
+ }
+ "auth" => {
+ let name = arg.get("name").ok_or(merr!(msg, "auth message does not have name"))?.as_str().ok_or(merr!(msg, "auth name not a string"))?.to_string();
+ let typ = arg.get("type").ok_or(merr!(msg, "auth message does not have type"))?.as_str().ok_or(merr!(msg, "auth type not a string"))?;
+ Message::Auth(
+ name,
+ match typ {
+ "guest" => Authentication::Guest,
+ "tilde" => Authentication::Tilde,
+ "passtoken" => Authentication::Passtoken(
+ arg
+ .get("passtoken")
+ .ok_or(merr!(msg, "passtoken auth message does not have passtoken"))?
+ .as_str()
+ .ok_or(merr!(msg, "passtoken not a string"))?
+ .to_string()
+ ),
+ _ => {return Err(merr!(msg, "invalid authentication type"))}
+ }
+ )
}
_ => {
return Err(merr!(msg, format!("unknown messsage type {:?}", msgtype)))