prevent double logins
This commit is contained in:
parent
05596cec4f
commit
c802bceb3c
@ -58,7 +58,6 @@ fn main() {
|
|||||||
team_id: None,
|
team_id: None,
|
||||||
banned_until: None,
|
banned_until: None,
|
||||||
muted_until: None,
|
muted_until: None,
|
||||||
created_at: chrono::Utc::now(),
|
|
||||||
flags: 0,
|
flags: 0,
|
||||||
activated: true,
|
activated: true,
|
||||||
};
|
};
|
||||||
|
@ -21,9 +21,24 @@ pub struct NewUserAccountEntity {
|
|||||||
pub team_id: Option<u32>,
|
pub team_id: Option<u32>,
|
||||||
pub banned_until: Option<chrono::DateTime<chrono::Utc>>,
|
pub banned_until: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
pub muted_until: Option<chrono::DateTime<chrono::Utc>>,
|
pub muted_until: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
pub activated: bool,
|
pub activated: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NewUserAccountEntity {
|
||||||
|
fn default() -> NewUserAccountEntity {
|
||||||
|
NewUserAccountEntity {
|
||||||
|
email: "".into(),
|
||||||
|
username: "".into(),
|
||||||
|
password: "".into(),
|
||||||
|
guildcard: 0xFFFFFFFF,
|
||||||
|
team_id: None,
|
||||||
|
banned_until: None,
|
||||||
|
muted_until: None,
|
||||||
|
flags: 0,
|
||||||
|
activated: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -38,8 +53,18 @@ pub struct UserAccountEntity {
|
|||||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||||
pub flags: u32, // TODO: is this used for anything other than character creation?
|
pub flags: u32, // TODO: is this used for anything other than character creation?
|
||||||
pub activated: bool,
|
pub activated: bool,
|
||||||
|
pub at_login: bool,
|
||||||
|
pub at_character: bool,
|
||||||
|
pub at_ship: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl UserAccountEntity {
|
||||||
|
pub fn is_currently_online(&self) -> bool {
|
||||||
|
self.at_login | self.at_character | self.at_ship
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct NewUserSettingsEntity {
|
pub struct NewUserSettingsEntity {
|
||||||
pub user_id: UserAccountId,
|
pub user_id: UserAccountId,
|
||||||
|
@ -45,9 +45,12 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
team_id: user.team_id,
|
team_id: user.team_id,
|
||||||
banned_until: user.banned_until,
|
banned_until: user.banned_until,
|
||||||
muted_until: user.muted_until,
|
muted_until: user.muted_until,
|
||||||
created_at: user.created_at,
|
created_at: chrono::Utc::now(),
|
||||||
flags: user.flags,
|
flags: user.flags,
|
||||||
activated: user.activated,
|
activated: user.activated,
|
||||||
|
at_login: false,
|
||||||
|
at_character: false,
|
||||||
|
at_ship: false,
|
||||||
};
|
};
|
||||||
users.insert(user.id, user.clone());
|
users.insert(user.id, user.clone());
|
||||||
Ok(user)
|
Ok(user)
|
||||||
|
@ -9,7 +9,10 @@ create table user_accounts (
|
|||||||
flags integer default 0 not null,
|
flags integer default 0 not null,
|
||||||
activated boolean default false not null,
|
activated boolean default false not null,
|
||||||
game_session integer,
|
game_session integer,
|
||||||
interserver_session integer
|
interserver_session integer,
|
||||||
|
at_login boolean default false not null,
|
||||||
|
at_character boolean default false not null,
|
||||||
|
at_ship boolean default false not null
|
||||||
);
|
);
|
||||||
|
|
||||||
create table user_settings (
|
create table user_settings (
|
||||||
|
@ -18,6 +18,9 @@ pub struct PgUserAccount {
|
|||||||
created_at: chrono::DateTime<chrono::Utc>,
|
created_at: chrono::DateTime<chrono::Utc>,
|
||||||
flags: i32,
|
flags: i32,
|
||||||
activated: bool,
|
activated: bool,
|
||||||
|
at_login: bool,
|
||||||
|
at_character: bool,
|
||||||
|
at_ship: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<UserAccountEntity> for PgUserAccount {
|
impl Into<UserAccountEntity> for PgUserAccount {
|
||||||
@ -32,7 +35,10 @@ impl Into<UserAccountEntity> for PgUserAccount {
|
|||||||
flags: self.flags as u32,
|
flags: self.flags as u32,
|
||||||
guildcard: self.id as u32 + 1,
|
guildcard: self.id as u32 + 1,
|
||||||
team_id: None,
|
team_id: None,
|
||||||
activated: self.activated
|
activated: self.activated,
|
||||||
|
at_login: self.at_login,
|
||||||
|
at_character: self.at_character,
|
||||||
|
at_ship: self.at_ship,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ use crate::entity::item::tool::Tool;
|
|||||||
use crate::entity::item::mag::Mag;
|
use crate::entity::item::mag::Mag;
|
||||||
use crate::entity::character::{CharacterEntity, NewCharacterEntity, CharacterClass, TechLevel};
|
use crate::entity::character::{CharacterEntity, NewCharacterEntity, CharacterClass, TechLevel};
|
||||||
|
|
||||||
use crate::login::login::get_login_status;
|
use crate::login::login::{get_login_status, check_if_already_online};
|
||||||
|
|
||||||
pub const CHARACTER_PORT: u16 = 12001;
|
pub const CHARACTER_PORT: u16 = 12001;
|
||||||
const SHIP_MENU_ID: u32 = 1;
|
const SHIP_MENU_ID: u32 = 1;
|
||||||
@ -39,6 +39,7 @@ pub enum CharacterError {
|
|||||||
CouldNotLoadSettings,
|
CouldNotLoadSettings,
|
||||||
CouldNotLoadCharacters,
|
CouldNotLoadCharacters,
|
||||||
CouldNotLoadGuildcard,
|
CouldNotLoadGuildcard,
|
||||||
|
DbError,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -280,8 +281,11 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
|||||||
|
|
||||||
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendCharacterPacket>, CharacterError> {
|
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendCharacterPacket>, CharacterError> {
|
||||||
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
||||||
Ok(match get_login_status(&self.entity_gateway, pkt).await {
|
Ok(match get_login_status(&self.entity_gateway, pkt).await.and_then(check_if_already_online) {
|
||||||
Ok(user) => {
|
Ok(mut user) => {
|
||||||
|
user.at_character = true;
|
||||||
|
self.entity_gateway.save_user(&user).await.map_err(|_| CharacterError::DbError)?;
|
||||||
|
|
||||||
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
|
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
|
||||||
response.guildcard = user.guildcard;
|
response.guildcard = user.guildcard;
|
||||||
response.team_id = user.team_id.map_or(0, |ti| ti) as u32;
|
response.team_id = user.team_id.map_or(0, |ti| ti) as u32;
|
||||||
@ -471,7 +475,7 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
|
|||||||
type RecvPacket = RecvCharacterPacket;
|
type RecvPacket = RecvCharacterPacket;
|
||||||
type PacketError = CharacterError;
|
type PacketError = CharacterError;
|
||||||
|
|
||||||
fn on_connect(&mut self, id: ClientId) -> Vec<OnConnect<Self::SendPacket>> {
|
async fn on_connect(&mut self, id: ClientId) -> Vec<OnConnect<Self::SendPacket>> {
|
||||||
self.clients.insert(id, ClientState::new());
|
self.clients.insert(id, ClientState::new());
|
||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
@ -531,7 +535,13 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_disconnect(&mut self, _id: ClientId) -> Vec<(ClientId, SendCharacterPacket)> {
|
async fn on_disconnect(&mut self, id: ClientId) -> Vec<(ClientId, SendCharacterPacket)> {
|
||||||
|
if let Some(client) = self.clients.remove(&id) {
|
||||||
|
if let Some(mut user) = client.user {
|
||||||
|
user.at_character= false;
|
||||||
|
self.entity_gateway.save_user(&user).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -684,6 +694,9 @@ mod test {
|
|||||||
team_id: None,
|
team_id: None,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
activated: true,
|
activated: true,
|
||||||
|
at_login: false,
|
||||||
|
at_character: false,
|
||||||
|
at_ship: false,
|
||||||
});
|
});
|
||||||
server.clients.insert(ClientId(5), clientstate);
|
server.clients.insert(ClientId(5), clientstate);
|
||||||
|
|
||||||
@ -728,6 +741,9 @@ mod test {
|
|||||||
created_at: chrono::Utc::now(),
|
created_at: chrono::Utc::now(),
|
||||||
flags: 0,
|
flags: 0,
|
||||||
activated: true,
|
activated: true,
|
||||||
|
at_login: false,
|
||||||
|
at_character: false,
|
||||||
|
at_ship: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut server = CharacterServerState::new(test_data.clone());
|
let mut server = CharacterServerState::new(test_data.clone());
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// TODO: rename this module to auth
|
// TODO: rename this module to auth
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::net;
|
use std::net;
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@ -21,6 +22,7 @@ pub const COMMUNICATION_PORT: u16 = 12123;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum LoginError {
|
pub enum LoginError {
|
||||||
|
DbError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -61,6 +63,10 @@ pub async fn get_login_status(entity_gateway: &impl EntityGateway, pkt: &Login)
|
|||||||
let username = array_to_utf8(pkt.username).map_err(|_err| AccountStatus::Error)?;
|
let username = array_to_utf8(pkt.username).map_err(|_err| AccountStatus::Error)?;
|
||||||
let password = array_to_utf8(pkt.password).map_err(|_err| AccountStatus::Error)?;
|
let password = array_to_utf8(pkt.password).map_err(|_err| AccountStatus::Error)?;
|
||||||
let user = entity_gateway.get_user_by_name(username).await.map_err(|_| AccountStatus::InvalidUser)?;
|
let user = entity_gateway.get_user_by_name(username).await.map_err(|_| AccountStatus::InvalidUser)?;
|
||||||
|
if user.is_currently_online() {
|
||||||
|
return Err(AccountStatus::AlreadyOnline)
|
||||||
|
}
|
||||||
|
|
||||||
if !user.activated {
|
if !user.activated {
|
||||||
return Err(AccountStatus::PayUp)
|
return Err(AccountStatus::PayUp)
|
||||||
}
|
}
|
||||||
@ -77,10 +83,19 @@ pub async fn get_login_status(entity_gateway: &impl EntityGateway, pkt: &Login)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_if_already_online(user: UserAccountEntity) -> Result<UserAccountEntity, AccountStatus> {
|
||||||
|
if user.is_currently_online() {
|
||||||
|
Err(AccountStatus::PayUp)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Ok(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct LoginServerState<EG: EntityGateway> {
|
pub struct LoginServerState<EG: EntityGateway> {
|
||||||
character_server_ip: net::Ipv4Addr,
|
character_server_ip: net::Ipv4Addr,
|
||||||
entity_gateway: EG,
|
entity_gateway: EG,
|
||||||
|
clients: HashMap<ClientId, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<EG: EntityGateway> LoginServerState<EG> {
|
impl<EG: EntityGateway> LoginServerState<EG> {
|
||||||
@ -88,20 +103,24 @@ impl<EG: EntityGateway> LoginServerState<EG> {
|
|||||||
LoginServerState {
|
LoginServerState {
|
||||||
entity_gateway: entity_gateway,
|
entity_gateway: entity_gateway,
|
||||||
character_server_ip: character_server_ip.into(),
|
character_server_ip: character_server_ip.into(),
|
||||||
|
clients: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn validate_login(&mut self, pkt: &Login) -> Vec<SendLoginPacket> {
|
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendLoginPacket>, LoginError> {
|
||||||
match get_login_status(&self.entity_gateway, pkt).await {
|
match get_login_status(&self.entity_gateway, pkt).await.and_then(check_if_already_online) {
|
||||||
Ok(_user) => {
|
Ok(mut user) => {
|
||||||
|
user.at_login = true;
|
||||||
|
self.entity_gateway.save_user(&user).await.map_err(|_| LoginError::DbError)?;
|
||||||
|
self.clients.insert(id, user.username.clone());
|
||||||
|
|
||||||
let response = SendLoginPacket::LoginResponse(LoginResponse::by_status(AccountStatus::Ok, pkt.session));
|
let response = SendLoginPacket::LoginResponse(LoginResponse::by_status(AccountStatus::Ok, pkt.session));
|
||||||
//let ip = net::Ipv4Addr::new(127,0,0,1);
|
|
||||||
let ip = u32::from_ne_bytes(self.character_server_ip.octets());
|
let ip = u32::from_ne_bytes(self.character_server_ip.octets());
|
||||||
vec![response,
|
Ok(vec![response,
|
||||||
SendLoginPacket::RedirectClient(RedirectClient::new(ip, crate::login::character::CHARACTER_PORT))]
|
SendLoginPacket::RedirectClient(RedirectClient::new(ip, crate::login::character::CHARACTER_PORT))])
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
vec![SendLoginPacket::LoginResponse(LoginResponse::by_status(err, pkt.session))]
|
Ok(vec![SendLoginPacket::LoginResponse(LoginResponse::by_status(err, pkt.session))])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,7 +150,7 @@ impl<EG: EntityGateway> ServerState for LoginServerState<EG> {
|
|||||||
-> Result<Box<dyn Iterator<Item = (ClientId, Self::SendPacket)> + Send>, LoginError> {
|
-> Result<Box<dyn Iterator<Item = (ClientId, Self::SendPacket)> + Send>, LoginError> {
|
||||||
Ok(match pkt {
|
Ok(match pkt {
|
||||||
RecvLoginPacket::Login(login) => {
|
RecvLoginPacket::Login(login) => {
|
||||||
Box::new(self.validate_login(login).await
|
Box::new(self.validate_login(id, login).await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |pkt| {
|
.map(move |pkt| {
|
||||||
(id, pkt)
|
(id, pkt)
|
||||||
@ -140,7 +159,13 @@ impl<EG: EntityGateway> ServerState for LoginServerState<EG> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_disconnect(&mut self, _id: ClientId) -> Vec<(ClientId, SendLoginPacket)> {
|
async fn on_disconnect(&mut self, id: ClientId) -> Vec<(ClientId, SendLoginPacket)> {
|
||||||
|
if let Some(username) = self.clients.remove(&id) {
|
||||||
|
if let Ok(mut user) = self.entity_gateway.get_user_by_name(username).await {
|
||||||
|
user.at_login = false;
|
||||||
|
self.entity_gateway.save_user(&user).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,6 +222,9 @@ mod test {
|
|||||||
created_at: chrono::Utc::now(),
|
created_at: chrono::Utc::now(),
|
||||||
flags: 0,
|
flags: 0,
|
||||||
activated: true,
|
activated: true,
|
||||||
|
at_login: false,
|
||||||
|
at_character: false,
|
||||||
|
at_ship: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -282,6 +310,9 @@ mod test {
|
|||||||
created_at: chrono::Utc::now(),
|
created_at: chrono::Utc::now(),
|
||||||
flags: 0,
|
flags: 0,
|
||||||
activated: true,
|
activated: true,
|
||||||
|
at_login: false,
|
||||||
|
at_character: false,
|
||||||
|
at_ship: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -328,6 +359,9 @@ mod test {
|
|||||||
created_at: chrono::Utc::now(),
|
created_at: chrono::Utc::now(),
|
||||||
flags: 0,
|
flags: 0,
|
||||||
activated: true,
|
activated: true,
|
||||||
|
at_login: false,
|
||||||
|
at_character: false,
|
||||||
|
at_ship: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use libpso::packet::login::{Login, LoginResponse, AccountStatus, Session};
|
|||||||
use libpso::packet::ship::*;
|
use libpso::packet::ship::*;
|
||||||
use crate::common::serverstate::ClientId;
|
use crate::common::serverstate::ClientId;
|
||||||
use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients};
|
use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients};
|
||||||
use crate::login::login::get_login_status;
|
use crate::login::login::{get_login_status, check_if_already_online};
|
||||||
use crate::entity::gateway::EntityGateway;
|
use crate::entity::gateway::EntityGateway;
|
||||||
use crate::ship::items::ItemManager;
|
use crate::ship::items::ItemManager;
|
||||||
|
|
||||||
@ -13,8 +13,11 @@ pub async fn validate_login<EG: EntityGateway>(id: ClientId,
|
|||||||
item_manager: &mut ItemManager,
|
item_manager: &mut ItemManager,
|
||||||
ship_name: &String)
|
ship_name: &String)
|
||||||
-> Result<Vec<SendShipPacket>, ShipError> {
|
-> Result<Vec<SendShipPacket>, ShipError> {
|
||||||
Ok(match get_login_status(entity_gateway, pkt).await {
|
Ok(match get_login_status(entity_gateway, pkt).await.and_then(check_if_already_online) {
|
||||||
Ok(user) => {
|
Ok(mut user) => {
|
||||||
|
user.at_ship= true;
|
||||||
|
entity_gateway.save_user(&user).await?;
|
||||||
|
|
||||||
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
|
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
|
||||||
response.guildcard = user.id.0 as u32;
|
response.guildcard = user.id.0 as u32;
|
||||||
response.team_id = user.team_id.map_or(31, |ti| ti) as u32;
|
response.team_id = user.team_id.map_or(31, |ti| ti) as u32;
|
||||||
|
@ -600,6 +600,11 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
|||||||
self.client_location.remove_client_from_area(id);
|
self.client_location.remove_client_from_area(id);
|
||||||
self.item_manager.remove_character_from_room(&client.character);
|
self.item_manager.remove_character_from_room(&client.character);
|
||||||
|
|
||||||
|
if let Some(mut client) = self.clients.remove(&id) {
|
||||||
|
client.user.at_ship = false;
|
||||||
|
self.entity_gateway.save_user(&client.user).await;
|
||||||
|
}
|
||||||
|
|
||||||
neighbors.into_iter().map(|n| {
|
neighbors.into_iter().map(|n| {
|
||||||
(n.client, pkt.clone())
|
(n.client, pkt.clone())
|
||||||
}).collect()
|
}).collect()
|
||||||
|
@ -18,12 +18,8 @@ pub async fn new_user_character<EG: EntityGateway>(entity_gateway: &mut EG, user
|
|||||||
username: username.into(),
|
username: username.into(),
|
||||||
password: bcrypt::hash(password, 5).unwrap(),
|
password: bcrypt::hash(password, 5).unwrap(),
|
||||||
guildcard: 1,
|
guildcard: 1,
|
||||||
team_id: None,
|
|
||||||
banned_until: None,
|
|
||||||
muted_until: None,
|
|
||||||
created_at: chrono::Utc::now(),
|
|
||||||
flags: 0,
|
|
||||||
activated: true,
|
activated: true,
|
||||||
|
..NewUserAccountEntity::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let user = entity_gateway.create_user(new_user).await.unwrap();
|
let user = entity_gateway.create_user(new_user).await.unwrap();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user