use libpso::packet::ship::*; use crate::common::serverstate::ClientId; use crate::common::leveltable::LEVEL_TABLE; use crate::ship::ship::{SendShipPacket, ShipError, Clients, ShipEvent}; use crate::ship::room::Rooms; use crate::ship::character::{FullCharacterBytesBuilder}; use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId}; use crate::ship::packet; use crate::ship::items::state::ItemState; use crate::entity::gateway::EntityGateway; use crate::entity::room::RoomNote; use crate::ship::map::MapArea; use futures::future::join_all; // this function needs a better home pub async fn block_selected(id: ClientId, pkt: MenuSelect, clients: &Clients, item_state: &ItemState) -> Result, anyhow::Error> { clients.with_mut(id, |client| { let item_state = item_state.clone(); Box::pin(async move { client.block = pkt.item as usize - 1; let (level, stats) = LEVEL_TABLE.get_stats_from_exp(client.character.char_class, client.character.exp); let inventory = item_state.get_character_inventory(&client.character).await?; let bank = item_state.get_character_bank(&client.character).await?; let fc = FullCharacterBytesBuilder::default() .character(&client.character) .stats(&stats) .level(level) .meseta(inventory.meseta) .inventory(&inventory) .bank(&bank) .keyboard_config(&client.settings.settings.keyboard_config) .gamepad_config(&client.settings.settings.gamepad_config) .symbol_chat(&client.settings.settings.symbol_chats) .tech_menu(&client.character.tech_menu.as_bytes()) .option_flags(client.character.option_flags) .build(); Ok(vec![ (id, SendShipPacket::FullCharacter(Box::new(FullCharacter { character: fc, }))), (id, SendShipPacket::CharDataRequest(CharDataRequest {})), (id, SendShipPacket::LobbyList(LobbyList::new())), ]) })}).await? } pub async fn send_player_to_lobby(id: ClientId, _pkt: CharData, client_location: &mut ClientLocation, clients: &Clients, item_state: &ItemState, event: ShipEvent) -> Result, anyhow::Error> { let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).await.map_err(|_| ShipError::TooManyClients)?; let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?; let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?; let neighbors = client_location.get_client_neighbors(id).await.unwrap(); Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] .into_iter() .chain(neighbors.into_iter() .map(|c| (c.client, SendShipPacket::AddToLobby(addto.clone())))).collect()) } #[allow(clippy::too_many_arguments)] pub async fn change_lobby(id: ClientId, requested_lobby: u32, client_location: &mut ClientLocation, clients: &Clients, item_state: &mut ItemState, rooms: &Rooms, entity_gateway: &mut EG, event: ShipEvent) -> Result, anyhow::Error> where EG: EntityGateway + Clone + 'static, { let prev_area = client_location.get_area(id).await.map_err(|err| -> ClientLocationError {err.into()})?; match prev_area { RoomLobby::Lobby(old_lobby) => { if old_lobby.0 == requested_lobby as usize { return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You are already in this Lobby!".into())))]) } }, RoomLobby::Room(old_room) => { let room_entity_id = rooms.with(old_room, |room| Box::pin(async { room.room_id })).await?; if client_location.get_client_neighbors(id).await?.is_empty() { rooms.remove(old_room).await; } let character_id = clients.with(id, |client| Box::pin(async { client.character.id })).await?; clients.with(id, |client| { let mut item_state = item_state.clone(); let mut entity_gateway = entity_gateway.clone(); Box::pin(async move { item_state.remove_character_from_room(&client.character).await; entity_gateway.add_room_note(room_entity_id, RoomNote::PlayerLeave { character_id }).await })}).await??; }, } let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location).await?; let old_neighbors = client_location.get_client_neighbors(id).await.unwrap(); let mut lobby = LobbyId(requested_lobby as usize); if client_location.add_client_to_lobby(id, lobby).await.is_err() { match prev_area { RoomLobby::Lobby(_lobby) => { let dialog = SmallDialog::new(String::from("Lobby is full.")); return Ok(vec![(id, SendShipPacket::SmallDialog(dialog))]) } RoomLobby::Room(_room) => { lobby = client_location.add_client_to_next_available_lobby(id, lobby).await.map_err(|_| ShipError::TooManyClients)?; } } } clients.with(id, |client| { let mut entity_gateway = entity_gateway.clone(); let mut item_state = item_state.clone(); Box::pin(async move { item_state.load_character(&mut entity_gateway, &client.character).await })}).await??; let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?; let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?; let neighbors = client_location.get_client_neighbors(id).await?; Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] .into_iter() .chain(neighbors.into_iter() .map(|c| (c.client, SendShipPacket::AddToLobby(addto.clone())))) .chain(old_neighbors.into_iter() .map(|c| (c.client, SendShipPacket::LeaveLobby(leave_lobby.clone())))) .chain(std::iter::once((id, SendShipPacket::LobbyEvent(LobbyEvent{ event: event.into()})))) .collect()) } pub async fn remove_from_lobby(id: ClientId, client_location: &mut ClientLocation) -> Result, anyhow::Error> { let area_client = client_location.get_local_client(id).await?; let neighbors = client_location.get_client_neighbors(id).await?; let leader = client_location.get_leader_by_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let leave_lobby_pkt = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())); client_location.remove_client_from_area(id).await.map_err(|err| -> ClientLocationError { err.into() })?; Ok(neighbors.into_iter().map(|n| { (n.client, leave_lobby_pkt.clone()) }).collect()) } pub async fn get_room_tab_info(id: ClientId, pkt: MenuDetail, client_location: &mut ClientLocation, clients: &Clients) -> Result, anyhow::Error> { let room_id = RoomId(pkt.item as usize); let clients_in_room = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room_info = if clients_in_room.is_empty() { String::from("Game is no longer active") } else { join_all(clients_in_room.iter() .map(|clientl| async move { clients.with(clientl.client, |client| Box::pin(async move { format!("{} Lv{} {}\n{} {}", client.character.name, LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp), client.user.guildcard, client.character.char_class, client.area.unwrap_or(MapArea::Pioneer2Ep1)) })).await })).await .into_iter() .collect::, anyhow::Error>>()? .join("\n") }; Ok(vec![(id, SendShipPacket::SmallLeftDialog(SmallLeftDialog::new(room_info)))]) }