From 7d5c4e01652076b3cdfe99543e4bce3e85b5e40d Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Jul 2022 20:48:55 -0600 Subject: [PATCH] move bank code out of state --- src/ship/character.rs | 2 +- src/ship/items/actions.rs | 3 +- src/ship/items/bank.rs | 340 +++++++++++++++++++++++++++++ src/ship/items/mod.rs | 1 + src/ship/items/state.rs | 328 +--------------------------- src/ship/packet/builder/message.rs | 2 +- 6 files changed, 348 insertions(+), 328 deletions(-) create mode 100644 src/ship/items/bank.rs diff --git a/src/ship/character.rs b/src/ship/character.rs index 674e17d..454b2c8 100644 --- a/src/ship/character.rs +++ b/src/ship/character.rs @@ -2,7 +2,7 @@ use libpso::character::character; use crate::common::leveltable::CharacterStats; use crate::entity::character::CharacterEntity; //use crate::ship::items::{CharacterInventory, CharacterBank}; -use crate::ship::items::state::{BankState}; +use crate::ship::items::bank::BankState; use crate::ship::items::inventory::InventoryState; use crate::entity::item::Meseta; diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 9753043..e6ee1b4 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -8,7 +8,8 @@ use std::pin::Pin; use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; -use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, BankItem, BankItemDetail, IndividualItemDetail}; +use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail}; +use crate::ship::items::bank::{BankItem, BankItemDetail}; use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction}; use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; use crate::ship::items::floor::{FloorItem, FloorItemDetail}; diff --git a/src/ship/items/bank.rs b/src/ship/items/bank.rs new file mode 100644 index 0000000..5abef95 --- /dev/null +++ b/src/ship/items/bank.rs @@ -0,0 +1,340 @@ +use std::cmp::Ordering; +use libpso::character::character; +use crate::ship::items::ClientItemId; +use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, BankEntity, BankItemEntity, BankName}; +use std::future::Future; + +use crate::entity::character::CharacterEntityId; +use crate::ship::items::state::ItemStateError; +use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult}; +use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; + + +#[derive(thiserror::Error, Debug)] +pub enum BankError { + #[error("bank full")] + BankFull, + #[error("stack full")] + StackFull, + #[error("meseta full")] + MesetaFull, +} + + +#[derive(Clone, Debug)] +pub enum BankItemDetail { + Individual(IndividualItemDetail), + Stacked(StackedItemDetail), +} + +impl BankItemDetail { + fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> { + match self { + BankItemDetail::Stacked(sitem) => Some(sitem), + _ => None, + } + } + + pub fn as_client_bytes(&self) -> [u8; 16] { + match self { + BankItemDetail::Individual(item) => { + match &item.item { + ItemDetail::Weapon(w) => w.as_bytes(), + ItemDetail::Armor(a) => a.as_bytes(), + ItemDetail::Shield(s) => s.as_bytes(), + ItemDetail::Unit(u) => u.as_bytes(), + ItemDetail::Tool(t) => t.as_individual_bytes(), + ItemDetail::TechniqueDisk(d) => d.as_bytes(), + ItemDetail::Mag(m) => m.as_bytes(), + ItemDetail::ESWeapon(e) => e.as_bytes(), + } + }, + BankItemDetail::Stacked(item) => { + item.tool.as_stacked_bytes(item.entity_ids.len()) + }, + } + } +} + + +#[derive(Clone, Debug)] +pub struct BankItem { + pub item_id: ClientItemId, + pub item: BankItemDetail, +} + +impl BankItem { + pub async fn with_entity_id(&self, mut param: T, mut func: F) -> Result + where + F: FnMut(T, ItemEntityId) -> Fut, + Fut: Future>, + { + match &self.item { + BankItemDetail::Individual(individual_item) => { + param = func(param, individual_item.entity_id).await?; + }, + BankItemDetail::Stacked(stacked_item) => { + for entity_id in &stacked_item.entity_ids { + param = func(param, *entity_id).await?; + } + } + } + Ok(param) + } +} + + +#[derive(Clone, Debug)] +pub struct Bank(Vec); + +impl Bank { + pub fn new(items: Vec) -> Bank { + Bank(items) + } +} + +#[derive(Clone, Debug)] +pub struct BankState { + pub character_id: CharacterEntityId, + pub item_id_counter: u32, + pub name: BankName, + pub bank: Bank, + pub meseta: Meseta, +} + +impl BankState { + pub fn new(character_id: CharacterEntityId, name: BankName, mut bank: Bank, meseta: Meseta) -> BankState { + bank.0.sort(); + BankState { + character_id, + item_id_counter: 0, + name, + bank, + meseta, + } + } + + pub fn count(&self) -> usize { + self.bank.0.len() + } + + pub fn initialize_item_ids(&mut self, base_item_id: u32) { + for (i, item) in self.bank.0.iter_mut().enumerate() { + item.item_id = ClientItemId(base_item_id + i as u32); + } + self.item_id_counter = base_item_id + self.bank.0.len() as u32; + } + + pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if self.meseta.0 + amount > 999999 { + return Err(ItemStateError::FullOfMeseta) + } + self.meseta.0 += amount; + Ok(()) + } + + pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if amount > self.meseta.0 { + return Err(ItemStateError::InvalidMesetaRemoval(amount)) + } + self.meseta.0 -= amount; + Ok(()) + } + + pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result { + match item.item { + InventoryItemDetail::Individual(iitem) => { + if self.bank.0.len() >= 30 { + Err(BankError::BankFull) + } + else { + self.bank.0.push(BankItem { + item_id: item.item_id, + item: BankItemDetail::Individual(iitem) + }); + self.bank.0.sort(); + Ok(AddItemResult::NewItem) + } + }, + InventoryItemDetail::Stacked(sitem) => { + let existing_stack = self.bank.0 + .iter_mut() + .filter_map(|item| item.item.stacked_mut()) + .find(|item| { + item.tool == sitem.tool + }); + match existing_stack { + Some(existing_stack) => { + if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { + Err(BankError::StackFull) + } + else { + existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); + Ok(AddItemResult::AddToStack) + } + }, + None => { + if self.bank.0.len() >= 30 { + Err(BankError::BankFull) + } + else { + self.bank.0.push(BankItem { + item_id: item.item_id, + item: BankItemDetail::Stacked(sitem) + }); + self.bank.0.sort(); + Ok(AddItemResult::NewItem) + } + } + } + } + } + } + + pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option { + let idx = self.bank.0 + .iter() + .position(|i| i.item_id == *item_id)?; + match &mut self.bank.0[idx].item { + BankItemDetail::Individual(_individual_item) => { + Some(self.bank.0.remove(idx)) + }, + BankItemDetail::Stacked(stacked_item) => { + let remove_all = match stacked_item.entity_ids.len().cmp(&(amount as usize)) { + Ordering::Equal => true, + Ordering::Greater => false, + Ordering::Less => return None, + }; + + if remove_all { + Some(self.bank.0.remove(idx)) + } + else { + let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect(); + self.item_id_counter += 1; + Some(BankItem { + item_id: ClientItemId(self.item_id_counter), + item: BankItemDetail::Stacked(StackedItemDetail { + entity_ids, + tool: stacked_item.tool, + })}) + } + } + } + } + + pub fn as_client_bank_items(&self) -> character::Bank { + self.bank.0.iter() + .enumerate() + .fold(character::Bank::default(), |mut bank, (slot, item)| { + bank.item_count = (slot + 1) as u32; + let bytes = item.item.as_client_bytes(); + bank.items[slot].data1.copy_from_slice(&bytes[0..12]); + bank.items[slot].data2.copy_from_slice(&bytes[12..16]); + bank.items[slot].item_id = item.item_id.0; + + bank + }) + } + + pub fn as_client_bank_request(&self) -> Vec { + self.bank.0.iter() + .map(|item| { + let bytes = item.item.as_client_bytes(); + let mut data1 = [0; 12]; + let mut data2 = [0; 4]; + data1.copy_from_slice(&bytes[0..12]); + data2.copy_from_slice(&bytes[12..16]); + let amount = match &item.item { + BankItemDetail::Individual(_individual_bank_item) => { + 1 + }, + BankItemDetail::Stacked(stacked_bank_item) => { + stacked_bank_item.count() + }, + }; + character::BankItem { + data1, + data2, + item_id: item.item_id.0, + amount: amount as u16, + flags: 1, + } + }) + .collect() + } + + pub fn as_bank_entity(&self) -> BankEntity { + BankEntity { + items: self.bank.0.iter() + .map(|item| { + match &item.item { + BankItemDetail::Individual(item) => { + BankItemEntity::Individual(ItemEntity { + id: item.entity_id, + item: item.item.clone(), + }) + }, + BankItemDetail::Stacked(items) => { + BankItemEntity::Stacked(items.entity_ids.iter() + .map(|id| { + ItemEntity { + id: *id, + item: ItemDetail::Tool(items.tool) + } + }) + .collect()) + }, + } + }) + .collect() + } + } +} + +impl std::cmp::PartialEq for BankItem { + fn eq(&self, other: &BankItem) -> bool { + let mut self_bytes = [0u8; 4]; + let mut other_bytes = [0u8; 4]; + self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); + + let self_value = u32::from_be_bytes(self_bytes); + let other_value = u32::from_be_bytes(other_bytes); + + self_value.eq(&other_value) + } +} + +impl std::cmp::Eq for BankItem {} + +impl std::cmp::PartialOrd for BankItem { + fn partial_cmp(&self, other: &BankItem) -> Option { + let mut self_bytes = [0u8; 4]; + let mut other_bytes = [0u8; 4]; + self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); + + + let self_value = u32::from_be_bytes(self_bytes); + let other_value = u32::from_be_bytes(other_bytes); + + self_value.partial_cmp(&other_value) + } +} + +impl std::cmp::Ord for BankItem { + fn cmp(&self, other: &BankItem) -> std::cmp::Ordering { + let mut self_bytes = [0u8; 4]; + let mut other_bytes = [0u8; 4]; + self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); + + + let self_value = u32::from_le_bytes(self_bytes); + let other_value = u32::from_le_bytes(other_bytes); + + self_value.cmp(&other_value) + } +} + diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index e318df8..4b9913b 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -4,6 +4,7 @@ pub mod apply_item; pub mod itemstateaction; pub mod inventory; pub mod floor; +pub mod bank; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)] pub struct ClientItemId(pub u32); diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 84bd883..70a5ddf 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -1,9 +1,6 @@ -use std::cmp::Ordering; use std::collections::HashMap; -use libpso::character::character; use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankEntity, BankItemEntity, BankName}; -use std::future::Future; +use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankName}; use crate::ship::location::{AreaClient, RoomId}; use crate::entity::character::{CharacterEntity, CharacterEntityId}; @@ -14,8 +11,8 @@ use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState}; - use crate::ship::items::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType}; +use crate::ship::items::bank::{Bank, BankState, BankItem, BankItemDetail, BankError}; // TODO: Commit trait that ItemStateProxy and EntityTransaction implement that .commit requires and acts on upon everything succeeding (like 3 less lines of code!) @@ -136,79 +133,8 @@ impl StackedItemDetail { } } -#[derive(Clone, Debug)] -pub enum BankItemDetail { - Individual(IndividualItemDetail), - Stacked(StackedItemDetail), -} - -impl BankItemDetail { - fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> { - match self { - BankItemDetail::Stacked(sitem) => Some(sitem), - _ => None, - } - } - - pub fn as_client_bytes(&self) -> [u8; 16] { - match self { - BankItemDetail::Individual(item) => { - match &item.item { - ItemDetail::Weapon(w) => w.as_bytes(), - ItemDetail::Armor(a) => a.as_bytes(), - ItemDetail::Shield(s) => s.as_bytes(), - ItemDetail::Unit(u) => u.as_bytes(), - ItemDetail::Tool(t) => t.as_individual_bytes(), - ItemDetail::TechniqueDisk(d) => d.as_bytes(), - ItemDetail::Mag(m) => m.as_bytes(), - ItemDetail::ESWeapon(e) => e.as_bytes(), - } - }, - BankItemDetail::Stacked(item) => { - item.tool.as_stacked_bytes(item.entity_ids.len()) - }, - } - } -} - -#[derive(Clone, Debug)] -pub struct BankItem { - pub item_id: ClientItemId, - pub item: BankItemDetail, -} - -impl BankItem { - pub async fn with_entity_id(&self, mut param: T, mut func: F) -> Result - where - F: FnMut(T, ItemEntityId) -> Fut, - Fut: Future>, - { - match &self.item { - BankItemDetail::Individual(individual_item) => { - param = func(param, individual_item.entity_id).await?; - }, - BankItemDetail::Stacked(stacked_item) => { - for entity_id in &stacked_item.entity_ids { - param = func(param, *entity_id).await?; - } - } - } - Ok(param) - } -} - -#[derive(thiserror::Error, Debug)] -pub enum BankError { - #[error("bank full")] - BankFull, - #[error("stack full")] - StackFull, - #[error("meseta full")] - MesetaFull, -} - #[derive(Clone)] pub enum AddItemResult { NewItem, @@ -216,254 +142,6 @@ pub enum AddItemResult { Meseta, } -#[derive(Clone, Debug)] -pub struct Bank(Vec); - -#[derive(Clone, Debug)] -pub struct BankState { - character_id: CharacterEntityId, - item_id_counter: u32, - pub name: BankName, - bank: Bank, - pub meseta: Meseta, -} - -impl BankState { - pub fn new(character_id: CharacterEntityId, name: BankName, mut bank: Bank, meseta: Meseta) -> BankState { - bank.0.sort(); - BankState { - character_id, - item_id_counter: 0, - name, - bank, - meseta, - } - } - - pub fn count(&self) -> usize { - self.bank.0.len() - } - - pub fn initialize_item_ids(&mut self, base_item_id: u32) { - for (i, item) in self.bank.0.iter_mut().enumerate() { - item.item_id = ClientItemId(base_item_id + i as u32); - } - self.item_id_counter = base_item_id + self.bank.0.len() as u32; - } - - pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { - if self.meseta.0 + amount > 999999 { - return Err(ItemStateError::FullOfMeseta) - } - self.meseta.0 += amount; - Ok(()) - } - - pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { - if amount > self.meseta.0 { - return Err(ItemStateError::InvalidMesetaRemoval(amount)) - } - self.meseta.0 -= amount; - Ok(()) - } - - pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result { - match item.item { - InventoryItemDetail::Individual(iitem) => { - if self.bank.0.len() >= 30 { - Err(BankError::BankFull) - } - else { - self.bank.0.push(BankItem { - item_id: item.item_id, - item: BankItemDetail::Individual(iitem) - }); - self.bank.0.sort(); - Ok(AddItemResult::NewItem) - } - }, - InventoryItemDetail::Stacked(sitem) => { - let existing_stack = self.bank.0 - .iter_mut() - .filter_map(|item| item.item.stacked_mut()) - .find(|item| { - item.tool == sitem.tool - }); - match existing_stack { - Some(existing_stack) => { - if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { - Err(BankError::StackFull) - } - else { - existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); - Ok(AddItemResult::AddToStack) - } - }, - None => { - if self.bank.0.len() >= 30 { - Err(BankError::BankFull) - } - else { - self.bank.0.push(BankItem { - item_id: item.item_id, - item: BankItemDetail::Stacked(sitem) - }); - self.bank.0.sort(); - Ok(AddItemResult::NewItem) - } - } - } - } - } - } - - pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option { - let idx = self.bank.0 - .iter() - .position(|i| i.item_id == *item_id)?; - match &mut self.bank.0[idx].item { - BankItemDetail::Individual(_individual_item) => { - Some(self.bank.0.remove(idx)) - }, - BankItemDetail::Stacked(stacked_item) => { - let remove_all = match stacked_item.entity_ids.len().cmp(&(amount as usize)) { - Ordering::Equal => true, - Ordering::Greater => false, - Ordering::Less => return None, - }; - - if remove_all { - Some(self.bank.0.remove(idx)) - } - else { - let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect(); - self.item_id_counter += 1; - Some(BankItem { - item_id: ClientItemId(self.item_id_counter), - item: BankItemDetail::Stacked(StackedItemDetail { - entity_ids, - tool: stacked_item.tool, - })}) - } - } - } - } - - pub fn as_client_bank_items(&self) -> character::Bank { - self.bank.0.iter() - .enumerate() - .fold(character::Bank::default(), |mut bank, (slot, item)| { - bank.item_count = (slot + 1) as u32; - let bytes = item.item.as_client_bytes(); - bank.items[slot].data1.copy_from_slice(&bytes[0..12]); - bank.items[slot].data2.copy_from_slice(&bytes[12..16]); - bank.items[slot].item_id = item.item_id.0; - - bank - }) - } - - pub fn as_client_bank_request(&self) -> Vec { - self.bank.0.iter() - .map(|item| { - let bytes = item.item.as_client_bytes(); - let mut data1 = [0; 12]; - let mut data2 = [0; 4]; - data1.copy_from_slice(&bytes[0..12]); - data2.copy_from_slice(&bytes[12..16]); - let amount = match &item.item { - BankItemDetail::Individual(_individual_bank_item) => { - 1 - }, - BankItemDetail::Stacked(stacked_bank_item) => { - stacked_bank_item.count() - }, - }; - character::BankItem { - data1, - data2, - item_id: item.item_id.0, - amount: amount as u16, - flags: 1, - } - }) - .collect() - } - - pub fn as_bank_entity(&self) -> BankEntity { - BankEntity { - items: self.bank.0.iter() - .map(|item| { - match &item.item { - BankItemDetail::Individual(item) => { - BankItemEntity::Individual(ItemEntity { - id: item.entity_id, - item: item.item.clone(), - }) - }, - BankItemDetail::Stacked(items) => { - BankItemEntity::Stacked(items.entity_ids.iter() - .map(|id| { - ItemEntity { - id: *id, - item: ItemDetail::Tool(items.tool) - } - }) - .collect()) - }, - } - }) - .collect() - } - } -} - -impl std::cmp::PartialEq for BankItem { - fn eq(&self, other: &BankItem) -> bool { - let mut self_bytes = [0u8; 4]; - let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); - - let self_value = u32::from_be_bytes(self_bytes); - let other_value = u32::from_be_bytes(other_bytes); - - self_value.eq(&other_value) - } -} - -impl std::cmp::Eq for BankItem {} - -impl std::cmp::PartialOrd for BankItem { - fn partial_cmp(&self, other: &BankItem) -> Option { - let mut self_bytes = [0u8; 4]; - let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); - - - let self_value = u32::from_be_bytes(self_bytes); - let other_value = u32::from_be_bytes(other_bytes); - - self_value.partial_cmp(&other_value) - } -} - -impl std::cmp::Ord for BankItem { - fn cmp(&self, other: &BankItem) -> std::cmp::Ordering { - let mut self_bytes = [0u8; 4]; - let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); - - - let self_value = u32::from_le_bytes(self_bytes); - let other_value = u32::from_le_bytes(other_bytes); - - self_value.cmp(&other_value) - } -} - pub struct ItemState { character_inventory: HashMap, @@ -582,7 +260,7 @@ impl ItemState { .collect::, _>>()?; let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &BankName("".into())).await?; - let bank_state = BankState::new(character.id, BankName("".into()), Bank(bank_items), bank_meseta); + let bank_state = BankState::new(character.id, BankName("".into()), Bank::new(bank_items), bank_meseta); self.character_inventory.insert(character.id, inventory_state); self.character_bank.insert(character.id, bank_state); diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index fa9368c..ac60484 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -6,7 +6,7 @@ use crate::ship::ship::{ShipError}; use crate::ship::items::ClientItemId; use crate::ship::items::inventory::InventoryItem; use crate::ship::items::state::IndividualItemDetail; -use crate::ship::items::state::BankState; +use crate::ship::items::bank::BankState; use crate::ship::items::floor::FloorItem; use crate::ship::location::AreaClient; use std::convert::TryInto;