From 161c0c94f8ca9ac1eaab7b30b19c2af29ae337c2 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 15 Dec 2019 23:18:21 -0800 Subject: [PATCH] location overhaul --- src/ship/character.rs | 132 +++++++++++++++++++++++++++++++++++ src/ship/location.rs | 157 +++++++++++++++++++++++++++++++++++------- src/ship/mod.rs | 1 + src/ship/ship.rs | 29 ++++---- 4 files changed, 277 insertions(+), 42 deletions(-) create mode 100644 src/ship/character.rs diff --git a/src/ship/character.rs b/src/ship/character.rs new file mode 100644 index 0000000..29c5c65 --- /dev/null +++ b/src/ship/character.rs @@ -0,0 +1,132 @@ +use crate::common::leveltable::CharacterStats; +use libpso::character::character; + + +pub struct CharacterBuilder<'a> { + character: Option<&'a character::Character>, + stats: Option<&'a CharacterStats>, + level: Option, +} + + +impl<'a> CharacterBuilder<'a> { + pub fn new() -> CharacterBuilder<'a> { + CharacterBuilder { + character: None, + stats: None, + level: None, + } + } + + pub fn character(self, character: &'a character::Character) -> CharacterBuilder<'a> { + CharacterBuilder { + character: Some(character), + ..self + } + } + + pub fn stats(self, stats: &'a CharacterStats) -> CharacterBuilder<'a> { + CharacterBuilder { + stats: Some(stats), + ..self + } + } + + pub fn level(self, level: u32) -> CharacterBuilder<'a> { + CharacterBuilder { + level: Some(level), + ..self + } + } + + pub fn build(self) -> character::Character { + let character = self.character.unwrap(); + let stats = self.stats.unwrap(); + let level = self.level.unwrap(); + character::Character { + hp: stats.hp, + atp: stats.atp, + mst: stats.mst, + evp: stats.evp, + dfp: stats.dfp, + ata: stats.ata, + lck: stats.lck, + level: level, + ..*character + } + } +} + + +pub struct FullCharacterBuilder<'a> { + character: Option<&'a character::Character>, + inventory: Option<&'a [character::InventoryItem; 30]>, + inventory_len: Option, + key_config: Option<&'a [u8; 0x16C]>, + joystick_config: Option<&'a [u8; 0x38]>, +} + + +impl<'a> FullCharacterBuilder<'a> { + pub fn new() -> FullCharacterBuilder<'a> { + FullCharacterBuilder { + character: None, + inventory: None, + inventory_len: None, + key_config: None, + joystick_config: None, + } + } + + pub fn character(self, character: &'a character::Character) -> FullCharacterBuilder<'a> { + FullCharacterBuilder { + character: Some(character), + ..self + } + } + + pub fn inventory(self, inventory: &'a [character::InventoryItem; 30], len: usize) -> FullCharacterBuilder<'a> { + FullCharacterBuilder { + inventory: Some(inventory), + inventory_len: Some(len), + ..self + } + } + + pub fn key_config(self, key_config: &'a [u8; 0x16C]) -> FullCharacterBuilder<'a> { + FullCharacterBuilder { + key_config: Some(key_config), + ..self + } + } + + pub fn joystick_config(self, joystick_config: &'a [u8; 0x38]) -> FullCharacterBuilder<'a> { + FullCharacterBuilder { + joystick_config: Some(joystick_config), + ..self + } + } + + pub fn build(self) -> character::FullCharacter { + let character = self.character.unwrap(); + let inventory = self.inventory.unwrap(); + let inventory_len = self.inventory_len.unwrap(); + let key_config = self.key_config.unwrap(); + let joystick_config = self.joystick_config.unwrap(); + + character::FullCharacter { + character: *character, + inventory: character::Inventory { + item_count: inventory_len as u8, + items: *inventory, + ..character::Inventory::default() + }, + key_team_config: character::KeyTeamConfig { + key_config: *key_config, + joystick_config: *joystick_config, + ..character::KeyTeamConfig::default() + }, + ..character::FullCharacter::default() + } + } +} diff --git a/src/ship/location.rs b/src/ship/location.rs index ddf35d4..6d91fc7 100644 --- a/src/ship/location.rs +++ b/src/ship/location.rs @@ -1,63 +1,168 @@ - +use std::time::SystemTime; use crate::common::serverstate::ClientId; // TODO: room passwords? // TODO: remove clients from areas (or upon insert, remove that id from anywhere else) #[derive(Copy, Clone)] -pub struct ClientArea { - clients: [Option; N], +pub struct AreaClient { + client_id: ClientId, + time_join: SystemTime, +} + + +#[derive(Copy, Clone)] +pub struct InnerClientArea { + clients: [Option; N], } -impl ClientArea<{N}> { - pub fn new() -> ClientArea<{N}> { - let mut clients: [std::mem::MaybeUninit>; N] = unsafe { +impl InnerClientArea<{N}> { + pub fn new() -> InnerClientArea<{N}> { + let mut clients: [std::mem::MaybeUninit>; N] = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; for i in clients.iter_mut() { i.write(None); } - ClientArea { - clients: unsafe { (&clients as *const _ as *const [Option; N]).read()} + InnerClientArea { + clients: unsafe { (&clients as *const _ as *const [Option; N]).read()} } } - - + + fn add(&mut self, id: ClientId) -> Option { for (i, client) in self.clients.iter_mut().enumerate() { if client.is_none() { - *client = Some(id); + *client = Some(AreaClient{ + client_id: id, + time_join: SystemTime::now(), + }); return Some(i); } } return None; } fn remove(&mut self, id: ClientId) { - for client in self.clients.iter_mut() { - if *client == Some(id) { - *client = None + for areaclient in self.clients.iter_mut() { + if let Some(client) = *areaclient { + if client.client_id == id { + *areaclient = None; + } } } } -} -pub type Lobby = ClientArea<12>; -pub type Room = ClientArea<4>; + fn contains(&self, id: ClientId) -> bool { + self.clients.iter() + .filter(|k| k.is_some()) + .map(|k| k.unwrap() ) + .fold(false, |acc, k| { + if acc { + acc + } + else if k.client_id == id { + true + } + else { + false + } + }) + } +} pub type LobbyId = usize; pub type RoomId = usize; +pub type Lobby = InnerClientArea<12>; +pub type Room = InnerClientArea<4>; + pub struct ClientLocation { lobbies: [Lobby; 15], rooms: [Option; 128], } +trait ClientArea<'a> { + fn clients(&'a self) -> std::slice::Iter<'_, Option>; +} + + +impl<'a> ClientArea<'a> for Lobby { + fn clients(&'a self) -> std::slice::Iter<'_, Option> { + self.clients.iter() + } +} + +impl<'a> ClientArea<'a> for Room { + fn clients(&'a self) -> std::slice::Iter<'_, Option> { + self.clients.iter() + } +} + + +#[derive(Debug)] +pub struct ClientAtLocation { + pub client_id: ClientId, + pub index: usize, +} + + +pub struct Area<'a> { + area: &'a dyn ClientArea<'a>, + index: usize, +} +impl<'a> Area<'a> { + fn new(area: &'a dyn ClientArea<'a>, index: usize) -> Area<'a> { + Area { + area: area, + index: index, + } + } + pub fn clients(&'a self) -> Vec { + self.area.clients() + .enumerate() + .filter(|(_i, k)| k.is_some()) + .map(|(i, k)| (i, k.unwrap()) ) + .map(|(i, k)| ClientAtLocation { + client_id: k.client_id, + index: i + }).collect() + } + // TODO: Result in cases where no one is in the area? + pub fn leader(&self) -> ClientAtLocation { + self.area.clients() + .enumerate() + .filter(|(i, k)| k.is_some()) + .map(|(i, k)| (i, k.unwrap()) ) + .fold((ClientAtLocation { + client_id: ClientId(0), + index: 0 + }, SystemTime::UNIX_EPOCH), + |(acc, time), (i, k)| { + if time > k.time_join { + (ClientAtLocation { + client_id: k.client_id, + index: i, + }, k.time_join) + } + else { + (acc, time) + } + }).0 + } + + pub fn id(&self) -> usize { + self.index + } +} + +#[derive(Debug)] pub enum JoinRoomError { RoomDoesNotExist, RoomFull, } +#[derive(Debug)] pub enum JoinLobbyError { LobbyDoesNotExist, LobbyFull, @@ -87,21 +192,21 @@ impl ClientLocation { .ok_or(JoinRoomError::RoomFull) } - pub fn get_client_neighbors(&self, id: ClientId) -> Vec { - for lobby in self.lobbies.iter() { - if lobby.clients.contains(&Some(id)) { - return lobby.clients.iter().filter(|c| c.is_some()).map(|c| c.unwrap()).collect(); + pub fn get_area_by_user(&self, id: ClientId) -> Area { + for (i, lobby) in self.lobbies.iter().enumerate() { + if lobby.contains(id) { + return Area::new(lobby, i); } } - for room in self.rooms.iter() { + for (i, room) in self.rooms.iter().enumerate() { if let Some(room) = room { - if room.clients.contains(&Some(id)) { - return room.clients.iter().filter(|c| c.is_some()).map(|c| c.unwrap()).collect(); + if room.contains(id){ + return Area::new(room, i); } } } - - panic!() + + panic!("client is not in a room/lobby") } } diff --git a/src/ship/mod.rs b/src/ship/mod.rs index ed1787d..dcfe1b2 100644 --- a/src/ship/mod.rs +++ b/src/ship/mod.rs @@ -1,2 +1,3 @@ pub mod ship; pub mod location; +pub mod character; diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 2c930db..f68c321 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -20,6 +20,7 @@ use crate::entity::character::Character; use crate::entity::item::ItemLocation; use crate::login::login::get_login_status; use crate::ship::location::ClientLocation; +use crate::ship::character::{CharacterBuilder, FullCharacterBuilder}; pub const SHIP_PORT: u16 = 23423; @@ -143,17 +144,7 @@ impl ShipServerState { let client = self.clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; client.block = pkt.item as u32; - let mut fc = character::FullCharacter::default(); - fc.character = client.character.character; - let stats = self.level_table.get_stats_from_exp(character::Class::from(fc.character.ch_class), fc.character.exp); - fc.character.hp = stats.1.hp; - fc.character.atp = stats.1.atp; - fc.character.mst = stats.1.mst; - fc.character.evp = stats.1.evp; - fc.character.dfp = stats.1.dfp; - fc.character.ata = stats.1.ata; - fc.character.lck = stats.1.lck; - fc.character.level = stats.0 - 1; + let (level, stats) = self.level_table.get_stats_from_exp(character::Class::from(client.character.character.ch_class), client.character.character.exp); let items = self.entity_gateway.get_items_by_character(&client.character); let (mut inventory, inv_len) = items @@ -173,11 +164,17 @@ impl ShipServerState { inv[index].item_id = item.id; (inv, len+1) }); - fc.inventory.items = inventory; - fc.inventory.item_count = inv_len; - - fc.key_team_config.key_config = client.settings.settings.key_config; - fc.key_team_config.joystick_config = client.settings.settings.joystick_config; + let fc = FullCharacterBuilder::new() + .character(&CharacterBuilder::new() + .character(&client.character.character) + .stats(&stats) + .level(level) + .build()) + .inventory(&inventory, inv_len) + .key_config(&client.settings.settings.key_config) + .joystick_config(&client.settings.settings.joystick_config) + //.team_config(&joystick_config) + .build(); Ok(vec![ SendShipPacket::FullCharacter(FullCharacter {