Browse Source

start on misc interserver things like player counts and ship lists

pbs
jake 4 years ago
parent
commit
ae7ba9e3fe
  1. 5
      src/common/interserver.rs
  2. 1
      src/common/mainloop/interserver.rs
  3. 3
      src/entity/account.rs
  4. 100
      src/login/character.rs
  5. 11
      src/ship/packet/handler/auth.rs
  6. 34
      src/ship/ship.rs

5
src/common/interserver.rs

@ -8,7 +8,7 @@ pub struct ServerId(pub usize);
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AuthToken(pub String); pub struct AuthToken(pub String);
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Ship { pub struct Ship {
pub name: String, pub name: String,
pub ip: Ipv4Addr, pub ip: Ipv4Addr,
@ -38,7 +38,8 @@ pub enum ShipMessage {
message: String, message: String,
}, },
RequestShipList, RequestShipList,
AddUser(UserAccountId),
RemoveUser(UserAccountId),
} }

1
src/common/mainloop/interserver.rs

@ -204,7 +204,6 @@ pub fn login_listen_mainloop<EG: EntityGateway + 'static>(state: Arc<Mutex<Chara
})) }))
} }
login_recv_loop(server_id, socket.clone(), server_state_sender.clone(), client_sender).await; login_recv_loop(server_id, socket.clone(), server_state_sender.clone(), client_sender).await;
interserver_send_loop(server_id, socket.clone(), client_receiver).await; interserver_send_loop(server_id, socket.clone(), client_receiver).await;
} }

3
src/entity/account.rs

@ -1,10 +1,11 @@
use serde::{Serialize, Deserialize};
use libpso::character::settings; use libpso::character::settings;
use libpso::character::guildcard; use libpso::character::guildcard;
pub const USERFLAG_NEWCHAR: u32 = 0x00000001; pub const USERFLAG_NEWCHAR: u32 = 0x00000001;
pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002; pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct UserAccountId(pub u32); pub struct UserAccountId(pub u32);
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UserSettingsId(pub u32); pub struct UserSettingsId(pub u32);

100
src/login/character.rs

