diff --git a/src/ship/packet/builder/lobby.rs b/src/ship/packet/builder/lobby.rs index 6c89536..29a1b8b 100644 --- a/src/ship/packet/builder/lobby.rs +++ b/src/ship/packet/builder/lobby.rs @@ -6,40 +6,11 @@ use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients}; use crate::ship::character::{CharacterBytesBuilder, FullCharacterBytesBuilder}; use crate::ship::location::{ClientLocation, LobbyId, AreaClient}; use crate::entity::character::CharacterEntity; -use crate::ship::items::{ActiveInventory}; +use crate::ship::items::ActiveInventory; +use crate::ship::packet::builder::{player_header, player_info}; use libpso::character::character::{Inventory, InventoryItem}; use libpso::utf8_to_utf16_array; -fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) -> PlayerHeader { - PlayerHeader { - tag: tag, - 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, - } -} - -fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, level_table: &CharacterLevelTable) -> PlayerInfo { - let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); - let character = CharacterBytesBuilder::new() - .character(&client.character) - .stats(&stats) - .level(level - 1) - .build(); - PlayerInfo { - header: player_header(tag, client, area_client), - inventory: Inventory { - item_count: client.inventory.count() as u8, - hp_mats_used: 0, // TODO: materials - tp_mats_used: 0, // TODO: materials - language: 0, // TODO: account language - items: client.inventory.as_client_inventory_items(), - }, - character: character, - } -} pub fn join_lobby(id: ClientId, lobby: LobbyId, diff --git a/src/ship/packet/builder/mod.rs b/src/ship/packet/builder/mod.rs index 36bd53c..19120f1 100644 --- a/src/ship/packet/builder/mod.rs +++ b/src/ship/packet/builder/mod.rs @@ -1 +1,41 @@ pub mod lobby; +pub mod room; + +use libpso::character::character::Inventory; +use libpso::packet::ship::{PlayerHeader, PlayerInfo}; +use libpso::utf8_to_utf16_array; +use crate::common::leveltable::CharacterLevelTable; +use crate::ship::character::CharacterBytesBuilder; +use crate::ship::ship::ClientState; +use crate::ship::location::AreaClient; + +pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) -> PlayerHeader { + PlayerHeader { + tag: tag, + 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, + } +} + +pub fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, level_table: &CharacterLevelTable) -> PlayerInfo { + let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); + let character = CharacterBytesBuilder::new() + .character(&client.character) + .stats(&stats) + .level(level - 1) + .build(); + PlayerInfo { + header: player_header(tag, client, area_client), + inventory: Inventory { + item_count: client.inventory.count() as u8, + hp_mats_used: 0, // TODO: materials + tp_mats_used: 0, // TODO: materials + language: 0, // TODO: account language + items: client.inventory.as_client_inventory_items(), + }, + character: character, + } +} diff --git a/src/ship/packet/builder/room.rs b/src/ship/packet/builder/room.rs new file mode 100644 index 0000000..26abdb3 --- /dev/null +++ b/src/ship/packet/builder/room.rs @@ -0,0 +1,77 @@ +use std::collections::HashMap; +use libpso::packet::ship::*; +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, RoomId, AreaClient}; +use crate::entity::character::CharacterEntity; +use crate::ship::items::ActiveInventory; +use crate::ship::room::RoomState; +use crate::ship::packet::builder::{player_header, player_info}; +use libpso::character::character::{Inventory, InventoryItem}; +use libpso::utf8_to_utf16_array; + +pub fn join_room(id: ClientId, + clients: &Clients, + client_location: &ClientLocation, + room_id: RoomId, + room: &RoomState) + -> Result { + let all_clients = client_location.get_clients_in_room(room_id).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; + let players = all_clients.iter() + .enumerate() + .fold(Ok([PlayerHeader::default(); 4]), |acc, (i, c)| { + let header_client = clients.get(&c.client).ok_or(ShipError::ClientNotFound(id))?; + let header_area_client = client_location.get_local_client(id).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; + acc.map(|mut a| { + a[i] = player_header(0x10000, &header_client, &header_area_client); + a + }) + })?; + + let area_client = client_location.get_local_client(id).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; + let leader = client_location.get_room_leader(room_id).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; + Ok(JoinRoom { + flag: all_clients.len() as u32, + maps: room.maps.map_headers(), + players: players, + client: area_client.local_client.id(), + leader: leader.local_client.id(), + one: 1, + difficulty: room.mode.difficulty().into(), + battle: room.mode.battle(), + event: 0, + section: room.section_id.into(), + challenge: room.mode.challenge(), + random_seed: room.random_seed, + episode: room.mode.episode().into(), + one2: 1, + single_player: room.mode.single_player(), + unknown: 0, + }) +} + + +pub fn add_to_room(id: ClientId, + client: &ClientState, + area_client: &AreaClient, + leader: &AreaClient, + level_table: &CharacterLevelTable, + room_id: RoomId, +) + -> Result { + + Ok(AddToRoom { + flag: 1, + client: area_client.local_client.id(), + leader: leader.local_client.id(), + one: 0, // TODO: ???????? + lobby: 0xFF, + block: 0, + event: 0, + padding: 0, + playerinfo: player_info(0x10000, client, &area_client, level_table), + }) +} + diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index fa046d8..2d4b228 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -5,6 +5,7 @@ use crate::common::leveltable::CharacterLevelTable; use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Rooms, Clients}; use crate::ship::character::{CharacterBytesBuilder, FullCharacterBytesBuilder}; use crate::ship::location::{ClientLocation, LobbyId, RoomId, RoomLobby, MAX_ROOMS}; +use crate::ship::packet::builder; use libpso::character::character; use crate::ship::room; @@ -13,7 +14,7 @@ pub fn create_room(id: ClientId, client_location: &mut ClientLocation, clients: &mut Clients, rooms: &mut Rooms) - -> Box + Send> { + -> Result + Send>, ShipError> { let area = client_location.get_area(id).unwrap(); let area_client = client_location.get_local_client(id).unwrap(); let lobby_neighbors = client_location.get_client_neighbors(id).unwrap(); @@ -23,44 +24,18 @@ pub fn create_room(id: ClientId, let mut room = room::RoomState::from_create_room(create_room, client.character.section_id).unwrap(); room.bursting = true; - let players = [PlayerHeader { - tag: 0x10000, - guildcard: client.user.id.0, - _unknown1: [0; 5], - client_id: 0, - name: libpso::utf8_to_utf16_array!(client.character.name, 16), - _unknown2: 2, - }, PlayerHeader::default(), PlayerHeader::default(), PlayerHeader::default()]; - - let join_room = JoinRoom { - flag: 1, - maps: room.maps.map_headers(), - players: players, - client: 0, - leader: 0, - one: 1, - difficulty: create_room.difficulty, - battle: create_room.battle, - event: 0, - section: 0, // TODO - challenge: create_room.challenge, - random_seed: 23, // TODO - episode: create_room.episode, - one2: 1, - single_player: create_room.single_player, - unknown: 0, - }; + let join_room = builder::room::join_room(id, clients, client_location, room_id, &room)?; rooms[room_id.0] = Some(room); let leader = 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 + Ok(leader) => Ok(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) + })))), + Err(_) => Ok(Box::new(result)) } } @@ -81,109 +56,42 @@ pub fn join_room(id: ClientId, clients: &mut Clients, level_table: &CharacterLevelTable, rooms: &mut Rooms) - -> Result, ShipError> { + -> Result + Send>, ShipError> { let original_area = client_location.get_area(id).unwrap(); let original_neighbors = client_location.get_client_neighbors(id).unwrap(); let room = rooms.get(pkt.item as usize) .ok_or_else(|| ShipError::InvalidRoom(pkt.item))?.as_ref() .ok_or_else(|| ShipError::InvalidRoom(pkt.item))?; if room.bursting { - return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("player is bursting\nplease wait".into())))]) + return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("player is bursting\nplease wait".into())))].into_iter())) } let room_id = RoomId(pkt.item as usize); let original_room_clients = client_location.get_clients_in_room(room_id).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; client_location.add_client_to_room(id, room_id).unwrap(); // TODO: show room full error or whatever - let all_clients = 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 = 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 client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let area_client = client_location.get_local_client(id).map_err(|err| ShipError::ClientError(format!("{:?}", err)))?; let leader = 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 = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - let (level, stats) = 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()))) - ); + let join_room = builder::room::join_room(id, clients, client_location, room_id, &room)?; + let add_to = builder::room::add_to_room(id, &client, &area_client, &leader, level_table, room_id)?; let room = rooms.get_mut(room_id.0).unwrap().as_mut().unwrap(); room.bursting = true; + + let mut result: Box + Send> = Box::new( + vec![(id, SendShipPacket::JoinRoom(join_room))] + .into_iter() + .chain(original_room_clients.clone().into_iter() + .map(move |c| (c.client, SendShipPacket::AddToRoom(add_to.clone()))) + )); + if let Ok(leader) = 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()) + result = Box::new(result.chain(original_neighbors.into_iter() + .map(move |c| (c.client, leave_lobby.clone())))) } + + Ok(result) } pub fn done_bursting(id: ClientId, diff --git a/src/ship/room.rs b/src/ship/room.rs index b91c790..66c7830 100644 --- a/src/ship/room.rs +++ b/src/ship/room.rs @@ -117,6 +117,27 @@ impl RoomMode { RoomMode::Challenge {episode, ..} => *episode, } } + + pub fn battle(&self) -> u8 { + match self { + RoomMode::Battle {..} => 1, + _ => 0, + } + } + + pub fn challenge(&self) -> u8 { + match self { + RoomMode::Challenge {..} => 1, + _ => 0, + } + } + + pub fn single_player(&self) -> u8 { + match self { + RoomMode::Single {..} => 1, + _ => 0, + } + } } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 5ef4728..595dc64 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -245,7 +245,7 @@ impl ServerState for ShipServerState { RecvShipPacket::MenuSelect(menuselect) => { match menuselect.menu { BLOCK_MENU_ID => Box::new(handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.level_table)?.into_iter().map(move |pkt| (id, pkt))), - ROOM_MENU_ID => Box::new(handler::room::join_room(id, menuselect, &mut self.client_location, &mut self.clients, &self.level_table, &mut self.rooms)?.into_iter()), + ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut self.client_location, &mut self.clients, &self.level_table, &mut self.rooms)?, _ => unreachable!(), } }, @@ -262,7 +262,7 @@ impl ServerState for ShipServerState { Box::new(handler::communication::player_chat(id, msg, &self.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.rooms) + handler::room::create_room(id, create_room, &mut self.client_location, &mut self.clients, &mut self.rooms)? }, RecvShipPacket::RoomNameRequest(_req) => { handler::room::room_name_request(id, &self.client_location, &self.rooms)