diff --git a/src/common/interserver.rs b/src/common/interserver.rs index bd09913..db2f6a1 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)] @@ -8,7 +9,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, @@ -25,7 +26,8 @@ pub enum LoginMessage { }, ShipList { ships: Vec, - } + }, + RequestUsers, } #[derive(Debug, Serialize, Deserialize)] @@ -38,7 +40,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 34143a9..7e4b95d 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 +232,14 @@ pub fn ship_connect_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(Debug, Clone)] +struct ConnectedClient { + ship_id: Option, + expires: Option>, +} pub struct CharacterServerState { entity_gateway: EG, @@ -177,11 +187,14 @@ pub struct CharacterServerState { ships: BTreeMap, level_table: CharacterLevelTable, auth_token: AuthToken, + + connected_clients: BTreeMap, authenticated_ships: BTreeSet, + ship_sender: BTreeMap>, } -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 +202,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 +224,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 +238,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 +253,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 +265,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 +278,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(()) } @@ -295,27 +309,48 @@ impl CharacterServerState { level_table: CharacterLevelTable::new(), auth_token: auth_token, authenticated_ships: BTreeSet::new(), + ship_sender: BTreeMap::new(), + connected_clients: 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) { - Ok(mut user) => { - user.at_character = true; - self.entity_gateway.save_user(&user).await.map_err(|_| CharacterError::DbError)?; + 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()))]); + } + } + else { + 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: None, //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> { @@ -457,7 +492,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 @@ -477,15 +512,33 @@ 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_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] @@ -549,16 +602,21 @@ 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 { + SHIP_MENU_ID => Box::new(self.ship_detail(menudetail)?.into_iter().map(move |pkt| (id, pkt))), + _ => Box::new(Vec::new().into_iter()) + } } }) } 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()) @@ -581,19 +639,66 @@ 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); } + + 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) { + 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/location.rs b/src/ship/location.rs index 2248d5a..c06a89e 100644 --- a/src/ship/location.rs +++ b/src/ship/location.rs @@ -133,16 +133,25 @@ 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)?; + 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() @@ -154,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(()) } @@ -389,7 +397,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 +410,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 +419,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 +430,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 +441,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 +458,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 +467,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 +477,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 +497,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 +512,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 +526,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 +537,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 +553,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/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| { 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/auth.rs b/src/ship/packet/handler/auth.rs index 16228c5..23f261d 100644 --- a/src/ship/packet/handler/auth.rs +++ b/src/ship/packet/handler/auth.rs @@ -5,19 +5,19 @@ 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, - 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.and_then(check_if_already_online) { - Ok(mut user) => { - user.at_ship= true; - entity_gateway.save_user(&user).await?; - + Ok(match get_login_status(entity_gateway, pkt).await { + 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; @@ -30,8 +30,12 @@ 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))] + 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..87d9131 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -15,9 +15,9 @@ 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 u32; + client.block = pkt.item as usize - 1; let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); @@ -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/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 a0aa5d3..9bd9706 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,8 @@ pub enum ShipError { ShopError, GatewayError(#[from] GatewayError), UnknownMonster(crate::ship::monster::MonsterType), + InvalidShip(usize), + InvalidBlock(usize), } #[derive(Debug)] @@ -98,6 +102,8 @@ pub enum RecvShipPacket { DoneLoadingQuest(DoneLoadingQuest), FullCharacterData(Box), SaveOptions(SaveOptions), + RequestShipList(RequestShipList), + RequestShipBlockList(RequestShipBlockList), } impl RecvServerPacket for RecvShipPacket { @@ -131,6 +137,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 +152,7 @@ impl RecvServerPacket for RecvShipPacket { pub enum SendShipPacket { ShipWelcome(ShipWelcome), LoginResponse(LoginResponse), + ShipList(ShipList), ShipBlockList(ShipBlockList), FullCharacter(Box), CharDataRequest(CharDataRequest), @@ -172,6 +181,7 @@ pub enum SendShipPacket { QuestChunk(QuestChunk), DoneLoadingQuest(DoneLoadingQuest), BankItemList(BankItemList), + RedirectClient(RedirectClient), } impl SendServerPacket for SendShipPacket { @@ -179,6 +189,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 +218,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(), } } } @@ -231,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, @@ -251,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, @@ -299,6 +311,7 @@ pub struct ShipServerStateBuilder { ip: Option, port: Option, auth_token: Option, + num_blocks: usize, } impl ShipServerStateBuilder { @@ -309,6 +322,7 @@ impl ShipServerStateBuilder { ip: None, port: None, auth_token: None, + num_blocks: 2, } } @@ -337,37 +351,73 @@ 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, + } + } +} + + +pub struct Block { + client_location: Box, + pub rooms: Box, +} + +impl Default for Block { + fn default() -> Block { + Block { + client_location: Box::new(ClientLocation::default()), + rooms: Box::new([None; MAX_ROOMS]), } } } +pub struct Blocks(pub 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, + pub blocks: Blocks, + ip: Ipv4Addr, port: u16, - shops: Box, + auth_token: AuthToken, + ship_list: Vec, + shipgate_sender: Option>, } impl ShipServerState { @@ -375,37 +425,48 @@ 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) => { - 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? @@ -418,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())) })) @@ -430,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())) @@ -489,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.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 { @@ -498,36 +569,49 @@ impl ServerState for ShipServerState { } }, RecvShipPacket::MenuSelect(menuselect) => { + let block = self.blocks.with_client(id, &self.clients)?; match menuselect.menu { - 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)?, + 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!(), } }, 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? @@ -536,41 +620,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)? @@ -582,7 +675,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()) @@ -590,30 +684,40 @@ 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, self.blocks.0.len()) + } }) } 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)?; + 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).unwrap(); + 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).unwrap(); + let leader = block.client_location.get_lobby_leader(lobby)?; SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())) } }; - self.client_location.remove_client_from_area(id); + if let Some(shipgate_sender) = self.shipgate_sender.as_ref() { + shipgate_sender(ShipMessage::RemoveUser(client.user.id.clone())); + } + + 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) { @@ -642,11 +746,28 @@ 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> { - 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)> { 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 {