@ -6,6 +6,7 @@ use rand::Rng;
use crc::{crc32, Hasher32}; use crc::{crc32, Hasher32};
use libpso::packet::login::*; use libpso::packet::login::*;
use libpso::packet::ship::{MenuDetail, SmallLeftDialog};
use libpso::{PacketParseError, PSOPacket}; use libpso::{PacketParseError, PSOPacket};
use libpso::crypto::bb::PSOBBCipher; use libpso::crypto::bb::PSOBBCipher;
use crate::entity::item; use crate::entity::item;
@ -18,7 +19,7 @@ use crate::common::leveltable::CharacterLevelTable;
use libpso::{utf8_to_array, utf8_to_utf16_array}; use libpso::{utf8_to_array, utf8_to_utf16_array};
use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::gateway::{EntityGateway, GatewayError};
use crate::entity::account::{UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
use crate::entity::account::{UserAccountId, UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
use crate::entity::item::{NewItemEntity, ItemDetail, ItemLocation, InventoryItemEntity, InventoryEntity, BankEntity, BankName, EquippedEntity}; use crate::entity::item::{NewItemEntity, ItemDetail, ItemLocation, InventoryItemEntity, InventoryEntity, BankEntity, BankName, EquippedEntity};
use crate::entity::item::weapon::Weapon; use crate::entity::item::weapon::Weapon;
use crate::entity::item::armor::Armor; use crate::entity::item::armor::Armor;
@ -57,6 +58,7 @@ pub enum RecvCharacterPacket {
CharacterPreview(CharacterPreview), CharacterPreview(CharacterPreview),
SetFlag(SetFlag), SetFlag(SetFlag),
MenuSelect(MenuSelect), MenuSelect(MenuSelect),
MenuDetail(MenuDetail),
} }
impl RecvServerPacket for RecvCharacterPacket { impl RecvServerPacket for RecvCharacterPacket {
@ -73,6 +75,7 @@ impl RecvServerPacket for RecvCharacterPacket {
0xE5 => Ok(RecvCharacterPacket::CharacterPreview(CharacterPreview::from_bytes(data)?)), 0xE5 => Ok(RecvCharacterPacket::CharacterPreview(CharacterPreview::from_bytes(data)?)),
0xEC => Ok(RecvCharacterPacket::SetFlag(SetFlag::from_bytes(data)?)), 0xEC => Ok(RecvCharacterPacket::SetFlag(SetFlag::from_bytes(data)?)),
0x10 => Ok(RecvCharacterPacket::MenuSelect(MenuSelect::from_bytes(data)?)), 0x10 => Ok(RecvCharacterPacket::MenuSelect(MenuSelect::from_bytes(data)?)),
0x09 => Ok(RecvCharacterPacket::MenuDetail(MenuDetail::from_bytes(data)?)),
_ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec())) _ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec()))
} }
} }
@ -94,6 +97,7 @@ pub enum SendCharacterPacket {
Timestamp(Timestamp), Timestamp(Timestamp),
ShipList(ShipList), ShipList(ShipList),
RedirectClient(RedirectClient), RedirectClient(RedirectClient),
SmallLeftDialog(SmallLeftDialog),
} }
impl SendServerPacket for SendCharacterPacket { impl SendServerPacket for SendCharacterPacket {
@ -112,6 +116,7 @@ impl SendServerPacket for SendCharacterPacket {
SendCharacterPacket::Timestamp(pkt) => pkt.as_bytes(), SendCharacterPacket::Timestamp(pkt) => pkt.as_bytes(),
SendCharacterPacket::ShipList(pkt) => pkt.as_bytes(), SendCharacterPacket::ShipList(pkt) => pkt.as_bytes(),
SendCharacterPacket::RedirectClient(pkt) => pkt.as_bytes(), SendCharacterPacket::RedirectClient(pkt) => pkt.as_bytes(),
SendCharacterPacket::SmallLeftDialog(pkt) => pkt.as_bytes(),
} }
} }
} }
@ -168,6 +173,11 @@ impl ClientState {
} }
} }
#[derive(Clone)]
struct ConnectedClient {
ship_id: Option<ServerId>,
expires: Option<chrono::DateTime<chrono::Utc>>,
}
pub struct CharacterServerState<EG: EntityGateway> { pub struct CharacterServerState<EG: EntityGateway> {
entity_gateway: EG, entity_gateway: EG,
@ -177,6 +187,8 @@ pub struct CharacterServerState<EG: EntityGateway> {
ships: BTreeMap<ServerId, Ship>, ships: BTreeMap<ServerId, Ship>,
level_table: CharacterLevelTable, level_table: CharacterLevelTable,
auth_token: AuthToken, auth_token: AuthToken,
connected_clients: BTreeMap<UserAccountId, ConnectedClient>,
authenticated_ships: BTreeSet<ServerId>, authenticated_ships: BTreeSet<ServerId>,
ship_sender: BTreeMap<ServerId, Box<dyn Fn(LoginMessage) + Send>>, ship_sender: BTreeMap<ServerId, Box<dyn Fn(LoginMessage) + Send>>,
} }
@ -298,6 +310,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
auth_token: auth_token, auth_token: auth_token,
authenticated_ships: BTreeSet::new(), authenticated_ships: BTreeSet::new(),
ship_sender: BTreeMap::new(), ship_sender: BTreeMap::new(),
connected_clients: BTreeMap::new(),
} }
} }
@ -306,23 +319,35 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
} }
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendCharacterPacket>, anyhow::Error> { async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
Ok(match get_login_status(&self.entity_gateway, pkt).await.and_then(check_if_already_online) {
Ok(mut user) => {
user.at_character = true;
self.entity_gateway.save_user(&user).await?;
match get_login_status(&self.entity_gateway, pkt).await {
Ok(user) => {
if let Some(connected_client) = self.connected_clients.get(&user.id) {
if let Some(expires) = connected_client.expires {
if expires < chrono::Utc::now() {
return Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(AccountStatus::AlreadyOnline, Session::new()))]);
}
}
}
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;
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
self.connected_clients.insert(user.id.clone(), ConnectedClient {
ship_id: None,
expires: Some(chrono::Utc::now() + chrono::Duration::minutes(1)),
});
client.user = Some(user); client.user = Some(user);
client.session = pkt.session; client.session = pkt.session;
vec![SendCharacterPacket::LoginResponse(response)]
Ok(vec![SendCharacterPacket::LoginResponse(response)])
}, },
Err(err) => { Err(err) => {
vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(err, Session::new()))]
Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(err, Session::new()))])
}
} }
})
} }
fn send_ship_list(&mut self, _id: ClientId, _pkt: &Login) -> Result<Vec<SendCharacterPacket>, anyhow::Error> { fn send_ship_list(&mut self, _id: ClientId, _pkt: &Login) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
@ -493,6 +518,16 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
.ok_or(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?; .ok_or(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?;
Ok(vec![SendCharacterPacket::RedirectClient(RedirectClient::new(u32::from_le_bytes(ship.ip.octets()), ship.port))]) Ok(vec![SendCharacterPacket::RedirectClient(RedirectClient::new(u32::from_le_bytes(ship.ip.octets()), ship.port))])
} }
fn ship_detail(&mut self, menudetail: &MenuDetail) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
let players = self.connected_clients.iter()
.filter(|(_, client)| {
client.ship_id == Some(ServerId(menudetail.item as usize))
})
.count();
let ship_details = format!("players: {}\nrooms: {}", players, 0);
Ok(vec![SendCharacterPacket::SmallLeftDialog(SmallLeftDialog::new(ship_details))])
}
} }
#[async_trait::async_trait] #[async_trait::async_trait]
@ -557,6 +592,12 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
}, },
RecvCharacterPacket::MenuSelect(menuselect) => { RecvCharacterPacket::MenuSelect(menuselect) => {
Box::new(self.select_ship(menuselect)?.into_iter().map(move |pkt| (id, pkt))) Box::new(self.select_ship(menuselect)?.into_iter().map(move |pkt| (id, pkt)))
},
RecvCharacterPacket::MenuDetail(menudetail) => {
match menudetail.menu {
SHIP_MENU_ID => Box::new(self.ship_detail(menudetail)?.into_iter().map(move |pkt| (id, pkt))),
_ => Box::new(Vec::new().into_iter())
}
} }
}) })
} }
@ -588,20 +629,59 @@ impl<EG: EntityGateway> InterserverActor for CharacterServerState<EG> {
if self.auth_token == auth_token { if self.auth_token == auth_token {
self.authenticated_ships.insert(id); self.authenticated_ships.insert(id);
} }
Ok(Vec::new())
}, },
ShipMessage::NewShip(new_ship) => { ShipMessage::NewShip(new_ship) => {
if self.authenticated_ships.contains(&id) { if self.authenticated_ships.contains(&id) {
self.ships.insert(id, new_ship); self.ships.insert(id, new_ship);
} }
Ok(Vec::new())
}, },
_ => {}
ShipMessage::AddUser(new_user) => {
if self.authenticated_ships.contains(&id) {
self.connected_clients.insert(new_user, ConnectedClient {
ship_id: Some(id),
expires: None,
});
}
Ok(Vec::new())
},
ShipMessage::RemoveUser(new_user) => {
if self.authenticated_ships.contains(&id) {
self.connected_clients.remove(&new_user);
}
Ok(Vec::new())
},
ShipMessage::RequestShipList => {
if self.authenticated_ships.contains(&id) {
Ok(vec![(id, LoginMessage::ShipList {
ships: self.ships
.iter()
.map(|(_, ship)| {
ship
})
.cloned()
.collect()
})])
}
else {
Ok(Vec::new())
} }
},
ShipMessage::SendMail{..} => {
Ok(Vec::new()) Ok(Vec::new())
},
}
} }
async fn on_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> { async fn on_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {
self.ships.remove(&id); self.ships.remove(&id);
self.ship_sender.remove(&id); self.ship_sender.remove(&id);
self.connected_clients = self.connected_clients.clone().into_iter()
.filter(|(_, client)| {
client.ship_id != Some(id)
})
.collect();
Vec::new() Vec::new()
} }
} }

