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> { | ||||
| @ -490,9 +515,19 @@ impl<EG: EntityGateway> CharacterServerState<EG> { | ||||
|         } | ||||
| 
 | ||||
|         let ship = self.ships.get(&ServerId(menuselect.item as usize)) | ||||
|                              .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))]) | ||||
|     } | ||||
| 
 | ||||
|     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()) | ||||
|             }, | ||||
|             _ => {} | ||||
|         } | ||||
|         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> { | ||||
|         Ok(Vec::new()) | ||||
|     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