diff --git a/src/ship/packet/builder/lobby.rs b/src/ship/packet/builder/lobby.rs index c595a6f..84f1d14 100644 --- a/src/ship/packet/builder/lobby.rs +++ b/src/ship/packet/builder/lobby.rs @@ -61,3 +61,17 @@ pub fn add_to_lobby(id: ClientId, playerinfo: player_info(0x100, &client, &area_client, level_table), }) } + +pub fn remove_from_lobby(id: ClientId, + client_location: &ClientLocation) + -> Result { + let prev_area_index = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?.local_client.id(); + let prev_area_leader_index = client_location.get_area_leader(client_location.get_area(id) + .map_err(|err| -> ClientLocationError { err.into() })?) + .map_err(|err| -> ClientLocationError { err.into() })?.local_client.id(); + Ok(LeaveLobby { + client: prev_area_index, + leader: prev_area_leader_index, + _padding: 0, + }) +} \ No newline at end of file diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index 9be8556..5192646 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -4,9 +4,10 @@ use crate::common::serverstate::ClientId; use crate::common::leveltable::CharacterLevelTable; use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients}; use crate::ship::character::{CharacterBytesBuilder, FullCharacterBytesBuilder}; -use crate::ship::location::{ClientLocation, LobbyId, RoomId, RoomLobby, MAX_ROOMS}; +use crate::ship::location::{ClientLocation, LobbyId, RoomId, RoomLobby, MAX_ROOMS, ClientLocationError}; use crate::ship::packet; use libpso::character::character; +use crate::ship::location::ClientLocationError::GetAreaError; // this function needs a better home pub fn block_selected(id: ClientId, @@ -35,6 +36,7 @@ pub fn block_selected(id: ClientId, character: fc, }), SendShipPacket::CharDataRequest(CharDataRequest {}), + SendShipPacket::LobbyList(LobbyList::new()), ]) } @@ -47,10 +49,46 @@ pub fn send_player_to_lobby(id: ClientId, let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).map_err(|_| ShipError::TooManyClients)?; let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, level_table)?; let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, level_table)?; - let neighbors = client_location.get_client_neighbors(id).unwrap(); Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] .into_iter() .chain(neighbors.into_iter() .map(|c| (c.client, SendShipPacket::AddToLobby(addto.clone())))).collect()) } + +pub fn change_lobby(id: ClientId, + requested_lobby: u32, + client_location: &mut ClientLocation, + clients: &Clients, + level_table: &CharacterLevelTable) + -> Result, ShipError> { + let prev_area = client_location.get_area(id).map_err(|err| -> ClientLocationError {err.into()})?; + if prev_area == RoomLobby::Lobby(LobbyId(requested_lobby as usize)) { // If the client is already in the selected lobby, + let dialog = SmallDialog::new(String::from("You are already in this Lobby!")); // Send a SmallDialog to prevent client softlock / server crash + return Ok(vec![(id, SendShipPacket::SmallDialog(dialog))]) + } + let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location)?; + let old_neighbors = client_location.get_client_neighbors(id).unwrap(); + let mut lobby = LobbyId(requested_lobby as usize); + if let Err(_) = client_location.add_client_to_lobby(id, lobby) { + 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).map_err(|_| ShipError::TooManyClients)?; + } + } + } + let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, level_table)?; + let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, level_table)?; + let neighbors = client_location.get_client_neighbors(id).unwrap(); + 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())))) + .collect()) +} diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 10d2db5..8845676 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -64,6 +64,7 @@ pub enum RecvShipPacket { Like62ButCooler(Like62ButCooler), ClientCharacterData(ClientCharacterData), DoneBursting(DoneBursting), + LobbySelect(LobbySelect), } impl RecvServerPacket for RecvShipPacket { @@ -84,6 +85,7 @@ impl RecvServerPacket for RecvShipPacket { 0x6D => Ok(RecvShipPacket::Like62ButCooler(Like62ButCooler::from_bytes(data)?)), 0x98 => Ok(RecvShipPacket::ClientCharacterData(ClientCharacterData::from_bytes(data)?)), 0x6F => Ok(RecvShipPacket::DoneBursting(DoneBursting::from_bytes(data)?)), + 0x84 => Ok(RecvShipPacket::LobbySelect(LobbySelect::from_bytes(data)?)), _ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec())) } } @@ -112,6 +114,7 @@ pub enum SendShipPacket { Like62ButCooler(Like62ButCooler), BurstDone72(BurstDone72), DoneBursting(DoneBursting), + LobbyList(LobbyList), } impl SendServerPacket for SendShipPacket { @@ -138,6 +141,7 @@ impl SendServerPacket for SendShipPacket { SendShipPacket::Like62ButCooler(pkt) => pkt.as_bytes(), SendShipPacket::BurstDone72(pkt) => pkt.as_bytes(), SendShipPacket::DoneBursting(pkt) => pkt.as_bytes(), + SendShipPacket::LobbyList(pkt) => pkt.as_bytes(), } } } @@ -300,6 +304,9 @@ impl ServerState for ShipServerState { }, RecvShipPacket::DoneBursting(_) => { handler::room::done_bursting(id, &self.client_location, &mut self.rooms) + }, + RecvShipPacket::LobbySelect(pkt) => { + Box::new(handler::lobby::change_lobby(id, pkt.lobby, &mut self.client_location, &self.clients, &self.level_table)?.into_iter()) } }) }