11
src/ship/packet/handler/auth.rs

@ -5,19 +5,18 @@ use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients};
use crate::login::login::{get_login_status, check_if_already_online}; 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;
use crate::common::interserver::ShipMessage;
pub async fn validate_login<EG: EntityGateway>(id: ClientId, pub async fn validate_login<EG: EntityGateway>(id: ClientId,
pkt: &Login, pkt: &Login,
entity_gateway: &mut EG, entity_gateway: &mut EG,
clients: &mut Clients, clients: &mut Clients,
item_manager: &mut ItemManager, item_manager: &mut ItemManager,
shipgate_sender: &Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
ship_name: &String) ship_name: &String)
-> Result<Vec<SendShipPacket>, anyhow::Error> { -> Result<Vec<SendShipPacket>, anyhow::Error> {
Ok(match get_login_status(entity_gateway, pkt).await.and_then(check_if_already_online) {
Ok(match get_login_status(entity_gateway, pkt).await {
Ok(mut 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;
@ -30,6 +29,10 @@ pub async fn validate_login<EG: EntityGateway>(id: ClientId,
let settings = entity_gateway.get_user_settings_by_user(&user).await?; let settings = entity_gateway.get_user_settings_by_user(&user).await?;
item_manager.load_character(entity_gateway, &character).await?; item_manager.load_character(entity_gateway, &character).await?;
if let Some(shipgate_sender) = shipgate_sender.as_ref() {
shipgate_sender(ShipMessage::AddUser(user.id.clone()));
}
clients.insert(id, ClientState::new(user, settings, character, pkt.session)); clients.insert(id, ClientState::new(user, settings, character, pkt.session));
vec![SendShipPacket::LoginResponse(response), SendShipPacket::ShipBlockList(ShipBlockList::new(&&ship_name, 3))] vec![SendShipPacket::LoginResponse(response), SendShipPacket::ShipBlockList(ShipBlockList::new(&&ship_name, 3))]
}, },

34
src/ship/ship.rs

@ -351,6 +351,7 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
port: self.port.unwrap_or(SHIP_PORT), port: self.port.unwrap_or(SHIP_PORT),
shops: Box::new(ItemShops::new()), shops: Box::new(ItemShops::new()),
auth_token: self.auth_token.unwrap_or(AuthToken("".into())), auth_token: self.auth_token.unwrap_or(AuthToken("".into())),
ship_list: Vec::new(),
shipgate_sender: None, shipgate_sender: None,
} }
} }
@ -370,7 +371,8 @@ pub struct ShipServerState<EG: EntityGateway> {
shops: Box<ItemShops>, shops: Box<ItemShops>,
auth_token: AuthToken, auth_token: AuthToken,
shipgate_sender: Option<Box<dyn Fn(ShipMessage) + Send>>,
ship_list: Vec<Ship>,
shipgate_sender: Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
} }
impl<EG: EntityGateway> ShipServerState<EG> { impl<EG: EntityGateway> ShipServerState<EG> {
@ -378,7 +380,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
ShipServerStateBuilder::new() ShipServerStateBuilder::new()
} }
pub fn set_sender(&mut self, sender: Box<dyn Fn(ShipMessage) + Send>) {
pub fn set_sender(&mut self, sender: Box<dyn Fn(ShipMessage) + Send + Sync>) {
self.shipgate_sender = Some(sender); self.shipgate_sender = Some(sender);
} }
@ -496,7 +498,7 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> { -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
Ok(match pkt { Ok(match pkt {
RecvShipPacket::Login(login) => { RecvShipPacket::Login(login) => {
Box::new(handler::auth::validate_login(id, login, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager, &self.name).await?.into_iter().map(move |pkt| (id, pkt)))
Box::new(handler::auth::validate_login(id, login, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager, &self.shipgate_sender, &self.name).await?.into_iter().map(move |pkt| (id, pkt)))
}, },
RecvShipPacket::QuestDetailRequest(questdetailrequest) => { RecvShipPacket::QuestDetailRequest(questdetailrequest) => {
match questdetailrequest.menu { match questdetailrequest.menu {
@ -601,7 +603,6 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
} }
async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> { async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
// TODO: don't unwrap!
let client = self.clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let client = self.clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
let area_client = self.client_location.get_local_client(id)?; let area_client = self.client_location.get_local_client(id)?;
let neighbors = self.client_location.get_client_neighbors(id)?; let neighbors = self.client_location.get_client_neighbors(id)?;
@ -611,15 +612,19 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
if neighbors.len() == 0 { if neighbors.len() == 0 {
self.rooms[room.0] = None; self.rooms[room.0] = None;
} }
let leader = self.client_location.get_room_leader(room).unwrap();
let leader = self.client_location.get_room_leader(room)?;
SendShipPacket::LeaveRoom(LeaveRoom::new(area_client.local_client.id(), leader.local_client.id())) SendShipPacket::LeaveRoom(LeaveRoom::new(area_client.local_client.id(), leader.local_client.id()))
}, },
RoomLobby::Lobby(lobby) => { RoomLobby::Lobby(lobby) => {
let leader = self.client_location.get_lobby_leader(lobby).unwrap();
let leader = self.client_location.get_lobby_leader(lobby)?;
SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())) SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()))
} }
}; };
if let Some(shipgate_sender) = self.shipgate_sender.as_ref() {
shipgate_sender(ShipMessage::RemoveUser(client.user.id.clone()));
}
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);
@ -652,8 +657,23 @@ impl<EG: EntityGateway> InterserverActor for ShipServerState<EG> {
})) ] })) ]
} }
async fn action(&mut self, _id: ServerId, _msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
async fn action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
match msg {
LoginMessage::SendMail{..} => {
Ok(Vec::new())
},
LoginMessage::ShipList{ships} => {
self.ship_list = ships;
Ok(Vec::new()) Ok(Vec::new())
},
LoginMessage::RequestUsers => {
Ok(self.clients.iter()
.map(|(_, client)| {
(id, ShipMessage::AddUser(client.user.id.clone()))
})
.collect())
}
}
} }
async fn on_disconnect(&mut self, _id: ServerId) -> Vec<(ServerId, Self::SendMessage)> { async fn on_disconnect(&mut self, _id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {

Loading…
Cancel
Save