From 75260c40a04fe618fe622ab889982660035f48a5 Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 16 Nov 2020 23:05:28 -0700 Subject: [PATCH 01/13] improve error handling in new character --- src/login/character.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/login/character.rs b/src/login/character.rs index 82434f3..9443c8f 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -17,7 +17,7 @@ use crate::common::interserver::{ServerId, InterserverActor, LoginMessage, ShipM use crate::common::leveltable::CharacterLevelTable; use libpso::{utf8_to_array, utf8_to_utf16_array}; -use crate::entity::gateway::EntityGateway; +use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::account::{UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM}; use crate::entity::item::{NewItemEntity, ItemDetail, ItemLocation, InventoryItemEntity, InventoryEntity, BankEntity, BankName, EquippedEntity}; use crate::entity::item::weapon::Weapon; @@ -41,7 +41,7 @@ pub enum CharacterError { CouldNotLoadSettings, CouldNotLoadCharacters, CouldNotLoadGuildcard, - DbError, + GatewayError(#[from] GatewayError), } #[derive(Debug)] @@ -181,7 +181,7 @@ pub struct CharacterServerState { } -async fn new_character(entity_gateway: &mut EG, user: &UserAccountEntity, preview: &CharacterPreview) { +async fn new_character(entity_gateway: &mut EG, user: &UserAccountEntity, preview: &CharacterPreview) -> Result<(), anyhow::Error> { let mut character = new_character_from_preview(user, preview); match character.char_class { CharacterClass::FOmar | CharacterClass::FOmarl| CharacterClass::FOnewm | CharacterClass::FOnewearl => character.techs.set_tech(Technique::Foie, TechLevel(1)), @@ -189,7 +189,7 @@ async fn new_character(entity_gateway: &mut EG, user: &UserAc } character.meseta = 300; - let character = entity_gateway.create_character(character).await.unwrap(); + let character = entity_gateway.create_character(character).await?; let new_weapon = match character.char_class { CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber, @@ -211,7 +211,7 @@ async fn new_character(entity_gateway: &mut EG, user: &UserAc }), location: ItemLocation::Inventory { character_id: character.id, - }}).await.unwrap(); + }}).await?; let armor = entity_gateway.create_item( NewItemEntity { @@ -225,7 +225,7 @@ async fn new_character(entity_gateway: &mut EG, user: &UserAc }), location: ItemLocation::Inventory { character_id: character.id, - }}).await.unwrap(); + }}).await?; let mut mag = { if character.char_class.is_android() { @@ -240,7 +240,7 @@ async fn new_character(entity_gateway: &mut EG, user: &UserAc item: ItemDetail::Mag(mag), location: ItemLocation::Inventory { character_id: character.id, - }}).await.unwrap(); + }}).await?; let mut monomates = Vec::new(); for _ in 0..4usize { @@ -252,7 +252,7 @@ async fn new_character(entity_gateway: &mut EG, user: &UserAc }), location: ItemLocation::Inventory { character_id: character.id, - }}).await.unwrap()) + }}).await?) } let mut monofluids = Vec::new(); @@ -265,20 +265,21 @@ async fn new_character(entity_gateway: &mut EG, user: &UserAc }), location: ItemLocation::Inventory { character_id: character.id, - }}).await.unwrap()) + }}).await?) } let inventory = InventoryEntity { items: vec![InventoryItemEntity::Individual(weapon.clone()), InventoryItemEntity::Individual(armor.clone()), InventoryItemEntity::Individual(mag.clone()), InventoryItemEntity::Stacked(monomates), InventoryItemEntity::Stacked(monofluids)], }; - entity_gateway.set_character_inventory(&character.id, &inventory).await; - entity_gateway.set_character_bank(&character.id, &BankEntity::default(), BankName("".into())).await; + entity_gateway.set_character_inventory(&character.id, &inventory).await?; + entity_gateway.set_character_bank(&character.id, &BankEntity::default(), BankName("".into())).await?; let mut equipped = EquippedEntity::default(); equipped.weapon = Some(weapon.id); equipped.armor = Some(armor.id); equipped.mag = Some(mag.id); - entity_gateway.set_character_equips(&character.id, &equipped).await; + entity_gateway.set_character_equips(&character.id, &equipped).await?; + Ok(()) } @@ -303,7 +304,7 @@ impl CharacterServerState { 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.map_err(|_| CharacterError::DbError)?; + self.entity_gateway.save_user(&user).await?; let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new()); response.guildcard = user.guildcard; @@ -457,7 +458,7 @@ impl CharacterServerState { let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?; let mut user = client.user.as_mut().unwrap(); if user.flags == USERFLAG_NEWCHAR { - new_character(&mut self.entity_gateway, &user, preview).await + new_character(&mut self.entity_gateway, &user, preview).await? } if user.flags == USERFLAG_DRESSINGROOM { // TODO: dressing room stuff From a3db5b65e2738674d140ceb81ff4ae8e2955fe54 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 17 Nov 2020 18:25:01 -0700 Subject: [PATCH 02/13] add ability for servers to manually send messages to eachother --- src/common/mainloop/interserver.rs | 24 ++++++++++++++++++++---- src/login/character.rs | 7 +++++++ src/ship/ship.rs | 7 +++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/common/mainloop/interserver.rs b/src/common/mainloop/interserver.rs index 34143a9..8c42f23 100644 --- a/src/common/mainloop/interserver.rs +++ b/src/common/mainloop/interserver.rs @@ -186,7 +186,7 @@ pub fn login_listen_mainloop(state: Arc(state: Arc(state: Arc(state: Arc>>, ip: std::net::Ipv4Addr, port: u16) -> Pin>> { Box::pin(async_std::task::spawn(async move { let mut id = 0; - let (server_state_sender, server_state_receiver) = async_std::sync::channel(1024); - interserver_state_loop(state, server_state_receiver).await; + + interserver_state_loop(state.clone(), server_state_receiver).await; loop { info!("trying to connect to loginserv"); @@ -225,6 +233,14 @@ pub fn ship_connect_mainloop(state: Arc { level_table: CharacterLevelTable, auth_token: AuthToken, authenticated_ships: BTreeSet, + ship_sender: BTreeMap>, } @@ -296,9 +297,14 @@ impl CharacterServerState { level_table: CharacterLevelTable::new(), auth_token: auth_token, authenticated_ships: BTreeSet::new(), + ship_sender: BTreeMap::new(), } } + pub fn set_sender(&mut self, server_id: ServerId, sender: Box) { + self.ship_sender.insert(server_id, sender); + } + async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result, 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) { @@ -595,6 +601,7 @@ impl InterserverActor for CharacterServerState { async fn on_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> { self.ships.remove(&id); + self.ship_sender.remove(&id); Vec::new() } } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index a0aa5d3..93a5487 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -351,6 +351,7 @@ impl ShipServerStateBuilder { port: self.port.unwrap_or(SHIP_PORT), shops: Box::new(ItemShops::new()), auth_token: self.auth_token.unwrap_or(AuthToken("".into())), + shipgate_sender: None, } } } @@ -368,6 +369,8 @@ pub struct ShipServerState { port: u16, shops: Box, auth_token: AuthToken, + + shipgate_sender: Option>, } impl ShipServerState { @@ -375,6 +378,10 @@ impl ShipServerState { ShipServerStateBuilder::new() } + pub fn set_sender(&mut self, sender: Box) { + self.shipgate_sender = Some(sender); + } + async fn message(&mut self, id: ClientId, msg: &Message) -> Result + Send>, anyhow::Error> { Ok(match &msg.msg { GameMessage::RequestExp(request_exp) => { From 6686aee82453205f279b85441df62d012c11c303 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 17 Nov 2020 19:02:56 -0700 Subject: [PATCH 03/13] fix dumb thing --- src/common/mainloop/interserver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/mainloop/interserver.rs b/src/common/mainloop/interserver.rs index 8c42f23..e788993 100644 --- a/src/common/mainloop/interserver.rs +++ b/src/common/mainloop/interserver.rs @@ -199,7 +199,7 @@ pub fn login_listen_mainloop(state: Arc Date: Wed, 18 Nov 2020 18:56:04 -0700 Subject: [PATCH 04/13] start on misc interserver things like player counts and ship lists --- src/common/interserver.rs | 5 +- src/common/mainloop/interserver.rs | 1 - src/entity/account.rs | 3 +- src/login/character.rs | 104 +++++++++++++++++++++++++---- src/ship/packet/handler/auth.rs | 11 +-- src/ship/ship.rs | 36 +++++++--- 6 files changed, 132 insertions(+), 28 deletions(-) diff --git a/src/common/interserver.rs b/src/common/interserver.rs index bd09913..cddaba7 100644 --- a/src/common/interserver.rs +++ b/src/common/interserver.rs @@ -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), } diff --git a/src/common/mainloop/interserver.rs b/src/common/mainloop/interserver.rs index e788993..7e4b95d 100644 --- a/src/common/mainloop/interserver.rs +++ b/src/common/mainloop/interserver.rs @@ -204,7 +204,6 @@ pub fn login_listen_mainloop(state: Arc 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, + expires: Option>, +} pub struct CharacterServerState { entity_gateway: EG, @@ -177,6 +187,8 @@ pub struct CharacterServerState { ships: BTreeMap, level_table: CharacterLevelTable, auth_token: AuthToken, + + connected_clients: BTreeMap, authenticated_ships: BTreeSet, ship_sender: BTreeMap>, } @@ -298,6 +310,7 @@ impl CharacterServerState { auth_token: auth_token, authenticated_ships: BTreeSet::new(), ship_sender: BTreeMap::new(), + connected_clients: BTreeMap::new(), } } @@ -306,23 +319,35 @@ impl CharacterServerState { } async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result, 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, anyhow::Error> { @@ -490,9 +515,19 @@ impl CharacterServerState { } 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, 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 ServerState for CharacterServerState { }, 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 InterserverActor for CharacterServerState { 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() } } diff --git a/src/ship/packet/handler/auth.rs b/src/ship/packet/handler/auth.rs index 16228c5..b691f25 100644 --- a/src/ship/packet/handler/auth.rs +++ b/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::entity::gateway::EntityGateway; use crate::ship::items::ItemManager; +use crate::common::interserver::ShipMessage; pub async fn validate_login(id: ClientId, pkt: &Login, entity_gateway: &mut EG, clients: &mut Clients, item_manager: &mut ItemManager, + shipgate_sender: &Option>, ship_name: &String) -> Result, 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(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))] }, diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 93a5487..6651999 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -351,6 +351,7 @@ impl ShipServerStateBuilder { 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 { shops: Box, auth_token: AuthToken, - shipgate_sender: Option>, + ship_list: Vec, + shipgate_sender: Option>, } impl ShipServerState { @@ -378,7 +380,7 @@ impl ShipServerState { ShipServerStateBuilder::new() } - pub fn set_sender(&mut self, sender: Box) { + pub fn set_sender(&mut self, sender: Box) { self.shipgate_sender = Some(sender); } @@ -496,7 +498,7 @@ impl ServerState for ShipServerState { -> Result + 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 ServerState for ShipServerState { } async fn on_disconnect(&mut self, id: ClientId) -> Result, 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 ServerState for ShipServerState { 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 InterserverActor for ShipServerState { })) ] } - async fn action(&mut self, _id: ServerId, _msg: Self::RecvMessage) -> Result, Self::Error> { - Ok(Vec::new()) + async fn action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result, 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)> { From ffce662c95f1263fbd6d581a33923debcab73c51 Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 21 Nov 2020 23:45:27 -0700 Subject: [PATCH 05/13] transfer ship --- src/login/character.rs | 15 +++++++++++---- src/ship/packet/builder/mod.rs | 1 + src/ship/packet/builder/ship.rs | 29 +++++++++++++++++++++++++++++ src/ship/packet/handler/mod.rs | 1 + src/ship/packet/handler/ship.rs | 23 +++++++++++++++++++++++ src/ship/ship.rs | 24 ++++++++++++++++++++++-- 6 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 src/ship/packet/builder/ship.rs create mode 100644 src/ship/packet/handler/ship.rs diff --git a/src/login/character.rs b/src/login/character.rs index 57b0bee..895ce7e 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -32,7 +32,7 @@ use crate::login::login::{get_login_status, check_if_already_online}; use crate::common::interserver::AuthToken; pub const CHARACTER_PORT: u16 = 12001; -const SHIP_MENU_ID: u32 = 1; +pub const SHIP_MENU_ID: u32 = 1; #[derive(thiserror::Error, Debug)] #[error("")] @@ -173,7 +173,7 @@ impl ClientState { } } -#[derive(Clone)] +#[derive(Debug, Clone)] struct ConnectedClient { ship_id: Option, expires: Option>, @@ -591,7 +591,7 @@ impl ServerState for CharacterServerState { Box::new(self.character_preview(id, preview).await?.into_iter().map(move |pkt| (id, pkt))) }, RecvCharacterPacket::MenuSelect(menuselect) => { - Box::new(self.select_ship(menuselect)?.into_iter().map(move |pkt| (id, pkt))) + Box::new(self.select_ship(id, menuselect)?.into_iter().map(move |pkt| (id, pkt))) }, RecvCharacterPacket::MenuDetail(menudetail) => { match menudetail.menu { @@ -635,7 +635,14 @@ impl InterserverActor for CharacterServerState { if self.authenticated_ships.contains(&id) { self.ships.insert(id, new_ship); } - Ok(Vec::new()) + + let ships = self.ships.iter().map(|(_, s)| s).cloned().collect::>(); + Ok(self.ships + .iter() + .map(|(id, _)| { + (*id, LoginMessage::ShipList{ ships: ships.clone() }) + }) + .collect()) }, ShipMessage::AddUser(new_user) => { if self.authenticated_ships.contains(&id) { diff --git a/src/ship/packet/builder/mod.rs b/src/ship/packet/builder/mod.rs index 2ce4837..b1fa664 100644 --- a/src/ship/packet/builder/mod.rs +++ b/src/ship/packet/builder/mod.rs @@ -2,6 +2,7 @@ pub mod lobby; pub mod message; pub mod room; pub mod quest; +pub mod ship; use libpso::character::character::Inventory; use libpso::packet::ship::{PlayerHeader, PlayerInfo}; diff --git a/src/ship/packet/builder/ship.rs b/src/ship/packet/builder/ship.rs new file mode 100644 index 0000000..c80d562 --- /dev/null +++ b/src/ship/packet/builder/ship.rs @@ -0,0 +1,29 @@ +use libpso::packet::login::{ShipList, ShipListEntry}; +use libpso::packet::ship::*; +use crate::common::serverstate::ClientId; +use crate::common::leveltable::CharacterLevelTable; +use crate::ship::ship::{ShipError, ClientState, Clients}; +use crate::ship::location::{ClientLocation, RoomId, AreaClient, ClientLocationError}; +use crate::ship::room::RoomState; +use crate::ship::items::ItemManager; +use crate::ship::packet::builder::{player_header, player_info}; +use libpso::utf8_to_utf16_array; + +use crate::common::interserver::Ship; +use libpso::packet::ship::BLOCK_MENU_ID; +use crate::login::character::SHIP_MENU_ID; + +pub fn ship_list(ships: &Vec) -> ShipList { + let ships = ships.iter() + .enumerate() + .map(|(i, ship)| { + ShipListEntry { + menu: SHIP_MENU_ID, + item: i as u32, + flags: 0, + name: utf8_to_utf16_array!(ship.name, 0x11) + } + }) + .collect(); + ShipList::new(ships) +} diff --git a/src/ship/packet/handler/mod.rs b/src/ship/packet/handler/mod.rs index 192c8ab..02761f8 100644 --- a/src/ship/packet/handler/mod.rs +++ b/src/ship/packet/handler/mod.rs @@ -6,3 +6,4 @@ pub mod message; pub mod room; pub mod settings; pub mod quest; +pub mod ship; diff --git a/src/ship/packet/handler/ship.rs b/src/ship/packet/handler/ship.rs new file mode 100644 index 0000000..9916901 --- /dev/null +++ b/src/ship/packet/handler/ship.rs @@ -0,0 +1,23 @@ +use libpso::packet::ship::*; +use libpso::packet::login::RedirectClient; +use crate::common::serverstate::ClientId; +use crate::common::interserver::Ship; +use crate::ship::ship::{SendShipPacket, ShipError, Clients}; +use crate::ship::packet::builder; + +pub fn ship_list(id: ClientId, ship_list: &Vec) + -> Box + Send> { + Box::new(vec![(id, SendShipPacket::ShipList(builder::ship::ship_list(ship_list)))].into_iter()) +} + +pub fn block_list(id: ClientId, shipname: &str, num_blocks: usize) + -> Box + Send> { + Box::new(vec![(id, SendShipPacket::ShipBlockList(ShipBlockList::new(shipname, num_blocks)))].into_iter()) +} + +pub fn selected_ship(id: ClientId, menuselect: &MenuSelect, ship_list: &Vec) + -> Result + Send>, ShipError> { + let ship = ship_list.get(menuselect.item as usize).ok_or(ShipError::InvalidShip(menuselect.item as usize))?; + let ip = u32::from_ne_bytes(ship.ip.octets()); + Ok(Box::new(vec![(id, SendShipPacket::RedirectClient(RedirectClient::new(ip, ship.port)))].into_iter())) +} diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 6651999..89d3015 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -6,7 +6,7 @@ use rand::Rng; use thiserror::Error; use libpso::packet::ship::*; -use libpso::packet::login::{Login, LoginResponse, Session}; +use libpso::packet::login::{RedirectClient, Login, LoginResponse, Session, ShipList}; use libpso::packet::messages::*; use libpso::{PacketParseError, PSOPacket}; use libpso::crypto::bb::PSOBBCipher; @@ -18,6 +18,8 @@ use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState use crate::common::leveltable::CharacterLevelTable; use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage}; +use crate::login::character::SHIP_MENU_ID; + use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::account::{UserAccountEntity, UserSettingsEntity}; use crate::entity::character::{CharacterEntity, SectionID}; @@ -67,6 +69,7 @@ pub enum ShipError { ShopError, GatewayError(#[from] GatewayError), UnknownMonster(crate::ship::monster::MonsterType), + InvalidShip(usize), } #[derive(Debug)] @@ -98,6 +101,8 @@ pub enum RecvShipPacket { DoneLoadingQuest(DoneLoadingQuest), FullCharacterData(Box), SaveOptions(SaveOptions), + RequestShipList(RequestShipList), + RequestShipBlockList(RequestShipBlockList), } impl RecvServerPacket for RecvShipPacket { @@ -131,6 +136,8 @@ impl RecvServerPacket for RecvShipPacket { 0x6F => Ok(RecvShipPacket::DoneBursting(DoneBursting::from_bytes(data)?)), 0x16F => Ok(RecvShipPacket::DoneBursting2(DoneBursting2::from_bytes(data)?)), 0x84 => Ok(RecvShipPacket::LobbySelect(LobbySelect::from_bytes(data)?)), + 0xA0 => Ok(RecvShipPacket::RequestShipList(RequestShipList::from_bytes(data)?)), + 0xA1 => Ok(RecvShipPacket::RequestShipBlockList(RequestShipBlockList::from_bytes(data)?)), 0xA2 => Ok(RecvShipPacket::RequestQuestList(RequestQuestList::from_bytes(data)?)), 0xAC => Ok(RecvShipPacket::DoneLoadingQuest(DoneLoadingQuest::from_bytes(data)?)), 0xE7 => Ok(RecvShipPacket::FullCharacterData(Box::new(FullCharacterData::from_bytes(data)?))), @@ -144,6 +151,7 @@ impl RecvServerPacket for RecvShipPacket { pub enum SendShipPacket { ShipWelcome(ShipWelcome), LoginResponse(LoginResponse), + ShipList(ShipList), ShipBlockList(ShipBlockList), FullCharacter(Box), CharDataRequest(CharDataRequest), @@ -172,6 +180,7 @@ pub enum SendShipPacket { QuestChunk(QuestChunk), DoneLoadingQuest(DoneLoadingQuest), BankItemList(BankItemList), + RedirectClient(RedirectClient), } impl SendServerPacket for SendShipPacket { @@ -179,6 +188,7 @@ impl SendServerPacket for SendShipPacket { match self { SendShipPacket::ShipWelcome(pkt) => pkt.as_bytes(), SendShipPacket::LoginResponse(pkt) => pkt.as_bytes(), + SendShipPacket::ShipList(pkt) => pkt.as_bytes(), SendShipPacket::ShipBlockList(pkt) => pkt.as_bytes(), SendShipPacket::FullCharacter(pkt) => pkt.as_bytes(), SendShipPacket::CharDataRequest(pkt) => pkt.as_bytes(), @@ -207,6 +217,7 @@ impl SendServerPacket for SendShipPacket { SendShipPacket::QuestChunk(pkt) => pkt.as_bytes(), SendShipPacket::DoneLoadingQuest(pkt) => pkt.as_bytes(), SendShipPacket::BankItemList(pkt) => pkt.as_bytes(), + SendShipPacket::RedirectClient(pkt) => pkt.as_bytes(), } } } @@ -508,6 +519,7 @@ impl ServerState for ShipServerState { }, RecvShipPacket::MenuSelect(menuselect) => { match menuselect.menu { + SHIP_MENU_ID => handler::ship::selected_ship(id, menuselect, &self.ship_list)?, BLOCK_MENU_ID => Box::new(handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_manager, &self.level_table)?.into_iter().map(move |pkt| (id, pkt))), ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut self.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut self.rooms)?, QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &self.quests)?, @@ -599,6 +611,12 @@ impl ServerState for ShipServerState { RecvShipPacket::SaveOptions(save_options) => { handler::settings::save_options(id, save_options, &mut self.clients, &mut self.entity_gateway).await }, + RecvShipPacket::RequestShipList(_) => { + handler::ship::ship_list(id, &self.ship_list) + }, + RecvShipPacket::RequestShipBlockList(_) => { + handler::ship::block_list(id, &self.name, 1) + } }) } @@ -654,7 +672,9 @@ impl InterserverActor for ShipServerState { ip: self.ip.clone(), port: self.port, block_count: 2, - })) ] + })), + (id, ShipMessage::RequestShipList) + ] } async fn action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result, Self::Error> { From 26be2757c9d7fddfdc077fab4e98dec022a23053 Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 21 Nov 2020 23:48:02 -0700 Subject: [PATCH 06/13] add use --- src/common/interserver.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/interserver.rs b/src/common/interserver.rs index cddaba7..60b5aa5 100644 --- a/src/common/interserver.rs +++ b/src/common/interserver.rs @@ -1,6 +1,7 @@ use std::net::Ipv4Addr; use serde::{Serialize, Deserialize}; use serde::de::DeserializeOwned; +use crate::entity::account::UserAccountId; use crate::entity::character::CharacterEntityId; #[derive(Debug, Copy, Clone, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)] From 5ca852260f6b8d89fa59860487dcc4bb17c3bae8 Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 21 Nov 2020 23:50:10 -0700 Subject: [PATCH 07/13] only 1 block actually exists --- src/ship/packet/handler/auth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ship/packet/handler/auth.rs b/src/ship/packet/handler/auth.rs index b691f25..bc3cc59 100644 --- a/src/ship/packet/handler/auth.rs +++ b/src/ship/packet/handler/auth.rs @@ -34,7 +34,7 @@ pub async fn validate_login(id: ClientId, 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))] + vec![SendShipPacket::LoginResponse(response), SendShipPacket::ShipBlockList(ShipBlockList::new(&&ship_name, 1))] }, Err(err) => { vec![SendShipPacket::LoginResponse(LoginResponse::by_status(err, Session::new()))] From 6f5aa5119f55d721772a3d13dcd794aac9ad400f Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 21 Nov 2020 23:53:01 -0700 Subject: [PATCH 08/13] login requests users from ship --- src/common/interserver.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/interserver.rs b/src/common/interserver.rs index 60b5aa5..db2f6a1 100644 --- a/src/common/interserver.rs +++ b/src/common/interserver.rs @@ -26,7 +26,8 @@ pub enum LoginMessage { }, ShipList { ships: Vec, - } + }, + RequestUsers, } #[derive(Debug, Serialize, Deserialize)] From 78ed2101e56f0c4efeb11e64c4d6f5fd165dea50 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 24 Nov 2020 23:17:42 -0700 Subject: [PATCH 09/13] I forgot blocks are a thing --- src/ship/location.rs | 35 ++++--- src/ship/packet/handler/auth.rs | 17 ++-- src/ship/packet/handler/lobby.rs | 2 +- src/ship/ship.rs | 168 +++++++++++++++++++++---------- 4 files changed, 146 insertions(+), 76 deletions(-) diff --git a/src/ship/location.rs b/src/ship/location.rs index 2248d5a..8b36055 100644 --- a/src/ship/location.rs +++ b/src/ship/location.rs @@ -133,15 +133,18 @@ pub struct ClientLocation { client_location: HashMap, } -impl ClientLocation { - pub fn new() -> ClientLocation { +impl Default for ClientLocation { + fn default() -> ClientLocation { ClientLocation { lobbies: [Lobby([None; 12]); 15], rooms: [None; MAX_ROOMS], client_location: HashMap::new(), } } +} + +impl ClientLocation { pub fn add_client_to_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result<(), JoinLobbyError> { let l = self.lobbies.get_mut(lobby.0).ok_or(JoinLobbyError::LobbyDoesNotExist)?; let (index, empty_slot) = l.0.iter_mut() @@ -389,7 +392,7 @@ mod test { #[test] fn test_add_client_to_lobby() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); cl.add_client_to_lobby(ClientId(12), LobbyId(0)); cl.add_client_to_lobby(ClientId(13), LobbyId(1)); cl.add_client_to_lobby(ClientId(14), LobbyId(0)); @@ -402,7 +405,7 @@ mod test { #[test] fn test_add_client_to_full_lobby() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); (0..12).for_each(|i| { cl.add_client_to_lobby(ClientId(i), LobbyId(0)); }); @@ -411,7 +414,7 @@ mod test { #[test] fn test_add_client_to_next_available_lobby() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); (1..4).for_each(|lobby| { (0..12).for_each(|i| { cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)); @@ -422,7 +425,7 @@ mod test { #[test] fn test_add_to_lobby_when_all_are_full() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); (0..15).for_each(|lobby| { (0..12).for_each(|i| { cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)); @@ -433,13 +436,13 @@ mod test { #[test] fn test_new_room() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); assert!(cl.create_new_room(ClientId(12)) == Ok(RoomId(0))); } #[test] fn test_add_client_to_room() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); let room = cl.create_new_room(ClientId(12)).unwrap(); assert!(cl.add_client_to_room(ClientId(234), room) == Ok(())); assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ @@ -450,7 +453,7 @@ mod test { #[test] fn test_no_new_room_slots() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); for i in 0..128 { cl.create_new_room(ClientId(i)); } @@ -459,7 +462,7 @@ mod test { #[test] fn test_joining_full_room() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); let room = cl.create_new_room(ClientId(0)).unwrap(); assert!(cl.add_client_to_room(ClientId(1), room) == Ok(())); assert!(cl.add_client_to_room(ClientId(2), room) == Ok(())); @@ -469,7 +472,7 @@ mod test { #[test] fn test_adding_client_to_room_removes_from_lobby() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); cl.add_client_to_lobby(ClientId(93), LobbyId(0)); cl.add_client_to_lobby(ClientId(23), LobbyId(0)); cl.add_client_to_lobby(ClientId(51), LobbyId(0)); @@ -489,7 +492,7 @@ mod test { #[test] fn test_getting_neighbors() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); cl.add_client_to_lobby(ClientId(93), LobbyId(0)); cl.add_client_to_lobby(ClientId(23), LobbyId(0)); cl.add_client_to_lobby(ClientId(51), LobbyId(0)); @@ -504,7 +507,7 @@ mod test { #[test] fn test_failing_to_join_lobby_does_not_remove_from_current_area() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); (0..12).for_each(|i| { cl.add_client_to_lobby(ClientId(i), LobbyId(0)); }); @@ -518,7 +521,7 @@ mod test { #[test] fn test_get_leader() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); cl.add_client_to_lobby(ClientId(93), LobbyId(0)); cl.add_client_to_lobby(ClientId(23), LobbyId(0)); cl.add_client_to_lobby(ClientId(51), LobbyId(0)); @@ -529,7 +532,7 @@ mod test { #[test] fn test_remove_client_from_room() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); let room = cl.create_new_room(ClientId(51)).unwrap(); cl.add_client_to_room(ClientId(93), room); cl.add_client_to_room(ClientId(23), room); @@ -545,7 +548,7 @@ mod test { #[test] fn test_leader_changes_on_leader_leaving() { - let mut cl = ClientLocation::new(); + let mut cl = ClientLocation::default(); let room = cl.create_new_room(ClientId(51)).unwrap(); cl.add_client_to_room(ClientId(93), room); cl.add_client_to_room(ClientId(23), room); diff --git a/src/ship/packet/handler/auth.rs b/src/ship/packet/handler/auth.rs index bc3cc59..23f261d 100644 --- a/src/ship/packet/handler/auth.rs +++ b/src/ship/packet/handler/auth.rs @@ -8,15 +8,16 @@ use crate::ship::items::ItemManager; use crate::common::interserver::ShipMessage; pub async fn validate_login(id: ClientId, - pkt: &Login, - entity_gateway: &mut EG, - clients: &mut Clients, - item_manager: &mut ItemManager, - shipgate_sender: &Option>, - ship_name: &String) + pkt: &Login, + entity_gateway: &mut EG, + clients: &mut Clients, + item_manager: &mut ItemManager, + shipgate_sender: &Option>, + ship_name: &String, + num_blocks: usize) -> Result, anyhow::Error> { Ok(match get_login_status(entity_gateway, pkt).await { - Ok(mut user) => { + Ok(user) => { 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; @@ -34,7 +35,7 @@ pub async fn validate_login(id: ClientId, 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, 1))] + vec![SendShipPacket::LoginResponse(response), SendShipPacket::ShipBlockList(ShipBlockList::new(&&ship_name, num_blocks))] }, Err(err) => { vec![SendShipPacket::LoginResponse(LoginResponse::by_status(err, Session::new()))] diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index 4954a27..7570591 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -17,7 +17,7 @@ pub fn block_selected(id: ClientId, level_table: &CharacterLevelTable) -> Result, anyhow::Error> { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - client.block = pkt.item as u32; + client.block = pkt.item as usize - 1; let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 89d3015..91ec726 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -70,6 +70,7 @@ pub enum ShipError { GatewayError(#[from] GatewayError), UnknownMonster(crate::ship::monster::MonsterType), InvalidShip(usize), + InvalidBlock(usize), } #[derive(Debug)] @@ -242,7 +243,7 @@ pub struct ClientState { pub character: CharacterEntity, session: Session, //guildcard: GuildCard, - pub block: u32, + pub block: usize, pub item_drop_location: Option, pub done_loading_quest: bool, //pub loading_quest: Option, @@ -310,6 +311,7 @@ pub struct ShipServerStateBuilder { ip: Option, port: Option, auth_token: Option, + num_blocks: usize, } impl ShipServerStateBuilder { @@ -320,6 +322,7 @@ impl ShipServerStateBuilder { ip: None, port: None, auth_token: None, + num_blocks: 2, } } @@ -348,19 +351,26 @@ impl ShipServerStateBuilder { self } + pub fn blocks(mut self, num_blocks: usize) -> ShipServerStateBuilder { + self.num_blocks = num_blocks; + self + } + pub fn build(self) -> ShipServerState { + let blocks = std::iter::repeat_with(Block::default).take(self.num_blocks).collect(); // Block doesn't have a Clone impl which limits the easy ways to init this ShipServerState { entity_gateway: self.entity_gateway.unwrap(), clients: HashMap::new(), - client_location: Box::new(ClientLocation::new()), level_table: CharacterLevelTable::new(), name: self.name.unwrap_or("NAMENOTSET".into()), - rooms: Box::new([None; MAX_ROOMS]), item_manager: items::ItemManager::new(), quests: quests::load_quests("data/quests.toml".into()).unwrap(), ip: self.ip.unwrap_or(Ipv4Addr::new(127,0,0,1)), port: self.port.unwrap_or(SHIP_PORT), shops: Box::new(ItemShops::new()), + + blocks: Blocks(blocks), + auth_token: self.auth_token.unwrap_or(AuthToken("".into())), ship_list: Vec::new(), shipgate_sender: None, @@ -368,20 +378,44 @@ impl ShipServerStateBuilder { } } + +struct Block { + client_location: Box, + rooms: Box, +} + +impl Default for Block { + fn default() -> Block { + Block { + client_location: Box::new(ClientLocation::default()), + rooms: Box::new([None; MAX_ROOMS]), + } + } +} + +struct Blocks(Vec); + +impl Blocks { + fn with_client(&mut self, id: ClientId, clients: &Clients) -> Result<&mut Block, ShipError> { + let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; + self.0.get_mut(client.block).ok_or(ShipError::InvalidBlock(client.block)) + } +} + pub struct ShipServerState { entity_gateway: EG, pub clients: Clients, - client_location: Box, level_table: CharacterLevelTable, name: String, - pub rooms: Box, item_manager: items::ItemManager, quests: quests::QuestList, + shops: Box, + blocks: Blocks, + ip: Ipv4Addr, port: u16, - shops: Box, - auth_token: AuthToken, + auth_token: AuthToken, ship_list: Vec, shipgate_sender: Option>, } @@ -398,34 +432,41 @@ impl ShipServerState { async fn message(&mut self, id: ClientId, msg: &Message) -> Result + Send>, anyhow::Error> { Ok(match &msg.msg { GameMessage::RequestExp(request_exp) => { - handler::message::request_exp(id, request_exp, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.rooms, &self.level_table).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::message::request_exp(id, request_exp, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &self.level_table).await? }, GameMessage::PlayerDropItem(player_drop_item) => { - handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? }, GameMessage::DropCoordinates(drop_coordinates) => { - handler::message::drop_coordinates(id, drop_coordinates, &self.client_location, &mut self.clients, &self.rooms)? + let block = self.blocks.with_client(id, &self.clients)?; + handler::message::drop_coordinates(id, drop_coordinates, &block.client_location, &mut self.clients, &block.rooms)? }, GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => { - handler::message::split_item_stack(id, no_longer_has_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::message::split_item_stack(id, no_longer_has_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await? }, GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) | GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) | GameMessage::PlayerLoadedIn(_) | GameMessage::PlayerWalking(_) | GameMessage::PlayerRunning(_) | GameMessage::PlayerWarped(_) | GameMessage::PlayerChangedFloor(_) | GameMessage::InitializeSpeechNpc(_) => { - handler::message::update_player_position(id, &msg, &mut self.clients, &mut self.client_location, &self.rooms)? + let block = self.blocks.with_client(id, &self.clients)?; + handler::message::update_player_position(id, &msg, &mut self.clients, &mut block.client_location, &block.rooms)? }, GameMessage::ChargeAttack(charge_attack) => { handler::message::charge_attack(id, charge_attack, &mut self.clients, &mut self.entity_gateway).await? }, GameMessage::PlayerUseItem(player_use_item) => { - handler::message::use_item(id, player_use_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::message::use_item(id, player_use_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await? }, GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => { handler::message::player_used_medical_center(id, &player_used_medical_center, &mut self.entity_gateway, &mut self.clients).await? }, GameMessage::PlayerFeedMag(player_feed_mag) => { - handler::message::player_feed_mag(id, &player_feed_mag, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::message::player_feed_mag(id, &player_feed_mag, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await? }, GameMessage::PlayerEquipItem(player_equip_item) => { handler::message::player_equips_item(id, &player_equip_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? @@ -438,7 +479,8 @@ impl ShipServerState { }, _ => { let cmsg = msg.clone(); - Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter() + let block = self.blocks.with_client(id, &self.clients)?; + Box::new(block.client_location.get_client_neighbors(id).unwrap().into_iter() .map(move |client| { (client.client, SendShipPacket::Message(cmsg.clone())) })) @@ -450,32 +492,40 @@ impl ShipServerState { let target = msg.flag; Ok(match &msg.msg { GameMessage::GuildcardSend(guildcard_send) => { - handler::direct_message::guildcard_send(id, guildcard_send, target, &self.client_location, &self.clients) + let block = self.blocks.with_client(id, &self.clients)?; + handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients) }, GameMessage::RequestItem(request_item) => { - handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? }, GameMessage::PickupItem(pickup_item) => { - handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await? }, GameMessage::BoxDropRequest(box_drop_request) => { - handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? }, GameMessage::BankRequest(_bank_request) => { handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await? }, GameMessage::BankInteraction(bank_interaction) => { - handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.item_manager).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? }, GameMessage::ShopRequest(shop_request) => { - handler::direct_message::shop_request(id, shop_request, &self.client_location, &mut self.clients, &self.rooms, &self.level_table, &mut self.shops).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &self.level_table, &mut self.shops).await? }, GameMessage::BuyItem(buy_item) => { - handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.item_manager).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? }, _ => { let cmsg = msg.clone(); - Box::new(self.client_location.get_all_clients_by_client(id).unwrap().into_iter() + let block = self.blocks.with_client(id, &self.clients)?; + Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter() .filter(move |client| client.local_client.id() == target as u8) .map(move |client| { (client.client, SendShipPacket::DirectMessage(cmsg.clone())) @@ -509,7 +559,8 @@ impl ServerState for ShipServerState { -> Result + 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.shipgate_sender, &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, self.blocks.0.len()) + .await?.into_iter().map(move |pkt| (id, pkt))) }, RecvShipPacket::QuestDetailRequest(questdetailrequest) => { match questdetailrequest.menu { @@ -518,37 +569,41 @@ impl ServerState for ShipServerState { } }, RecvShipPacket::MenuSelect(menuselect) => { + let block = self.blocks.with_client(id, &self.clients)?; match menuselect.menu { SHIP_MENU_ID => handler::ship::selected_ship(id, menuselect, &self.ship_list)?, BLOCK_MENU_ID => Box::new(handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_manager, &self.level_table)?.into_iter().map(move |pkt| (id, pkt))), - ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut self.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut self.rooms)?, + ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)?, QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &self.quests)?, _ => unreachable!(), } }, RecvShipPacket::QuestMenuSelect(questmenuselect) => { - handler::quest::load_quest(id, questmenuselect, &self.quests, &mut self.clients, &self.client_location, &mut self.rooms)? + let block = self.blocks.with_client(id, &self.clients)?; + handler::quest::load_quest(id, questmenuselect, &self.quests, &mut self.clients, &block.client_location, &mut block.rooms)? }, RecvShipPacket::MenuDetail(_menudetail) => { //unreachable!(); Box::new(Vec::new().into_iter()) }, RecvShipPacket::RoomPasswordReq(room_password_req) => { - if room_password_req.password == self.rooms[room_password_req.item as usize].as_ref() + let block = self.blocks.with_client(id, &self.clients)?; + if room_password_req.password == block.rooms[room_password_req.item as usize].as_ref() .ok_or(ShipError::InvalidRoom(room_password_req.item))? .password { - let menuselect = MenuSelect { - menu: room_password_req.menu, - item: room_password_req.item, - }; - handler::room::join_room(id, &menuselect, &mut self.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut self.rooms)? - } + let menuselect = MenuSelect { + menu: room_password_req.menu, + item: room_password_req.item, + }; + handler::room::join_room(id, &menuselect, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)? + } else { Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))].into_iter()) } }, RecvShipPacket::CharData(chardata) => { - Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut self.client_location, &self.clients, &self.item_manager, &self.level_table)?.into_iter()) + let block = self.blocks.with_client(id, &self.clients)?; + Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_manager, &self.level_table)?.into_iter()) }, RecvShipPacket::Message(msg) => { self.message(id, msg).await? @@ -557,41 +612,50 @@ impl ServerState for ShipServerState { self.direct_message(id, msg).await? }, RecvShipPacket::PlayerChat(msg) => { - Box::new(handler::communication::player_chat(id, msg, &self.client_location, &self.clients)?.into_iter()) + let block = self.blocks.with_client(id, &self.clients)?; + Box::new(handler::communication::player_chat(id, msg, &block.client_location, &self.clients)?.into_iter()) }, RecvShipPacket::CreateRoom(create_room) => { - handler::room::create_room(id, create_room, &mut self.client_location, &mut self.clients, &mut self.item_manager, &mut self.rooms)? + let block = self.blocks.with_client(id, &self.clients)?; + handler::room::create_room(id, create_room, &mut block.client_location, &mut self.clients, &mut self.item_manager, &mut block.rooms)? }, RecvShipPacket::RoomNameRequest(_req) => { - handler::room::room_name_request(id, &self.client_location, &self.rooms) + let block = self.blocks.with_client(id, &self.clients)?; + handler::room::room_name_request(id, &block.client_location, &block.rooms) }, RecvShipPacket::UpdateConfig(pkt) => { handler::settings::update_config(id, pkt, &mut self.clients, &mut self.entity_gateway).await }, RecvShipPacket::ViewInfoboardRequest(_pkt) => { - handler::communication::request_infoboard(id, &self.client_location, &self.clients) + let block = self.blocks.with_client(id, &self.clients)?; + handler::communication::request_infoboard(id, &block.client_location, &self.clients) }, RecvShipPacket::WriteInfoboard(pkt) => { handler::communication::write_infoboard(id, pkt, &mut self.clients, &mut self.entity_gateway).await }, RecvShipPacket::RoomListRequest(_req) => { - handler::room::request_room_list(id, &self.client_location, &self.rooms) + let block = self.blocks.with_client(id, &self.clients)?; + handler::room::request_room_list(id, &block.client_location, &block.rooms) }, RecvShipPacket::Like62ButCooler(cool62) => { - handler::room::cool_62(id, cool62, &self.client_location) + let block = self.blocks.with_client(id, &self.clients)?; + handler::room::cool_62(id, cool62, &block.client_location) }, RecvShipPacket::ClientCharacterData(_) => { // TOOD: validate this in some way? Box::new(None.into_iter()) }, RecvShipPacket::DoneBursting(_) => { - handler::room::done_bursting(id, &self.client_location, &mut self.rooms) + let block = self.blocks.with_client(id, &self.clients)?; + handler::room::done_bursting(id, &block.client_location, &mut block.rooms) }, RecvShipPacket::DoneBursting2(_) => { - handler::room::done_bursting(id, &self.client_location, &mut self.rooms) + let block = self.blocks.with_client(id, &self.clients)?; + handler::room::done_bursting(id, &block.client_location, &mut block.rooms) }, RecvShipPacket::LobbySelect(pkt) => { - Box::new(handler::lobby::change_lobby(id, pkt.lobby, &mut self.client_location, &self.clients, &mut self.item_manager, &self.level_table, &mut self.rooms, &mut self.entity_gateway).await?.into_iter()) + let block = self.blocks.with_client(id, &self.clients)?; + Box::new(handler::lobby::change_lobby(id, pkt.lobby, &mut block.client_location, &self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms, &mut self.entity_gateway).await?.into_iter()) }, RecvShipPacket::RequestQuestList(_) => { handler::quest::send_quest_category_list(id, &self.quests)? @@ -603,7 +667,8 @@ impl ServerState for ShipServerState { handler::quest::quest_chunk_ack(id, quest_chunk_ack, &self.quests)? }, RecvShipPacket::DoneLoadingQuest(_) => { - handler::quest::done_loading_quest(id, &mut self.clients, &self.client_location)? + let block = self.blocks.with_client(id, &self.clients)?; + handler::quest::done_loading_quest(id, &mut self.clients, &block.client_location)? }, RecvShipPacket::FullCharacterData(_full_character_data) => { Box::new(None.into_iter()) @@ -622,19 +687,20 @@ impl ServerState for ShipServerState { async fn on_disconnect(&mut self, id: ClientId) -> Result, anyhow::Error> { 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)?; + let block = self.blocks.with_client(id, &self.clients)?; + let area_client = block.client_location.get_local_client(id)?; + let neighbors = block.client_location.get_client_neighbors(id)?; - let pkt = match self.client_location.get_area(id)? { + let pkt = match block.client_location.get_area(id)? { RoomLobby::Room(room) => { if neighbors.len() == 0 { - self.rooms[room.0] = None; + block.rooms[room.0] = None; } - let leader = self.client_location.get_room_leader(room)?; + let leader = block.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)?; + let leader = block.client_location.get_lobby_leader(lobby)?; SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())) } }; @@ -643,7 +709,7 @@ impl ServerState for ShipServerState { shipgate_sender(ShipMessage::RemoveUser(client.user.id.clone())); } - self.client_location.remove_client_from_area(id); + block.client_location.remove_client_from_area(id); self.item_manager.remove_character_from_room(&client.character); if let Some(mut client) = self.clients.remove(&id) { From f96ae557fd8f4da5eb9cdbfb2fd5a6fb111cbe8f Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 25 Nov 2020 22:02:16 -0700 Subject: [PATCH 10/13] how did this ever work? --- src/ship/location.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ship/location.rs b/src/ship/location.rs index 8b36055..c06a89e 100644 --- a/src/ship/location.rs +++ b/src/ship/location.rs @@ -146,6 +146,12 @@ impl Default for ClientLocation { impl ClientLocation { pub fn add_client_to_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result<(), JoinLobbyError> { + let l = self.lobbies.get_mut(lobby.0).ok_or(JoinLobbyError::LobbyDoesNotExist)?; + if l.0.iter().filter(|c| c.is_none()).count() == 0 { + return Err(JoinLobbyError::LobbyFull); + } + self.remove_client_from_area(id); + let l = self.lobbies.get_mut(lobby.0).ok_or(JoinLobbyError::LobbyDoesNotExist)?; let (index, empty_slot) = l.0.iter_mut() .enumerate() @@ -157,7 +163,6 @@ impl ClientLocation { local_client: LocalClientId(index), time_join: SystemTime::now(), }); - self.remove_client_from_area(id); self.client_location.insert(id, RoomLobby::Lobby(lobby)); Ok(()) } From fe03515871a967db3d1ee260a736cb9e41048460 Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 25 Nov 2020 22:05:55 -0700 Subject: [PATCH 11/13] basic multiple login check --- src/login/character.rs | 22 ++++++++++++++++------ src/ship/packet/builder/lobby.rs | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/login/character.rs b/src/login/character.rs index 895ce7e..7516514 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -323,10 +323,13 @@ impl CharacterServerState { 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() { + if expires > chrono::Utc::now() { return Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(AccountStatus::AlreadyOnline, Session::new()))]); } } + else { + return Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(AccountStatus::AlreadyOnline, Session::new()))]); + } } let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new()); @@ -337,7 +340,7 @@ impl CharacterServerState { self.connected_clients.insert(user.id.clone(), ConnectedClient { ship_id: None, - expires: Some(chrono::Utc::now() + chrono::Duration::minutes(1)), + expires: None, //Some(chrono::Utc::now() + chrono::Duration::minutes(1)), }); client.user = Some(user); @@ -509,11 +512,19 @@ impl CharacterServerState { ]) } - fn select_ship(&mut self, menuselect: &MenuSelect) -> Result, anyhow::Error> { + fn select_ship(&mut self, id: ClientId, menuselect: &MenuSelect) -> Result, anyhow::Error> { if menuselect.menu != SHIP_MENU_ID { Err(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?; } + if let Some(client) = self.clients.get(&id) { + if let Some(user) = &client.user { + if let Some(cc) = self.connected_clients.get_mut(&user.id) { + cc.ship_id = Some(ServerId(menuselect.item as usize)); + } + } + } + let ship = self.ships.get(&ServerId(menuselect.item as usize)) .ok_or(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?; Ok(vec![SendCharacterPacket::RedirectClient(RedirectClient::new(u32::from_le_bytes(ship.ip.octets()), ship.port))]) @@ -604,9 +615,8 @@ impl ServerState for CharacterServerState { async fn on_disconnect(&mut self, id: ClientId) -> Result, anyhow::Error> { 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; + if let Some(user) = client.user { + self.connected_clients.remove(&user.id); } } Ok(Vec::new()) diff --git a/src/ship/packet/builder/lobby.rs b/src/ship/packet/builder/lobby.rs index 5e7d33b..b0a0d8e 100644 --- a/src/ship/packet/builder/lobby.rs +++ b/src/ship/packet/builder/lobby.rs @@ -13,7 +13,7 @@ pub fn join_lobby(id: ClientId, clients: &Clients, item_manager: &ItemManager, level_table: &CharacterLevelTable) - -> Result { + -> Result { let lobby_clients = client_location.get_clients_in_lobby(lobby).map_err(|err| -> ClientLocationError { err.into() })?; let playerinfo = lobby_clients.iter() .map(|area_client| { From ae696888e91afb06232c2c59767ac47c5d0e90e2 Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 25 Nov 2020 22:08:41 -0700 Subject: [PATCH 12/13] remove player from lobby before sending to another ship/block --- src/ship/packet/handler/lobby.rs | 24 +++++++++++++++++++----- src/ship/ship.rs | 14 +++++++++++--- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index 7570591..87d9131 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -15,7 +15,7 @@ pub fn block_selected(id: ClientId, clients: &mut Clients, item_manager: &ItemManager, level_table: &CharacterLevelTable) - -> Result, anyhow::Error> { + -> Result, anyhow::Error> { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; client.block = pkt.item as usize - 1; @@ -38,11 +38,11 @@ pub fn block_selected(id: ClientId, .build(); Ok(vec![ - SendShipPacket::FullCharacter(Box::new(FullCharacter { + (id, SendShipPacket::FullCharacter(Box::new(FullCharacter { character: fc, - })), - SendShipPacket::CharDataRequest(CharDataRequest {}), - SendShipPacket::LobbyList(LobbyList::new()), + }))), + (id, SendShipPacket::CharDataRequest(CharDataRequest {})), + (id, SendShipPacket::LobbyList(LobbyList::new())), ]) } @@ -113,3 +113,17 @@ pub async fn change_lobby(id: ClientId, .map(|c| (c.client, SendShipPacket::LeaveLobby(leave_lobby.clone())))) .collect()) } + +pub fn remove_from_lobby(id: ClientId, + client_location: &mut ClientLocation) + -> Result, anyhow::Error> { + let area_client = client_location.get_local_client(id)?; + let neighbors = client_location.get_client_neighbors(id)?; + let leader = client_location.get_leader_by_client(id)?; + let leave_lobby_pkt = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())); + + client_location.remove_client_from_area(id)?; + Ok(neighbors.into_iter().map(|n| { + (n.client, leave_lobby_pkt.clone()) + }).collect()) +} diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 91ec726..7810e12 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -571,8 +571,16 @@ impl ServerState for ShipServerState { RecvShipPacket::MenuSelect(menuselect) => { let block = self.blocks.with_client(id, &self.clients)?; match menuselect.menu { - SHIP_MENU_ID => handler::ship::selected_ship(id, menuselect, &self.ship_list)?, - BLOCK_MENU_ID => Box::new(handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_manager, &self.level_table)?.into_iter().map(move |pkt| (id, pkt))), + SHIP_MENU_ID => { + let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).into_iter().into_iter().flatten(); + let select_ship = handler::ship::selected_ship(id, menuselect, &self.ship_list)?; + Box::new(leave_lobby.chain(select_ship)) + } + BLOCK_MENU_ID => { + let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).into_iter().into_iter().flatten(); + let select_block = handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_manager, &self.level_table)?.into_iter(); + Box::new(leave_lobby.chain(select_block)) + } ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)?, QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &self.quests)?, _ => unreachable!(), @@ -680,7 +688,7 @@ impl ServerState for ShipServerState { handler::ship::ship_list(id, &self.ship_list) }, RecvShipPacket::RequestShipBlockList(_) => { - handler::ship::block_list(id, &self.name, 1) + handler::ship::block_list(id, &self.name, self.blocks.0.len()) } }) } From 11fecd5bda7d0555998b1b1ca5c221a21192fecf Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 25 Nov 2020 22:09:00 -0700 Subject: [PATCH 13/13] fix tests that care about rooms --- src/ship/ship.rs | 10 +++++----- tests/test_exp_gain.rs | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 7810e12..9bd9706 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -263,7 +263,7 @@ impl ClientState { settings: settings, character: character, session: session, - block: 1, + block: 0, item_drop_location: None, done_loading_quest: false, area: None, @@ -379,9 +379,9 @@ impl ShipServerStateBuilder { } -struct Block { +pub struct Block { client_location: Box, - rooms: Box, + pub rooms: Box, } impl Default for Block { @@ -393,7 +393,7 @@ impl Default for Block { } } -struct Blocks(Vec); +pub struct Blocks(pub Vec); impl Blocks { fn with_client(&mut self, id: ClientId, clients: &Clients) -> Result<&mut Block, ShipError> { @@ -410,7 +410,7 @@ pub struct ShipServerState { item_manager: items::ItemManager, quests: quests::QuestList, shops: Box, - blocks: Blocks, + pub blocks: Blocks, ip: Ipv4Addr, port: u16, diff --git a/tests/test_exp_gain.rs b/tests/test_exp_gain.rs index 343d804..fde9930 100644 --- a/tests/test_exp_gain.rs +++ b/tests/test_exp_gain.rs @@ -25,7 +25,7 @@ async fn test_character_gains_exp() { create_room(&mut ship, ClientId(1), "room", "").await; let (enemy_id, exp) = { - let room = ship.rooms[0].as_ref().unwrap(); + let room = ship.blocks.0[0].rooms[0].as_ref().unwrap(); let (enemy_id, map_enemy) = (0..).filter_map(|i| { room.maps.enemy_by_id(i).map(|enemy| { (i, enemy) @@ -64,7 +64,7 @@ async fn test_character_levels_up() { create_room(&mut ship, ClientId(1), "room", "").await; let enemy_id = { - let room = ship.rooms[0].as_ref().unwrap(); + let room = ship.blocks.0[0].rooms[0].as_ref().unwrap(); (0..).filter_map(|i| { room.maps.enemy_by_id(i).map(|_| { i @@ -102,7 +102,7 @@ async fn test_character_levels_up_multiple_times() { create_room(&mut ship, ClientId(1), "room", "").await; let (enemy_id, exp) = { - let room = ship.rooms[0].as_ref().unwrap(); + let room = ship.blocks.0[0].rooms[0].as_ref().unwrap(); let (enemy_id, map_enemy) = (0..).filter_map(|i| { room.maps.enemy_by_id(i).ok().and_then(|enemy| { if enemy.monster == MonsterType::DarkFalz2 { @@ -152,7 +152,7 @@ async fn test_one_character_gets_full_exp_and_other_attacker_gets_partial() { join_room(&mut ship, ClientId(2), 0).await; let (enemy_id, exp) = { - let room = ship.rooms[0].as_ref().unwrap(); + let room = ship.blocks.0[0].rooms[0].as_ref().unwrap(); let (enemy_id, map_enemy) = (0..).filter_map(|i| { room.maps.enemy_by_id(i).ok().and_then(|enemy| { if enemy.monster == MonsterType::DarkFalz2 {