start on misc interserver things like player counts and ship lists
This commit is contained in:
parent
6686aee824
commit
ae7ba9e3fe
@ -8,7 +8,7 @@ pub struct ServerId(pub usize);
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct AuthToken(pub String);
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Ship {
|
||||
pub name: String,
|
||||
pub ip: Ipv4Addr,
|
||||
@ -38,7 +38,8 @@ pub enum ShipMessage {
|
||||
message: String,
|
||||
},
|
||||
RequestShipList,
|
||||
|
||||
AddUser(UserAccountId),
|
||||
RemoveUser(UserAccountId),
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
interserver_send_loop(server_id, socket.clone(), client_receiver).await;
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use libpso::character::settings;
|
||||
use libpso::character::guildcard;
|
||||
|
||||
pub const USERFLAG_NEWCHAR: u32 = 0x00000001;
|
||||
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);
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct UserSettingsId(pub u32);
|
||||
|
@ -6,6 +6,7 @@ use rand::Rng;
|
||||
use crc::{crc32, Hasher32};
|
||||
|
||||
use libpso::packet::login::*;
|
||||
use libpso::packet::ship::{MenuDetail, SmallLeftDialog};
|
||||
use libpso::{PacketParseError, PSOPacket};
|
||||
use libpso::crypto::bb::PSOBBCipher;
|
||||
use crate::entity::item;
|
||||
@ -18,7 +19,7 @@ use crate::common::leveltable::CharacterLevelTable;
|
||||
use libpso::{utf8_to_array, utf8_to_utf16_array};
|
||||
|
||||
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::weapon::Weapon;
|
||||
use crate::entity::item::armor::Armor;
|
||||
@ -57,6 +58,7 @@ pub enum RecvCharacterPacket {
|
||||
CharacterPreview(CharacterPreview),
|
||||
SetFlag(SetFlag),
|
||||
MenuSelect(MenuSelect),
|
||||
MenuDetail(MenuDetail),
|
||||
}
|
||||
|
||||
impl RecvServerPacket for RecvCharacterPacket {
|
||||
@ -73,6 +75,7 @@ impl RecvServerPacket for RecvCharacterPacket {
|
||||
0xE5 => Ok(RecvCharacterPacket::CharacterPreview(CharacterPreview::from_bytes(data)?)),
|
||||
0xEC => Ok(RecvCharacterPacket::SetFlag(SetFlag::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()))
|
||||
}
|
||||
}
|
||||
@ -94,6 +97,7 @@ pub enum SendCharacterPacket {
|
||||
Timestamp(Timestamp),
|
||||
ShipList(ShipList),
|
||||
RedirectClient(RedirectClient),
|
||||
SmallLeftDialog(SmallLeftDialog),
|
||||
}
|
||||
|
||||
impl SendServerPacket for SendCharacterPacket {
|
||||
@ -112,6 +116,7 @@ impl SendServerPacket for SendCharacterPacket {
|
||||
SendCharacterPacket::Timestamp(pkt) => pkt.as_bytes(),
|
||||
SendCharacterPacket::ShipList(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> {
|
||||
entity_gateway: EG,
|
||||
@ -177,6 +187,8 @@ pub struct CharacterServerState<EG: EntityGateway> {
|
||||
ships: BTreeMap<ServerId, Ship>,
|
||||
level_table: CharacterLevelTable,
|
||||
auth_token: AuthToken,
|
||||
|
||||
connected_clients: BTreeMap<UserAccountId, ConnectedClient>,
|
||||
authenticated_ships: BTreeSet<ServerId>,
|
||||
ship_sender: BTreeMap<ServerId, Box<dyn Fn(LoginMessage) + Send>>,
|
||||
}
|
||||
@ -298,6 +310,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
auth_token: auth_token,
|
||||
authenticated_ships: BTreeSet::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> {
|
||||
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());
|
||||
response.guildcard = user.guildcard;
|
||||
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.session = pkt.session;
|
||||
vec![SendCharacterPacket::LoginResponse(response)]
|
||||
Ok(vec![SendCharacterPacket::LoginResponse(response)])
|
||||
},
|
||||
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> {
|
||||
@ -493,6 +518,16 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
.ok_or(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?;
|
||||
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]
|
||||
@ -557,6 +592,12 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
|
||||
},
|
||||
RecvCharacterPacket::MenuSelect(menuselect) => {
|
||||
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 {
|
||||
self.authenticated_ships.insert(id);
|
||||
}
|
||||
Ok(Vec::new())
|
||||
},
|
||||
ShipMessage::NewShip(new_ship) => {
|
||||
if self.authenticated_ships.contains(&id) {
|
||||
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())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {
|
||||
self.ships.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()
|
||||
}
|
||||
}
|
||||
|
@ -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::entity::gateway::EntityGateway;
|
||||
use crate::ship::items::ItemManager;
|
||||
use crate::common::interserver::ShipMessage;
|
||||
|
||||
pub async fn validate_login<EG: EntityGateway>(id: ClientId,
|
||||
pkt: &Login,
|
||||
entity_gateway: &mut EG,
|
||||
clients: &mut Clients,
|
||||
item_manager: &mut ItemManager,
|
||||
shipgate_sender: &Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
|
||||
ship_name: &String)
|
||||
-> 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) => {
|
||||
user.at_ship= true;
|
||||
entity_gateway.save_user(&user).await?;
|
||||
|
||||
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
|
||||
response.guildcard = user.id.0 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?;
|
||||
|
||||
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));
|
||||
vec![SendShipPacket::LoginResponse(response), SendShipPacket::ShipBlockList(ShipBlockList::new(&&ship_name, 3))]
|
||||
},
|
||||
|
@ -351,6 +351,7 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
|
||||
port: self.port.unwrap_or(SHIP_PORT),
|
||||
shops: Box::new(ItemShops::new()),
|
||||
auth_token: self.auth_token.unwrap_or(AuthToken("".into())),
|
||||
ship_list: Vec::new(),
|
||||
shipgate_sender: None,
|
||||
}
|
||||
}
|
||||
@ -370,7 +371,8 @@ pub struct ShipServerState<EG: EntityGateway> {
|
||||
shops: Box<ItemShops>,
|
||||
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> {
|
||||
@ -378,7 +380,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -496,7 +498,7 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
Ok(match pkt {
|
||||
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) => {
|
||||
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> {
|
||||
// TODO: don't unwrap!
|
||||
let client = self.clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let area_client = self.client_location.get_local_client(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 {
|
||||
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()))
|
||||
},
|
||||
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()))
|
||||
}
|
||||
};
|
||||
|
||||
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.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())
|
||||
},
|
||||
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)> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user