diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 77acc92..580f271 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -9,7 +9,7 @@ use libpso::packet::messages::*; use libpso::{PacketParseError, PSOPacket}; use libpso::crypto::bb::PSOBBCipher; use libpso::character::character; -use libpso::packet::ship::{ROOM_MENU_ID}; +use libpso::packet::ship::{BLOCK_MENU_ID, ROOM_MENU_ID}; use libpso::{utf8_to_array, utf8_to_utf16_array}; use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; @@ -34,7 +34,8 @@ pub enum ShipError { NoCharacterInSlot(ClientId, u32), InvalidSlot(ClientId, u32), TooManyClients, - ClientError, + ClientError(String), + InvalidRoom(u32), } #[derive(Debug)] @@ -51,6 +52,9 @@ pub enum RecvShipPacket { ViewInfoboardRequest(ViewInfoboardRequest), WriteInfoboard(WriteInfoboard), RoomListRequest(RoomListRequest), + Like62ButCooler(Like62ButCooler), + ClientCharacterData(ClientCharacterData), + DoneBursting(DoneBursting), } impl RecvServerPacket for RecvShipPacket { @@ -68,6 +72,9 @@ impl RecvServerPacket for RecvShipPacket { 0xD8 => Ok(RecvShipPacket::ViewInfoboardRequest(ViewInfoboardRequest::from_bytes(data)?)), 0xD9 => Ok(RecvShipPacket::WriteInfoboard(WriteInfoboard::from_bytes(data)?)), 0x08 => Ok(RecvShipPacket::RoomListRequest(RoomListRequest::from_bytes(data)?)), + 0x6D => Ok(RecvShipPacket::Like62ButCooler(Like62ButCooler::from_bytes(data)?)), + 0x98 => Ok(RecvShipPacket::ClientCharacterData(ClientCharacterData::from_bytes(data)?)), + 0x6F => Ok(RecvShipPacket::DoneBursting(DoneBursting::from_bytes(data)?)), _ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec())) } } @@ -93,6 +100,9 @@ pub enum SendShipPacket { RoomNameResponse(RoomNameResponse), ViewInfoboardResponse(ViewInfoboardResponse), RoomListResponse(RoomListResponse), + Like62ButCooler(Like62ButCooler), + BurstDone72(BurstDone72), + DoneBursting(DoneBursting), } impl SendServerPacket for SendShipPacket { @@ -116,6 +126,9 @@ impl SendServerPacket for SendShipPacket { SendShipPacket::RoomNameResponse(pkt) => pkt.as_bytes(), SendShipPacket::ViewInfoboardResponse(pkt) => pkt.as_bytes(), SendShipPacket::RoomListResponse(pkt) => pkt.as_bytes(), + SendShipPacket::Like62ButCooler(pkt) => pkt.as_bytes(), + SendShipPacket::BurstDone72(pkt) => pkt.as_bytes(), + SendShipPacket::DoneBursting(pkt) => pkt.as_bytes(), } } } @@ -217,9 +230,109 @@ impl ShipServerState { ]) } + fn join_room(&mut self, id: ClientId, pkt: &MenuSelect) -> Result, ShipError> { + let original_area = self.client_location.get_area(id).unwrap(); + let original_neighbors = self.client_location.get_client_neighbors(id).unwrap(); + let room = self.rooms.get(pkt.item as usize) + .ok_or_else(|| ShipError::InvalidRoom(pkt.item))?.as_ref() + .ok_or_else(|| ShipError::InvalidRoom(pkt.item))?; + let room_id = RoomId(pkt.item as usize); + let original_room_clients = self.client_location.get_clients_in_room(room_id).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; + self.client_location.add_client_to_room(id, room_id).unwrap(); // TODO: show room full error or whatever + + let all_clients = self.client_location.get_clients_in_room(room_id).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; + let player_headers = all_clients.iter() + .enumerate() + .fold([PlayerHeader::default(); 4], |mut acc, (i, c)| { + let header_client = self.clients.get(&c.client).ok_or(ShipError::ClientNotFound(id)).unwrap(); + acc[i] = PlayerHeader { + tag: 0x100, + guildcard: header_client.user.id.0, + _unknown1: [0,0,0, c.local_client.id() as u32, 0], + client_id: 0, + name: libpso::utf8_to_utf16_array!(header_client.character.name, 16), + _unknown2: 2, + }; + acc + }); + + let area_client = self.client_location.get_local_client(id).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; + let leader = self.client_location.get_room_leader(room_id).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; + + let join_room = JoinRoom { + flag: all_clients.len() as u32, + maps: room.map_headers(), + players: player_headers, + client: area_client.local_client.id(), + leader: leader.local_client.id(), + one: 1, + difficulty: room.mode.difficulty().into(), + battle: matches!(room.mode, room::RoomMode::Battle {..}) as u8, + event: 0, + section: room.section_id.into(), + challenge: matches!(room.mode, room::RoomMode::Challenge {..}) as u8, + random_seed: room.random_seed, + episode: room.mode.episode().into(), + one2: 1, + single_player: 0, // TODO + unknown: 0, + }; + + let client = self.clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; + let (level, stats) = self.level_table.get_stats_from_exp(client.character.char_class, client.character.exp); + let c = CharacterBytesBuilder::new() + .character(&client.character) + .stats(&stats) + .level(level - 1) + .build(); + let add_to = AddToRoom { + flag: 0x10000, + client: area_client.local_client.id(), + leader: leader.local_client.id(), + one: 0, // TODO: ?????????? + lobby: 0xff, + block: 0, + event: 0, + padding: 1, + playerinfo: PlayerInfo { + header: PlayerHeader { + tag: 0x10000, + guildcard: client.user.id.0, + _unknown1: [0; 5], + client_id: area_client.local_client.id() as u32, + name: libpso::utf8_to_utf16_array!(client.character.name, 16), + _unknown2: 2, + }, + inventory: character::Inventory { + item_count: 0, + hp_mats_used: 0, + tp_mats_used: 0, + language: 0, + items: [character::InventoryItem::default(); 30], // TOOD: this should be something + }, + character: c, + }, + }; + + let result = vec![(id, SendShipPacket::JoinRoom(join_room))] + .into_iter() + .chain(original_room_clients.clone().into_iter() + .map(|c| (c.client, SendShipPacket::AddToRoom(add_to.clone()))) + ); + + if let Ok(leader) = self.client_location.get_area_leader(original_area) { + let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())); + Ok(result.chain(original_neighbors.into_iter() + .map(|c| (c.client, leave_lobby.clone()))).collect()) + } + else { + Ok(result.collect()) + } + } + fn send_player_to_lobby(&mut self, id: ClientId, _pkt: &CharData) -> Result, ShipError> { let lobby = self.client_location.add_client_to_next_available_lobby(id, LobbyId(0)).map_err(|_| ShipError::TooManyClients)?; - let clients = self.client_location.get_clients_in_lobby(lobby).map_err(|_| ShipError::ClientError)?; + let clients = self.client_location.get_clients_in_lobby(lobby).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; let playerinfo = clients.iter() .map(|room_client| { let client = self.clients.get(&room_client.client).ok_or(ShipError::ClientNotFound(id)).unwrap(); @@ -248,8 +361,8 @@ impl ShipServerState { character: c, } }); - let area_client = self.client_location.get_local_client(id).map_err(|_| ShipError::ClientError)?; - let leader = self.client_location.get_lobby_leader(lobby).map_err(|_| ShipError::ClientError)?; + let area_client = self.client_location.get_local_client(id).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; + let leader = self.client_location.get_lobby_leader(lobby).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; let join_lobby = JoinLobby { client: area_client.local_client.id(), @@ -305,6 +418,15 @@ impl ShipServerState { .map(|c| (c.client, SendShipPacket::AddToLobby(addto.clone())))).collect()) } + fn done_bursting(&mut self, id: ClientId) -> Box + Send> { + Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter() + .map(move |client| { + vec![ + (client.client, SendShipPacket::BurstDone72(BurstDone72::new())), + ] + }).flatten()) + } + fn message(&mut self, id: ClientId, msg: &Message) -> Box + Send> { match &msg.msg { GameMessage::RequestExp(killmonster) => { @@ -321,7 +443,6 @@ impl ShipServerState { let cmsg = msg.clone(); Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter() - .filter(move |client| client.client != id) .map(move |client| { (client.client, SendShipPacket::Message(cmsg.clone())) })) @@ -400,14 +521,14 @@ impl ShipServerState { fn create_room(&mut self, id: ClientId, create_room: &CreateRoom) -> Box + Send> { let area = self.client_location.get_area(id).unwrap(); let area_client = self.client_location.get_local_client(id).unwrap(); - let neighbors = self.client_location.get_client_neighbors(id).unwrap(); + let lobby_neighbors = self.client_location.get_client_neighbors(id).unwrap(); let room_id = self.client_location.create_new_room(id).unwrap(); let client = self.clients.get_mut(&id).unwrap();//.ok_or(ShipError::ClientNotFound(id)).unwrap(); let room = room::RoomState::from_create_room(create_room, client.character.section_id).unwrap(); let players = [PlayerHeader { - tag: 0x00010000, + tag: 0x10000, guildcard: client.user.id.0, _unknown1: [0; 5], client_id: 0, @@ -419,8 +540,8 @@ impl ShipServerState { flag: 1, maps: room.maps.map_headers(), players: players, - client_id: 0, - leader_id: 0, + client: 0, + leader: 0, one: 1, difficulty: create_room.difficulty, battle: create_room.battle, @@ -435,14 +556,16 @@ impl ShipServerState { }; self.rooms[room_id.0] = Some(room); - let leader = self.client_location.get_area_leader(area).unwrap(); - Box::new(vec![(id, SendShipPacket::JoinRoom(join_room))] - .into_iter() - .chain(neighbors - .into_iter() - .map(move |c| { - (c.client, SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()))) - }))) + let leader = self.client_location.get_area_leader(area); + let result = vec![(id, SendShipPacket::JoinRoom(join_room))].into_iter(); + match leader { + Ok(leader) => Box::new(result.chain(lobby_neighbors + .into_iter() + .map(move |c| { + (c.client, SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()))) + }))), + Err(_) => Box::new(result) + } } fn room_name_request(&mut self, id: ClientId) -> Box + Send> { @@ -513,6 +636,16 @@ impl ShipServerState { rooms: active_room_list.collect() }))].into_iter()) } + + fn cool_62(&mut self, id: ClientId, cool_62: &Like62ButCooler) -> Box + Send> { + let target = cool_62.flag as u8; + let cool_62 = cool_62.clone(); + Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter() + .filter(move |client| client.local_client.id() == target) + .map(move |client| { + (client.client, SendShipPacket::Like62ButCooler(cool_62.clone())) + })) + } } impl ServerState for ShipServerState { @@ -543,6 +676,7 @@ impl ServerState for ShipServerState { RecvShipPacket::MenuSelect(menuselect) => { match menuselect.menu { BLOCK_MENU_ID => Box::new(self.block_selected(id, menuselect)?.into_iter().map(move |pkt| (id, pkt))), + ROOM_MENU_ID => Box::new(self.join_room(id, menuselect)?.into_iter()), _ => unreachable!(), } }, @@ -580,6 +714,16 @@ impl ServerState for ShipServerState { RecvShipPacket::RoomListRequest(_req) => { self.request_room_list(id) }, + RecvShipPacket::Like62ButCooler(cool62) => { + self.cool_62(id, cool62) + }, + RecvShipPacket::ClientCharacterData(_) => { + // TOOD: validate this in some way? + Box::new(None.into_iter()) + }, + RecvShipPacket::DoneBursting(_) => { + self.done_bursting(id) + } }) }