diff --git a/src/common/mainloop/client.rs b/src/common/mainloop/client.rs index 758d075..1d24600 100644 --- a/src/common/mainloop/client.rs +++ b/src/common/mainloop/client.rs @@ -191,7 +191,6 @@ where C: PSOCipher, { let buf = pkt.as_bytes(); - trace!("[send buf] {:?}", buf); let cbuf = cipher.encrypt(&buf)?; socket.write_all(&cbuf).await?; Ok(()) diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index 2a8b65b..75518e1 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -19,15 +19,12 @@ pub enum GatewayError { #[async_trait::async_trait] pub trait EntityGateway: Send + Sync { - async fn transaction<'a>(&'a mut self) -> Result, GatewayError> - { - unimplemented!(); - } + type Transaction: EntityGatewayTransaction + Clone; async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, _func: F) -> Result where - Fut: Future, R), E>> + Send + 'a, - F: FnOnce(Box) -> Fut + Send, + Fut: Future> + Send + 'a, + F: FnOnce(Self::Transaction) -> Fut + Send, R: Send, E: From, Self: Sized @@ -155,12 +152,14 @@ pub trait EntityGateway: Send + Sync { #[async_trait::async_trait] -pub trait EntityGatewayTransaction: Send + Sync { - fn gateway(&mut self) -> &mut dyn EntityGateway { +pub trait EntityGatewayTransaction: Send + Sync + Sized { + type ParentGateway: EntityGateway + Clone; + + fn gateway(&mut self) -> &mut Self::ParentGateway { unimplemented!() } - async fn commit(self: Box) -> Result<(), GatewayError> { + async fn commit(self) -> Result<(), GatewayError> { unimplemented!() } } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index ba6f665..a813a93 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -9,10 +9,10 @@ use crate::entity::item::*; use async_std::sync::{Arc, Mutex}; -// TODO: implement multiple banks -pub struct InMemoryGatewayTransaction<'a> { +#[derive(Clone)] +pub struct InMemoryGatewayTransaction { working_gateway: InMemoryGateway, - original_gateway: &'a mut InMemoryGateway, + original_gateway: InMemoryGateway, } @@ -30,7 +30,9 @@ where // functions here have been skipped as they are not used in transactions, add as needed #[async_trait::async_trait] -impl<'a> EntityGateway for InMemoryGatewayTransaction<'a> { +impl EntityGateway for InMemoryGatewayTransaction { + type Transaction = InMemoryGatewayTransaction; + async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { self.working_gateway.create_user(user).await } @@ -182,12 +184,14 @@ impl<'a> EntityGateway for InMemoryGatewayTransaction<'a> { } #[async_trait::async_trait] -impl<'a> EntityGatewayTransaction for InMemoryGatewayTransaction<'a> { - fn gateway(&mut self) -> &mut dyn EntityGateway { +impl EntityGatewayTransaction for InMemoryGatewayTransaction { + type ParentGateway = InMemoryGatewayTransaction; + + fn gateway(&mut self) -> &mut Self::ParentGateway { self } - async fn commit(mut self: Box) -> Result<(), GatewayError> { + async fn commit(mut self) -> Result<(), GatewayError> { self.original_gateway.users.lock().await.extend(self.working_gateway.users.lock().await.clone()); self.original_gateway.user_settings.lock().await.extend(self.working_gateway.user_settings.lock().await.clone()); self.original_gateway.characters.lock().await.extend(self.working_gateway.characters.lock().await.clone()); @@ -298,49 +302,12 @@ fn apply_modifiers(items: &BTreeMap, #[async_trait::async_trait] impl EntityGateway for InMemoryGateway { - async fn transaction<'a>(&'a mut self) -> Result, GatewayError> - { - let working_gateway = { - let users = self.users.lock().await.clone(); - let user_settings = self.user_settings.lock().await.clone(); - let characters = self.characters.lock().await.clone(); - let character_meseta = self.character_meseta.lock().await.clone(); - let bank_meseta = self.bank_meseta.lock().await.clone(); - let items = self.items.lock().await.clone(); - let inventories = self.inventories.lock().await.clone(); - let banks = self.banks.lock().await.clone(); - let equips = self.equips.lock().await.clone(); - let mag_modifiers = self.mag_modifiers.lock().await.clone(); - let weapon_modifiers = self.weapon_modifiers.lock().await.clone(); - let trades = self.trades.lock().await.clone(); - - InMemoryGateway { - users: Arc::new(Mutex::new(users)), - user_settings: Arc::new(Mutex::new(user_settings)), - characters: Arc::new(Mutex::new(characters)), - character_meseta: Arc::new(Mutex::new(character_meseta)), - bank_meseta: Arc::new(Mutex::new(bank_meseta)), - items: Arc::new(Mutex::new(items)), - inventories: Arc::new(Mutex::new(inventories)), - banks: Arc::new(Mutex::new(banks)), - equips: Arc::new(Mutex::new(equips)), - mag_modifiers: Arc::new(Mutex::new(mag_modifiers)), - weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)), - trades: Arc::new(Mutex::new(trades)), - } - }; - - Ok(Box::new(InMemoryGatewayTransaction { - working_gateway, - original_gateway: self, - })) - } - + type Transaction = InMemoryGatewayTransaction; async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, func: F) -> Result where - Fut: Future, R), E>> + Send + 'a, - F: FnOnce(Box) -> Fut + Send, + Fut: Future> + Send + 'a, + F: FnOnce(Self::Transaction) -> Fut + Send, R: Send, E: From, { @@ -372,10 +339,10 @@ impl EntityGateway for InMemoryGateway { trades: Arc::new(Mutex::new(trades)), }; - let transaction = Box::new(InMemoryGatewayTransaction { + let transaction = InMemoryGatewayTransaction { working_gateway, - original_gateway: self, - }); + original_gateway: self.clone(), + }; let (transaction, result) = func(transaction).await?; diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 813c810..d6ab8ee 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -4,6 +4,7 @@ use std::convert::{From, TryFrom, Into}; use futures::{Future, TryStreamExt}; use async_std::stream::StreamExt; +use async_std::sync::{Arc, Mutex}; use libpso::character::guildcard; use crate::entity::account::*; use crate::entity::character::*; @@ -21,31 +22,41 @@ mod embedded { } +#[derive(Clone)] pub struct PostgresTransaction<'t> { - pgtransaction: sqlx::Transaction<'t, sqlx::Postgres>, + pgtransaction: Arc>>, } #[async_trait::async_trait] impl<'t> EntityGatewayTransaction for PostgresTransaction<'t> { - fn gateway(&mut self) -> &mut dyn EntityGateway { + type ParentGateway = PostgresTransaction<'t>; + + fn gateway(&mut self) -> &mut Self::ParentGateway { self } - async fn commit(self: Box) -> Result<(), GatewayError> { - self.pgtransaction.commit().await?; + //async fn commit(self: Box) -> Result<(), GatewayError> { + async fn commit(self) -> Result<(), GatewayError> { + //self.pgtransaction.lock().await.commit().await?; + Arc::try_unwrap(self.pgtransaction) + .unwrap() + .into_inner() + .commit() + .await?; Ok(()) } } #[derive(Clone)] -pub struct PostgresGateway { +pub struct PostgresGateway<'t> { pool: sqlx::Pool, + _t: std::marker::PhantomData<&'t ()>, } -impl PostgresGateway { - pub fn new(host: &str, dbname: &str, username: &str, password: &str) -> PostgresGateway { +impl<'t> PostgresGateway<'t> { + pub fn new(host: &str, dbname: &str, username: &str, password: &str) -> PostgresGateway<'t> { let mut conn = refinery::config::Config::new(refinery::config::ConfigDbType::Postgres) .set_db_host(host) .set_db_user(username) @@ -61,6 +72,7 @@ impl PostgresGateway { PostgresGateway { pool, + _t: Default::default(), } } } @@ -581,24 +593,19 @@ async fn set_character_playtime(conn: &mut sqlx::PgConnection, char_id: &Charact } #[async_trait::async_trait] -impl EntityGateway for PostgresGateway { - async fn transaction<'a>(&'a mut self) -> Result, GatewayError> - { - Ok(Box::new(PostgresTransaction { - pgtransaction: self.pool.begin().await?, - })) - } +impl<'t> EntityGateway for PostgresGateway<'t> { + type Transaction = PostgresTransaction<'t>; async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, func: F) -> Result where - Fut: Future, R), E>> + Send + 'a, - F: FnOnce(Box) -> Fut + Send, + Fut: Future> + Send + 'a, + F: FnOnce(Self::Transaction) -> Fut + Send, R: Send, E: From, { - let transaction = Box::new(PostgresTransaction { - pgtransaction: self.pool.begin().await.map_err(|_| ()).unwrap() - }); + let transaction = PostgresTransaction { + pgtransaction: Arc::new(Mutex::new(self.pool.begin().await.map_err(|_| ()).unwrap())) + }; let (transaction, result) = func(transaction).await.map_err(|_| ()).unwrap(); transaction.commit().await.map_err(|_| ()).unwrap(); Ok(result) @@ -728,44 +735,46 @@ impl EntityGateway for PostgresGateway { #[async_trait::async_trait] impl<'c> EntityGateway for PostgresTransaction<'c> { + type Transaction = PostgresTransaction<'c>; + async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { - create_user(&mut *self.pgtransaction, user).await + create_user(&mut *self.pgtransaction.lock().await, user).await } async fn get_user_by_id(&mut self, id: UserAccountId) -> Result { - get_user_by_id(&mut *self.pgtransaction, id).await + get_user_by_id(&mut *self.pgtransaction.lock().await, id).await } async fn get_user_by_name(&mut self, username: String) -> Result { - get_user_by_name(&mut *self.pgtransaction, username).await + get_user_by_name(&mut *self.pgtransaction.lock().await, username).await } async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> { - save_user(&mut *self.pgtransaction, user).await + save_user(&mut *self.pgtransaction.lock().await, user).await } async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result { - create_user_settings(&mut *self.pgtransaction, settings).await + create_user_settings(&mut *self.pgtransaction.lock().await, settings).await } async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result { - get_user_settings_by_user(&mut *self.pgtransaction, user).await + get_user_settings_by_user(&mut *self.pgtransaction.lock().await, user).await } async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> { - save_user_settings(&mut *self.pgtransaction, settings).await + save_user_settings(&mut *self.pgtransaction.lock().await, settings).await } async fn create_character(&mut self, char: NewCharacterEntity) -> Result { - create_character(&mut *self.pgtransaction, char).await + create_character(&mut *self.pgtransaction.lock().await, char).await } async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { - get_characters_by_user(&mut *self.pgtransaction, user).await + get_characters_by_user(&mut *self.pgtransaction.lock().await, user).await } async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> { - save_character(&mut *self.pgtransaction, char).await + save_character(&mut *self.pgtransaction.lock().await, char).await } async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result { @@ -777,75 +786,75 @@ impl<'c> EntityGateway for PostgresTransaction<'c> { } async fn create_item(&mut self, item: NewItemEntity) -> Result { - create_item(&mut *self.pgtransaction, item).await + create_item(&mut *self.pgtransaction.lock().await, item).await } async fn add_item_note(&mut self, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> { - add_item_note(&mut *self.pgtransaction, item_id, item_note).await + add_item_note(&mut *self.pgtransaction.lock().await, item_id, item_note).await } async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> { - feed_mag(&mut *self.pgtransaction, mag_item_id, tool_item_id).await + feed_mag(&mut *self.pgtransaction.lock().await, mag_item_id, tool_item_id).await } async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> { - change_mag_owner(&mut *self.pgtransaction, mag_item_id, character).await + change_mag_owner(&mut *self.pgtransaction.lock().await, mag_item_id, character).await } async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> { - use_mag_cell(&mut *self.pgtransaction, mag_item_id, mag_cell_id).await + use_mag_cell(&mut *self.pgtransaction.lock().await, mag_item_id, mag_cell_id).await } async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: weapon::WeaponModifier) -> Result<(), GatewayError> { - add_weapon_modifier(&mut *self.pgtransaction, item_id, modifier).await + add_weapon_modifier(&mut *self.pgtransaction.lock().await, item_id, modifier).await } async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result { - get_character_inventory(&mut *self.pgtransaction, char_id).await + get_character_inventory(&mut *self.pgtransaction.lock().await, char_id).await } async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: &BankName) -> Result { - get_character_bank(&mut *self.pgtransaction, char_id, bank_name).await + get_character_bank(&mut *self.pgtransaction.lock().await, char_id, bank_name).await } async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> { - set_character_inventory(&mut *self.pgtransaction, char_id, inventory).await + set_character_inventory(&mut *self.pgtransaction.lock().await, char_id, inventory).await } async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: &BankName) -> Result<(), GatewayError> { - set_character_bank(&mut *self.pgtransaction, char_id, bank, bank_name).await + set_character_bank(&mut *self.pgtransaction.lock().await, char_id, bank, bank_name).await } async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result { - get_character_equips(&mut *self.pgtransaction, char_id).await + get_character_equips(&mut *self.pgtransaction.lock().await, char_id).await } async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> { - set_character_equips(&mut *self.pgtransaction, char_id, equips).await + set_character_equips(&mut *self.pgtransaction.lock().await, char_id, equips).await } async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> { - set_character_meseta(&mut *self.pgtransaction, char_id, meseta).await + set_character_meseta(&mut *self.pgtransaction.lock().await, char_id, meseta).await } async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result { - get_character_meseta(&mut *self.pgtransaction, char_id).await + get_character_meseta(&mut *self.pgtransaction.lock().await, char_id).await } async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> { - set_bank_meseta(&mut *self.pgtransaction, char_id, bank, meseta).await + set_bank_meseta(&mut *self.pgtransaction.lock().await, char_id, bank, meseta).await } async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName) -> Result { - get_bank_meseta(&mut *self.pgtransaction, char_id, bank).await + get_bank_meseta(&mut *self.pgtransaction.lock().await, char_id, bank).await } async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result { - create_trade(&mut *self.pgtransaction, char_id1, char_id2).await + create_trade(&mut *self.pgtransaction.lock().await, char_id1, char_id2).await } async fn set_character_playtime(&mut self, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError> { - set_character_playtime(&mut *self.pgtransaction, char_id, playtime).await + set_character_playtime(&mut *self.pgtransaction.lock().await, char_id, playtime).await } } diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index a1f78bd..eabddb5 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -241,6 +241,13 @@ impl InventoryItemEntity { _ => None, } } + + pub fn stacked(&self) -> Option<&Vec> { + match self { + InventoryItemEntity::Stacked(i) => Some(i), + _ => None, + } + } } #[derive(Clone, Debug, Default)] diff --git a/src/entity/item/tool.rs b/src/entity/item/tool.rs index 3826bbd..d0f756e 100644 --- a/src/entity/item/tool.rs +++ b/src/entity/item/tool.rs @@ -212,7 +212,43 @@ impl ToolType { ToolType::Addslot | ToolType::PhotonDrop | ToolType::PhotonSphere | - ToolType::PhotonCrystal) + ToolType::PhotonCrystal | + ToolType::JackOLantern | + ToolType::ChristmasPresent | + ToolType::EasterEgg | + ToolType::CellOfMag502 | + ToolType::CellOfMag213 | + ToolType::PartsOfRobochao | + ToolType::HeartOfOpaOpa | + ToolType::HeartOfPian | + ToolType::HeartOfChao | + ToolType::HeartOfChuChu | + ToolType::HeartOfAngel | + ToolType::HeartOfDevil | + ToolType::KitOfHamburger | + ToolType::PanthersSpirit | + ToolType::KitOfMark3 | + ToolType::KitOfMasterSystem | + ToolType::KitOfGenesis | + ToolType::KitOfSegaSaturn | + ToolType::KitOfDreamcast | + ToolType::HeartOfKapuKapu | + ToolType::Tablet | + ToolType::DragonScale | + ToolType::HeavenStrikerCoat | + ToolType::PioneerParts | + ToolType::AmitiesMemo | + ToolType::HeartOfMorolian | + ToolType::RappysBeak | + ToolType::YahoosEngine | + ToolType::DPhotonCore | + ToolType::LibertaKit | + ToolType::CellOfMag0503 | + ToolType::CellOfMag0504 | + ToolType::CellOfMag0505 | + ToolType::CellOfMag0506 | + ToolType::CellOfMag0507 + ) } pub fn max_stack(&self) -> usize { @@ -244,6 +280,41 @@ impl ToolType { ToolType::PhotonDrop => 99, ToolType::PhotonSphere => 99, ToolType::PhotonCrystal => 99, + ToolType::JackOLantern => 99, + ToolType::ChristmasPresent => 99, + ToolType::EasterEgg => 99, + ToolType::CellOfMag502 => 99, + ToolType::CellOfMag213 => 99, + ToolType::PartsOfRobochao => 99, + ToolType::HeartOfOpaOpa => 99, + ToolType::HeartOfPian => 99, + ToolType::HeartOfChao => 99, + ToolType::HeartOfChuChu => 99, + ToolType::HeartOfAngel => 99, + ToolType::HeartOfDevil => 99, + ToolType::KitOfHamburger => 99, + ToolType::PanthersSpirit => 99, + ToolType::KitOfMark3 => 99, + ToolType::KitOfMasterSystem => 99, + ToolType::KitOfGenesis => 99, + ToolType::KitOfSegaSaturn => 99, + ToolType::KitOfDreamcast => 99, + ToolType::HeartOfKapuKapu => 99, + ToolType::Tablet => 99, + ToolType::DragonScale => 99, + ToolType::HeavenStrikerCoat => 99, + ToolType::PioneerParts => 99, + ToolType::AmitiesMemo => 99, + ToolType::HeartOfMorolian => 99, + ToolType::RappysBeak => 99, + ToolType::YahoosEngine => 99, + ToolType::DPhotonCore => 99, + ToolType::LibertaKit => 99, + ToolType::CellOfMag0503 => 99, + ToolType::CellOfMag0504 => 99, + ToolType::CellOfMag0505 => 99, + ToolType::CellOfMag0506 => 99, + ToolType::CellOfMag0507 => 99, _ => 1, } } @@ -255,6 +326,7 @@ impl ToolType { ToolType::HeartOfOpaOpa | ToolType::HeartOfPian | ToolType::HeartOfChao | + ToolType::HeartOfChuChu | ToolType::HeartOfAngel | ToolType::HeartOfDevil | ToolType::KitOfHamburger | @@ -264,6 +336,7 @@ impl ToolType { ToolType::KitOfGenesis | ToolType::KitOfSegaSaturn | ToolType::KitOfDreamcast | + ToolType::HeartOfKapuKapu | ToolType::Tablet | ToolType::DragonScale | ToolType::HeavenStrikerCoat | @@ -662,7 +735,7 @@ impl Tool { } pub fn from_bytes(data: [u8; 16]) -> Result { - let t = ToolType::parse_type([data[0], data[1], data[2]]); + let t = ToolType::parse_type([data[0], data[1], data[2]]); if let Ok(t) = t { Ok(Tool { tool: t, diff --git a/src/login/character.rs b/src/login/character.rs index 87a3c4a..b6e28ab 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -838,6 +838,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { + type Transaction = (); async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result { Ok(UserSettingsEntity { id: UserSettingsId(0), @@ -879,7 +880,9 @@ mod test { async fn test_user_checksum() { #[derive(Clone)] struct TestData; - impl EntityGateway for TestData {} + impl EntityGateway for TestData { + type Transaction = (); + } let mut server = CharacterServerState::new(TestData {}, AuthToken("".into())); let send = server.handle(ClientId(1), RecvCharacterPacket::Checksum(Checksum {checksum: 1234, padding: 0, diff --git a/src/login/login.rs b/src/login/login.rs index 5b2ce52..e37b0ff 100644 --- a/src/login/login.rs +++ b/src/login/login.rs @@ -179,7 +179,7 @@ impl ServerState for LoginServerState { mod test { use super::*; use crate::entity::account::{UserAccountId}; - use crate::entity::gateway::GatewayError; + use crate::entity::gateway::{EntityGatewayTransaction, GatewayError}; const LOGIN_PACKET: RecvLoginPacket = RecvLoginPacket::Login(Login { tag: 65536, @@ -205,6 +205,14 @@ mod test { } }); + impl EntityGateway for () { + type Transaction = (); + } + + impl EntityGatewayTransaction for () { + type ParentGateway = (); + } + #[async_std::test] async fn test_correct_login() { #[derive(Clone)] @@ -213,6 +221,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { + type Transaction = (); async fn get_user_by_name(&mut self, name: String) -> Result { assert!(name == "testuser"); Ok(UserAccountEntity { @@ -271,6 +280,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { + type Transaction = (); async fn get_user_by_name(&mut self, _name: String) -> Result { Err(GatewayError::Error) } @@ -305,6 +315,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { + type Transaction = (); async fn get_user_by_name(&mut self, name: String) -> Result { assert!(name == "testuser"); Ok(UserAccountEntity { @@ -354,6 +365,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { + type Transaction = (); async fn get_user_by_name(&mut self, name: String) -> Result { assert!(name == "testuser"); Ok(UserAccountEntity { diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index e19c4ab..c6875ef 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -1,33 +1,46 @@ // TODO: replace various u32s and usizes denoting item amounts for ItemAmount(u32) for consistency use crate::ship::items::ClientItemId; use crate::entity::item::{Meseta, ItemNote}; +use async_std::sync::Arc; use std::future::Future; use std::pin::Pin; +use std::iter::IntoIterator; +use libpso::packet::{ship::Message, messages::GameMessage}; use crate::ship::map::MapArea; +use crate::ship::ship::SendShipPacket; use crate::entity::character::{CharacterEntity, CharacterEntityId}; -use crate::entity::gateway::EntityGatewayTransaction; +use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; use crate::ship::items::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail}; use crate::ship::items::bank::{BankItem, BankItemDetail}; use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; use crate::ship::items::floor::{FloorItem, FloorItemDetail}; -use crate::ship::items::apply_item::apply_item; +use crate::ship::items::apply_item::{apply_item, ApplyItemAction}; use crate::entity::item::{ItemDetail, NewItemEntity, TradeId}; use crate::entity::item::tool::Tool; use crate::entity::item::ItemModifier; use crate::ship::shops::ShopItem; use crate::ship::drops::{ItemDrop, ItemDropType}; +use crate::ship::packet::builder; +use crate::ship::location::AreaClient; + +type BoxFuture = Pin + Send>>; pub enum TriggerCreateItem { Yes, No } -pub(super) fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> +pub(super) fn take_item_from_floor( + character_id: CharacterEntityId, + item_id: ClientItemId +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway + Send, + TR: EntityGatewayTransaction + 'static, { - move |(mut item_state, transaction): (ItemStateProxy<'_>, Box) , _| { + move |(mut item_state, transaction): (ItemStateProxy, TR) , _| { Box::pin(async move { let mut floor = item_state.floor(&character_id).await?; let item = floor.take_item(&item_id).ok_or_else(|| ItemStateError::NoFloorItem(item_id))?; @@ -38,9 +51,13 @@ pub(super) fn take_item_from_floor(character_id: CharacterEntityId, item_id: Cli } } -pub(super) fn add_floor_item_to_inventory(character: &CharacterEntity) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), FloorItem) - -> Pin, Box), TriggerCreateItem), ItemStateError>> + Send + 'a>> +pub(super) fn add_floor_item_to_inventory( + character: &CharacterEntity +) -> impl Fn((ItemStateProxy, TR), FloorItem) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + Clone + 'static, { let character = character.clone(); move |(mut item_state, transaction), floor_item| { @@ -67,7 +84,7 @@ pub(super) fn add_floor_item_to_inventory(character: &CharacterEntity) let add_result = inventory.add_floor_item(floor_item)?; transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), match add_result { @@ -81,9 +98,15 @@ pub(super) fn add_floor_item_to_inventory(character: &CharacterEntity) -pub(super) fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> +pub(super) fn take_item_from_inventory( + character_id: CharacterEntityId, + item_id: ClientItemId, + amount: u32, +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { Box::pin(async move { @@ -91,7 +114,7 @@ pub(super) fn take_item_from_inventory(character_id: CharacterEntityId, item_id: let item = inventory.take_item(&item_id, amount).ok_or_else(|| ItemStateError::NoFloorItem(item_id))?; transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), item)) }) @@ -99,9 +122,15 @@ pub(super) fn take_item_from_inventory(character_id: CharacterEntityId, item_id: } -pub(super) fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32, f32)) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> +pub(super) fn add_inventory_item_to_shared_floor( + character_id: CharacterEntityId, + map_area: MapArea, + drop_position: (f32, f32, f32), +) -> impl Fn((ItemStateProxy, TR), InventoryItem) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, transaction), inventory_item| { Box::pin(async move { @@ -127,43 +156,59 @@ pub(super) fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId } -pub(super) fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +pub(super) fn take_meseta_from_inventory( + character_id: CharacterEntityId, + amount: u32, +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { Box::pin(async move { let mut inventory = item_state.inventory(&character_id).await?; inventory.remove_meseta(amount)?; transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), ())) }) } } -pub(super) fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u32) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +pub(super) fn add_meseta_to_inventory( + character_id: CharacterEntityId, + amount: u32 +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { Box::pin(async move { let mut inventory = item_state.inventory(&character_id).await?; inventory.add_meseta(amount)?; transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), ())) }) } } -pub(super) fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_area: MapArea, drop_position: (f32, f32)) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> +pub(super) fn add_meseta_to_shared_floor( + character_id: CharacterEntityId, + amount: u32, + map_area: MapArea, + drop_position: (f32, f32) +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { - move |(mut item_state, transaction), _| { Box::pin(async move { let floor_item = FloorItem { @@ -184,9 +229,14 @@ pub(super) fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount } } -pub(super) fn take_meseta_from_bank(character_id: CharacterEntityId, amount: u32) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +pub(super) fn take_meseta_from_bank( + character_id: CharacterEntityId, + amount: u32, +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { Box::pin(async move { @@ -199,9 +249,14 @@ pub(super) fn take_meseta_from_bank(character_id: CharacterEntityId, amount: u32 } } -pub(super) fn add_meseta_from_bank_to_inventory(character_id: CharacterEntityId, amount: u32) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +pub(super) fn add_meseta_from_bank_to_inventory( + character_id: CharacterEntityId, + amount: u32, +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { Box::pin(async move { @@ -215,9 +270,14 @@ pub(super) fn add_meseta_from_bank_to_inventory(character_id: CharacterEntityId, } -pub(super) fn add_meseta_to_bank(character_id: CharacterEntityId, amount: u32) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +pub(super) fn add_meseta_to_bank( + character_id: CharacterEntityId, + amount: u32, +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { Box::pin(async move { @@ -231,25 +291,35 @@ pub(super) fn add_meseta_to_bank(character_id: CharacterEntityId, amount: u32) } -pub(super) fn take_item_from_bank(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), BankItem), ItemStateError>> + Send + 'a>> +pub(super) fn take_item_from_bank( + character_id: CharacterEntityId, + item_id: ClientItemId, + amount: u32, +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { Box::pin(async move { let mut bank = item_state.bank(&character_id).await?; let item = bank.take_item(&item_id, amount).ok_or_else(|| ItemStateError::NoBankItem(item_id))?; transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?; - item_state.set_bank(bank); + item_state.set_bank(bank).await; Ok(((item_state, transaction), item)) }) } } -pub(super) fn add_bank_item_to_inventory(character: &CharacterEntity) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), BankItem) - -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> +pub(super) fn add_bank_item_to_inventory( + character: &CharacterEntity, +) -> impl Fn((ItemStateProxy, TR), BankItem) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { let character = character.clone(); move |(mut item_state, transaction), bank_item| { @@ -286,7 +356,7 @@ pub(super) fn add_bank_item_to_inventory(character: &CharacterEntity) inventory.add_item(inventory_item.clone())?; transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), inventory_item)) }) @@ -294,9 +364,13 @@ pub(super) fn add_bank_item_to_inventory(character: &CharacterEntity) } -pub(super) fn add_inventory_item_to_bank(character_id: CharacterEntityId) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +pub(super) fn add_inventory_item_to_bank( + character_id: CharacterEntityId, +) -> impl Fn((ItemStateProxy, TR), InventoryItem) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, transaction), inventory_item| { Box::pin(async move { @@ -315,7 +389,7 @@ pub(super) fn add_inventory_item_to_bank(character_id: CharacterEntityId) bank.add_inventory_item(inventory_item)?; transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?; - item_state.set_bank(bank); + item_state.set_bank(bank).await; Ok(((item_state, transaction), ())) @@ -324,16 +398,22 @@ pub(super) fn add_inventory_item_to_bank(character_id: CharacterEntityId) } -pub(super) fn equip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId, equip_slot: u8) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +pub(super) fn equip_inventory_item( + character_id: CharacterEntityId, + item_id: ClientItemId, + equip_slot: u8, +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { Box::pin(async move { let mut inventory = item_state.inventory(&character_id).await?; inventory.equip(&item_id, equip_slot); transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), ())) }) @@ -341,16 +421,21 @@ pub(super) fn equip_inventory_item(character_id: CharacterEntityId, item_id: Cli } -pub(super) fn unequip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +pub(super) fn unequip_inventory_item( + character_id: CharacterEntityId, + item_id: ClientItemId, +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { Box::pin(async move { let mut inventory = item_state.inventory(&character_id).await?; inventory.unequip(&item_id); transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), ())) }) @@ -359,9 +444,14 @@ pub(super) fn unequip_inventory_item(character_id: CharacterEntityId, item_id: C -pub(super) fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Vec) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +pub(super) fn sort_inventory_items( + character_id: CharacterEntityId, + item_ids: Vec, +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { let item_ids = item_ids.clone(); @@ -369,7 +459,7 @@ pub(super) fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Ve let mut inventory = item_state.inventory(&character_id).await?; inventory.sort(&item_ids); transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), ())) }) @@ -377,10 +467,15 @@ pub(super) fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Ve } -pub(super) fn use_consumed_item(character: CharacterEntity) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin, Box), CharacterEntity), ItemStateError>> + Send + 'a>> +pub(super) fn use_consumed_item( + character: &CharacterEntity, +) -> impl Fn((ItemStateProxy, TR), InventoryItem) + -> BoxFuture), ItemStateError>> +where + EG: EntityGateway + Clone + 'static, + TR: EntityGatewayTransaction + 'static, { + let character = character.clone(); move |(mut item_state, transaction), inventory_item| { let mut character = character.clone(); Box::pin(async move { @@ -390,17 +485,22 @@ pub(super) fn use_consumed_item(character: CharacterEntity) Ok(transaction) }}).await?; - apply_item(&mut item_state, transaction.gateway(), &mut character, inventory_item).await?; + let apply_item_actions = apply_item(&mut item_state, transaction.gateway(), &mut character, inventory_item).await?; - Ok(((item_state, transaction), character)) + Ok(((item_state, transaction), apply_item_actions)) }) } } -pub(super) fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin, Box), CharacterEntity), ItemStateError>> + Send + 'a>> +pub(super) fn feed_mag_item( + character: CharacterEntity, + mag_item_id: ClientItemId, +) -> impl Fn((ItemStateProxy, TR), InventoryItem) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, transaction), tool| { let character = character.clone(); @@ -437,20 +537,24 @@ pub(super) fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemI mag_entity.feed(food_tool); transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), character)) }) } } - -pub(super) fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId, - shop_item: &'a (dyn ShopItem + Send + Sync), - item_id: ClientItemId, - amount: u32) - -> impl Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> +#[allow(clippy::needless_lifetimes)] // clippy this lifetime is not needless get off my case +pub(super) fn add_bought_item_to_inventory<'a, EG, TR>( + character_id: CharacterEntityId, + shop_item: &'a (dyn ShopItem + Send + Sync), + item_id: ClientItemId, + amount: u32, +) -> impl Fn((ItemStateProxy, TR), ()) + -> Pin> + Send + 'a>> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { Box::pin(async move { @@ -499,16 +603,20 @@ pub(super) fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId, }; transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), inventory_item)) }) } } -pub(super) fn sell_inventory_item<'a>(character_id: CharacterEntityId) - -> impl Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> +pub(super) fn sell_inventory_item( + character_id: CharacterEntityId, +) -> impl Fn((ItemStateProxy, TR), InventoryItem) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, transaction), inventory_item| { Box::pin(async move { @@ -522,7 +630,7 @@ pub(super) fn sell_inventory_item<'a>(character_id: CharacterEntityId) Ok(transaction) }}).await?; transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), inventory_item)) }) } @@ -530,19 +638,22 @@ pub(super) fn sell_inventory_item<'a>(character_id: CharacterEntityId) #[async_recursion::async_recursion] -async fn iterate_inner<'a, I, O, T, F, FR>(state: (ItemStateProxy<'a>, Box), - mut input: Vec, - func: F, - arg: T) - -> Result<((ItemStateProxy<'a>, Box), Vec), ItemStateError> +async fn iterate_inner<'a, EG, TR, I, O, T, F, FR>( + state: (ItemStateProxy, TR), + mut input: Vec, + func: F, + arg: T, +) -> Result<((ItemStateProxy, TR), Vec), ItemStateError> where 'a: 'async_recursion, + EG: EntityGateway, + TR: EntityGatewayTransaction, I: Send, O: Send, T: Clone + Send + Sync, F: Fn(I) -> FR + Send + Sync + Clone + 'static, - FR: Fn((ItemStateProxy<'a>, Box), T) - -> Pin, Box), O), ItemStateError>> + Send + 'a>> + Send + Sync, + FR: Fn((ItemStateProxy, TR), T) + -> BoxFuture> + Send + Sync, { let item = match input.pop() { Some(item) => item, @@ -558,18 +669,20 @@ where Ok((state, output)) } -pub(super) fn iterate<'k, I, O, T, F, FR>( +pub(super) fn iterate( input: Vec, - func: F) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), T) - -> Pin, Box), Vec), ItemStateError>> + Send + 'a>> + func: F, +) -> impl Fn((ItemStateProxy, TR), T) + -> BoxFuture), ItemStateError>> where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, O: Send, I: Send + Clone + 'static + std::fmt::Debug, T: Send + Clone + 'static + std::fmt::Debug, F: Fn(I) -> FR + Send + Sync + Clone + 'static, - FR: for<'a> Fn((ItemStateProxy<'a>, Box), T) - -> Pin, Box), O), ItemStateError>> + Send + 'a>> + Send + Sync, + FR: Fn((ItemStateProxy, TR), T) + -> BoxFuture> + Send + Sync, T: Clone + Send + Sync, { move |(item_state, transaction), arg| { @@ -584,19 +697,22 @@ where #[async_recursion::async_recursion] -async fn foreach_inner<'a, O, T, F>(state: (ItemStateProxy<'a>, Box), - mut input: Vec, - func: F) - -> Result<((ItemStateProxy<'a>, Box), Vec), ItemStateError> +async fn foreach_inner<'a, EG, TR, O, T, F, I>( + state: (ItemStateProxy, TR), + mut input: I, + func: Arc, +) -> Result<((ItemStateProxy, TR), Vec), ItemStateError> where 'a: 'async_recursion, + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, O: Send, - T: Clone + Send, - F: Fn((ItemStateProxy<'a>, Box), T) - -> Pin, Box), O), ItemStateError>> + Send + 'a>> + Send + Sync, - F: Clone, + T: Send, + F: Fn((ItemStateProxy, TR), T) + -> BoxFuture> + Send + Sync, + I: Iterator + Send + Sync + 'static, { - let item = match input.pop() { + let item = match input.next() { Some(item) => item, None => return Ok((state, Vec::new())) }; @@ -609,19 +725,25 @@ where Ok((state, output)) } -pub(super) fn foreach<'k, O, T, F>(func: F) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), Vec) - -> Pin, Box), Vec), ItemStateError>> + Send + 'a>> +pub(super) fn foreach( + func: F +) -> impl Fn((ItemStateProxy, TR), I) + -> BoxFuture), ItemStateError>> where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, O: Send, T: Send + Clone + 'static + std::fmt::Debug, - F: for<'a> Fn((ItemStateProxy<'a>, Box), T) - -> Pin, Box), O), ItemStateError>> + Send + 'a>> + Send + Sync + 'static, - F: Clone, - T: Clone + Send + Sync, + F: Fn((ItemStateProxy, TR), T) + -> BoxFuture> + Send + Sync + 'static, + T: Send + Sync, + I: IntoIterator + Send + Sync + 'static, + I::IntoIter: Send + Sync, { + let func = Arc::new(func); move |(item_state, transaction), items| { let func = func.clone(); + let items = items.into_iter(); Box::pin(async move { let (state, result) = foreach_inner((item_state, transaction), items, func).await?; Ok((state, result)) @@ -629,9 +751,14 @@ where } } -pub(super) fn insert<'a, T: Send + Clone + 'a>(element: T) - -> impl Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), T), ItemStateError>> + Send + 'a>> +pub(super) fn insert<'a, EG, TR, T>( + element: T +) -> impl Fn((ItemStateProxy, TR), ()) + -> Pin> + Send + 'a>> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, + T: Send + Clone + 'a, { move |state, _| { let element = element.clone(); @@ -641,9 +768,41 @@ pub(super) fn insert<'a, T: Send + Clone + 'a>(element: T) } } -pub(super) fn add_item_to_inventory(character: CharacterEntity) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> + Clone +pub(super) fn fork( + func1: F1, + func2: F2, +) -> impl Fn((ItemStateProxy, TR), T) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, + F1: Fn((ItemStateProxy, TR), T) -> BoxFuture> + Send + Sync + 'static, + F2: Fn((ItemStateProxy, TR), T) -> BoxFuture> + Send + Sync + 'static, + T: Send + Sync + Clone + 'static, + O1: Send, + O2: Send, +{ + let func1 = Arc::new(func1); + let func2 = Arc::new(func2); + move |(item_state, transaction), input| { + let func1 = func1.clone(); + let func2 = func2.clone(); + Box::pin(async move { + let ((item_state, transaction), result1) = func1((item_state, transaction), input.clone()).await?; + let ((item_state, transaction), result2) = func2((item_state, transaction), input).await?; + + Ok(((item_state, transaction), (result1, result2))) + }) + } +} + +pub(super) fn add_item_to_inventory( + character: CharacterEntity, +) -> impl Fn((ItemStateProxy, TR), InventoryItem) + -> BoxFuture> + Clone +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, transaction), inventory_item| { let character = character.clone(); @@ -658,16 +817,22 @@ pub(super) fn add_item_to_inventory(character: CharacterEntity) inventory.add_item(inventory_item.clone())?; transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; Ok(((item_state, transaction), inventory_item)) }) } } -pub(super) fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, character_from: CharacterEntityId) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), Vec) - -> Pin, Box), Vec), ItemStateError>> + Send + 'a>> + Clone +pub(super) fn record_trade( + trade_id: TradeId, + character_to: CharacterEntityId, + character_from: CharacterEntityId, +) -> impl Fn((ItemStateProxy, TR), Vec) + -> BoxFuture), ItemStateError>> + Clone +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(item_state, mut transaction), traded_items| { Box::pin(async move { @@ -688,9 +853,12 @@ pub(super) fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, c } -pub(super) fn assign_new_item_id() - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> + Clone +pub(super) fn assign_new_item_id( +) -> impl Fn((ItemStateProxy, TR), InventoryItem) + -> BoxFuture> + Clone +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, transaction), mut inventory_item| { Box::pin(async move { @@ -701,9 +869,14 @@ pub(super) fn assign_new_item_id() } -pub(super) fn convert_item_drop_to_floor_item(character_id: CharacterEntityId, item_drop: ItemDrop) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> + Clone +pub(super) fn convert_item_drop_to_floor_item( + character_id: CharacterEntityId, + item_drop: ItemDrop, +) -> impl Fn((ItemStateProxy, TR), ()) + -> BoxFuture> + Clone +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, mut transaction), _| { let item_drop = item_drop.clone(); @@ -798,9 +971,13 @@ pub(super) fn convert_item_drop_to_floor_item(character_id: CharacterEntityId, i } } -pub(super) fn add_item_to_local_floor(character_id: CharacterEntityId) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), FloorItem) - -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> +pub(super) fn add_item_to_local_floor( + character_id: CharacterEntityId, +) -> impl Fn((ItemStateProxy, TR), FloorItem) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(mut item_state, transaction) , floor_item| { Box::pin(async move { @@ -813,9 +990,13 @@ pub(super) fn add_item_to_local_floor(character_id: CharacterEntityId) } } -pub(super) fn apply_modifier_to_inventory_item(modifier: ItemModifier) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> +pub(super) fn apply_modifier_to_inventory_item( + modifier: ItemModifier, +) -> impl Fn((ItemStateProxy, TR), InventoryItem) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(item_state, mut transaction), mut inventory_item| { let modifier = modifier.clone(); @@ -833,9 +1014,12 @@ pub(super) fn apply_modifier_to_inventory_item(modifier: ItemModifier) } } -pub(super) fn as_individual_item() - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin, Box), IndividualItemDetail), ItemStateError>> + Send + 'a>> +pub(super) fn as_individual_item( +) -> impl Fn((ItemStateProxy, TR), InventoryItem) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, { move |(item_state, transaction), inventory_item| { Box::pin(async move { @@ -848,3 +1032,85 @@ pub(super) fn as_individual_item() }) } } + + +pub(super) fn apply_item_action_packets( + character_id: CharacterEntityId, + area_client: AreaClient, +) -> impl Fn((ItemStateProxy, TR), ApplyItemAction) + -> BoxFuture), ItemStateError>> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, +{ + move |(mut item_state, mut transaction), apply_item_action| { + Box::pin(async move { + let pkts = if let ApplyItemAction::CreateItem(item_detail) = apply_item_action { + let new_item = transaction.gateway().create_item(NewItemEntity { + item: item_detail.clone(), + }).await?; + + let item_id = item_state.new_item_id().await?; + + let (inventory_item_detail, create_item) = if item_detail.is_stackable() { + let tool = item_detail.as_tool().ok_or_else(|| ItemStateError::NotATool(ClientItemId(0xFFFFFFFF)))?; + + let create_item = builder::message::create_stacked_item(area_client, item_id, &tool, 1).map_err(|_err| ItemStateError::Dummy)?; + let item_detail = StackedItemDetail { + entity_ids: vec![new_item.id], + tool + }; + (InventoryItemDetail::Stacked(item_detail), create_item) + } + else { + let item_detail = IndividualItemDetail { + entity_id: new_item.id, + item: item_detail, + }; + let create_item = builder::message::create_individual_item(area_client, item_id, &item_detail).map_err(|_err| ItemStateError::Dummy)?; + (InventoryItemDetail::Individual(item_detail), create_item) + }; + + let inventory_item = InventoryItem { + item_id, + item: inventory_item_detail, + }; + + let mut inventory = item_state.inventory(&character_id).await?; + inventory.add_item(inventory_item)?; + transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; + item_state.set_inventory(inventory).await; + + vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item)))] + } + else { + Vec::new() + }; + + Ok(((item_state, transaction), pkts)) + }) + } +} + +pub(super) fn apply_item_action_character( + character: &CharacterEntity +) -> impl Fn((ItemStateProxy, TR), Vec) + -> BoxFuture> +where + EG: EntityGateway, + TR: EntityGatewayTransaction + 'static, +{ + let character = character.clone(); + move |(item_state, transaction), apply_item_actions| { + let mut character = character.clone(); + Box::pin(async move { + for action in apply_item_actions { + if let ApplyItemAction::UpdateCharacter(new_character) = action { + character = *new_character + } + } + + Ok(((item_state, transaction), character)) + }) + } +} diff --git a/src/ship/items/apply_item.rs b/src/ship/items/apply_item.rs index 598678f..b78a94b 100644 --- a/src/ship/items/apply_item.rs +++ b/src/ship/items/apply_item.rs @@ -1,9 +1,12 @@ -use thiserror::Error; use std::convert::TryInto; +use futures::future::join_all; +use thiserror::Error; +use rand::SeedableRng; +use rand::distributions::{WeightedIndex, Distribution}; use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::character::CharacterEntity; use crate::entity::item::mag::{MagCell, MagCellError}; -use crate::entity::item::tool::ToolType; +use crate::entity::item::tool::{Tool, ToolType}; use crate::entity::item::{ItemDetail, ItemEntityId}; use crate::ship::items::state::{ItemStateProxy, ItemStateError}; use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; @@ -27,53 +30,60 @@ pub enum ApplyItemError { MagCellError(#[from] MagCellError), } +#[derive(Debug, Clone)] +pub enum ApplyItemAction { + UpdateCharacter(Box), + CreateItem(ItemDetail), + //TransformItem, + //RemoveItem, +} + impl From for ApplyItemError { fn from(other: ItemStateError) -> ApplyItemError { ApplyItemError::ItemStateError(Box::new(other)) } } -// TODO: make all these functions not-pub -pub async fn power_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { +async fn power_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result, ApplyItemError> { character.materials.power += 1; entity_gateway.save_character(character).await?; - Ok(()) + Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) } -pub async fn mind_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { +async fn mind_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result, ApplyItemError> { character.materials.mind += 1; entity_gateway.save_character(character).await.unwrap(); - Ok(()) + Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) } -pub async fn evade_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { +async fn evade_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result, ApplyItemError> { character.materials.evade += 1; entity_gateway.save_character(character).await.unwrap(); - Ok(()) + Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) } -pub async fn def_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { +async fn def_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result, ApplyItemError> { character.materials.def += 1; entity_gateway.save_character(character).await.unwrap(); - Ok(()) + Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) } -pub async fn luck_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { +async fn luck_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result, ApplyItemError> { character.materials.luck += 1; entity_gateway.save_character(character).await.unwrap(); - Ok(()) + Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) } -pub async fn hp_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { +async fn hp_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result, ApplyItemError> { character.materials.hp += 1; entity_gateway.save_character(character).await.unwrap(); - Ok(()) + Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) } -pub async fn tp_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { +async fn tp_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result, ApplyItemError> { character.materials.tp += 1; entity_gateway.save_character(character).await.unwrap(); - Ok(()) + Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) } /* @@ -98,12 +108,12 @@ async fn mag_cell(entity_gateway: &mut EG, used_cell: &Consum */ -async fn mag_cell<'a, EG>(item_state: &mut ItemStateProxy<'a>, +async fn mag_cell<'a, EG>(item_state: &mut ItemStateProxy, entity_gateway: &mut EG, character: &CharacterEntity, cell_entity_id: ItemEntityId, mag_cell_type: MagCell) - -> Result<(), ApplyItemError> + -> Result, ApplyItemError> where EG: EntityGateway + ?Sized, { @@ -115,9 +125,9 @@ where entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await?; entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; - item_state.set_inventory(inventory); + item_state.set_inventory(inventory).await; - Ok(()) + Ok(Vec::new()) } /* @@ -218,12 +228,31 @@ pub async fn liberta_kit(entity_gateway: &mut EG, used_cell: } */ -async fn apply_tool<'a, EG>(item_state: &mut ItemStateProxy<'a>, + +fn jack_o_lantern() -> Result, ApplyItemError> +{ + let mag_rate = WeightedIndex::new(&[13, 13, 13, 13, 12, 12, 12, 12]).unwrap(); + let mag_type = match mag_rate.sample(&mut rand_chacha::ChaChaRng::from_entropy()) { + 0 => ToolType::CellOfMag502, + 1 => ToolType::CellOfMag213, + 2 => ToolType::HeartOfChuChu, + 3 => ToolType::HeartOfKapuKapu, + 4 => ToolType::PartsOfRobochao, + 5 => ToolType::HeartOfOpaOpa, + 6 => ToolType::HeartOfPian, + 7 => ToolType::HeartOfChao, + _ => unreachable!(), + }; + + Ok(vec![ApplyItemAction::CreateItem(ItemDetail::Tool(Tool {tool: mag_type}))]) +} + +async fn apply_tool<'a, EG>(item_state: &mut ItemStateProxy, entity_gateway: &mut EG, character: &mut CharacterEntity, entity_id: ItemEntityId, tool: ToolType) - -> Result<(), ApplyItemError> + -> Result, ApplyItemError> where EG: EntityGateway + ?Sized, { @@ -235,13 +264,13 @@ where ToolType::LuckMaterial => luck_material(entity_gateway, character).await, ToolType::HpMaterial => hp_material(entity_gateway, character).await, ToolType::TpMaterial => tp_material(entity_gateway, character).await, - ToolType::Monomate => Ok(()), - ToolType::Dimate => Ok(()), - ToolType::Trimate => Ok(()), - ToolType::Monofluid => Ok(()), - ToolType::Difluid => Ok(()), - ToolType::Trifluid => Ok(()), - ToolType::HuntersReport => Ok(()), + ToolType::Monomate => Ok(Vec::new()), + ToolType::Dimate => Ok(Vec::new()), + ToolType::Trimate => Ok(Vec::new()), + ToolType::Monofluid => Ok(Vec::new()), + ToolType::Difluid => Ok(Vec::new()), + ToolType::Trifluid => Ok(Vec::new()), + ToolType::HuntersReport => Ok(Vec::new()), ToolType::CellOfMag502 | ToolType::CellOfMag213 | ToolType::PartsOfRobochao @@ -268,6 +297,7 @@ where | ToolType::LibertaKit => { mag_cell(item_state, entity_gateway, character, entity_id, tool.try_into()?).await } + ToolType::JackOLantern => jack_o_lantern(), // TODO: rest of these _ => Err(ApplyItemError::InvalidItem) } @@ -275,7 +305,14 @@ where } -pub async fn apply_item<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStateProxy<'a>, entity_gateway: &mut EG, character: &mut CharacterEntity, item: InventoryItem) -> Result<(), ApplyItemError> { +pub async fn apply_item<'a, EG>(item_state: &mut ItemStateProxy, + entity_gateway: &mut EG, + character: &mut CharacterEntity, + item: InventoryItem +) -> Result, ApplyItemError> +where + EG: EntityGateway + ?Sized + Clone + 'static +{ match item.item { InventoryItemDetail::Individual(individual_item) => { match individual_item.item { @@ -284,10 +321,22 @@ pub async fn apply_item<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemSta } }, InventoryItemDetail::Stacked(stacked_item) => { - for entity_id in stacked_item.entity_ids { - apply_tool(item_state, entity_gateway, character, entity_id, stacked_item.tool.tool).await? - } - Ok(()) + Ok(join_all(stacked_item.entity_ids.iter() + .map(|entity_id| { + let mut entity_gateway = entity_gateway.clone(); + let mut character = character.clone(); + let mut item_state = item_state.clone(); + async move { + apply_tool(&mut item_state, &mut entity_gateway, &mut character, *entity_id, stacked_item.tool.tool).await + } + }) + .collect::>()) + .await + .into_iter() + .collect::>, _>>()? + .into_iter() + .flatten() + .collect()) }, } } diff --git a/src/ship/items/inventory.rs b/src/ship/items/inventory.rs index 4024fc8..e10a808 100644 --- a/src/ship/items/inventory.rs +++ b/src/ship/items/inventory.rs @@ -179,7 +179,7 @@ pub enum InventoryError { MesetaFull, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct InventoryState { pub character_id: CharacterEntityId, pub item_id_counter: u32, diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 8c95391..6473f38 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use async_std::sync::{Arc, RwLock}; +use async_std::sync::{Arc, RwLock, Mutex}; use futures::future::join_all; use crate::entity::gateway::{EntityGateway, GatewayError}; @@ -124,7 +124,7 @@ pub enum AddItemResult { } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ItemState { character_inventory: Arc>>>, character_bank: Arc>>>, @@ -373,36 +373,38 @@ impl ItemState { } -#[derive(Default)] +#[derive(Default, Clone)] struct ProxiedItemState { - character_inventory: HashMap, - character_bank: HashMap, + character_inventory: Arc>>, + character_bank: Arc>>, //character_room: HashMap, - character_floor: HashMap, - room_floor: HashMap, + character_floor: Arc>>, + room_floor: Arc>>, } -pub struct ItemStateProxy<'a> { - item_state: &'a mut ItemState, +#[derive(Clone)] +pub struct ItemStateProxy { + item_state: ItemState, proxied_state: ProxiedItemState, } -impl<'a> ItemStateProxy<'a> { +impl ItemStateProxy { pub async fn commit(self) { async fn copy_back(master: &Arc>>>, - proxy: HashMap) + proxy: Arc>>) where K: Eq + std::hash::Hash, + V: Clone, { - for (key, value) in proxy { + for (key, value) in proxy.lock().await.iter() { if let Some(element) = master .read() .await - .get(&key) { + .get(key) { *element .write() - .await = value; + .await = value.clone(); } } } @@ -417,7 +419,7 @@ impl<'a> ItemStateProxy<'a> { async fn get_or_clone(master: &Arc>>>, - proxy: &mut HashMap, + proxy: &Arc>>, key: K, err: fn(K) -> ItemStateError) -> Result where @@ -432,14 +434,17 @@ where .read() .await .clone(); - Ok(proxy.entry(key) + Ok(proxy + .lock() + .await + .entry(key) .or_insert_with(|| existing_element) .clone()) } -impl<'a> ItemStateProxy<'a> { - pub fn new(item_state: &'a mut ItemState) -> Self { +impl ItemStateProxy { + pub fn new(item_state: ItemState) -> Self { ItemStateProxy { item_state, proxied_state: Default::default(), @@ -448,39 +453,39 @@ impl<'a> ItemStateProxy<'a> { pub async fn inventory(&mut self, character_id: &CharacterEntityId) -> Result { get_or_clone(&self.item_state.character_inventory, - &mut self.proxied_state.character_inventory, + &self.proxied_state.character_inventory, *character_id, ItemStateError::NoCharacter).await } - pub fn set_inventory(&mut self, inventory: InventoryState) { - self.proxied_state.character_inventory.insert(inventory.character_id, inventory); + pub async fn set_inventory(&mut self, inventory: InventoryState) { + self.proxied_state.character_inventory.lock().await.insert(inventory.character_id, inventory); } pub async fn bank(&mut self, character_id: &CharacterEntityId) -> Result { get_or_clone(&self.item_state.character_bank, - &mut self.proxied_state.character_bank, + &self.proxied_state.character_bank, *character_id, ItemStateError::NoCharacter).await } - pub fn set_bank(&mut self, bank: BankState) { - self.proxied_state.character_bank.insert(bank.character_id, bank); + pub async fn set_bank(&mut self, bank: BankState) { + self.proxied_state.character_bank.lock().await.insert(bank.character_id, bank); } pub async fn floor(&mut self, character_id: &CharacterEntityId) -> Result { let room_id = *self.item_state.character_room.read().await.get(character_id).unwrap(); Ok(FloorState { character_id: *character_id, - local: get_or_clone(&self.item_state.character_floor, &mut self.proxied_state.character_floor, *character_id, ItemStateError::NoCharacter).await?, - shared: get_or_clone(&self.item_state.room_floor, &mut self.proxied_state.room_floor, room_id, ItemStateError::NoRoom).await?, + local: get_or_clone(&self.item_state.character_floor, &self.proxied_state.character_floor, *character_id, ItemStateError::NoCharacter).await?, + shared: get_or_clone(&self.item_state.room_floor, &self.proxied_state.room_floor, room_id, ItemStateError::NoRoom).await?, }) } pub async fn set_floor(&mut self, floor: FloorState) { let room_id = *self.item_state.character_room.read().await.get(&floor.character_id).unwrap(); - self.proxied_state.character_floor.insert(floor.character_id, floor.local); - self.proxied_state.room_floor.insert(room_id, floor.shared); + self.proxied_state.character_floor.lock().await.insert(floor.character_id, floor.local); + self.proxied_state.room_floor.lock().await.insert(room_id, floor.shared); } pub async fn new_item_id(&mut self) -> Result { diff --git a/src/ship/items/tasks.rs b/src/ship/items/tasks.rs index 844d116..a1ab65c 100644 --- a/src/ship/items/tasks.rs +++ b/src/ship/items/tasks.rs @@ -1,9 +1,10 @@ use crate::ship::items::ClientItemId; use crate::entity::item::Meseta; +use crate::ship::ship::SendShipPacket; use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; -use crate::entity::gateway::EntityGateway; +use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, IndividualItemDetail}; use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction}; use crate::ship::items::inventory::InventoryItem; @@ -23,10 +24,11 @@ pub async fn pick_up_item( item_id: &ClientItemId) -> Result where - EG: EntityGateway, + EG: EntityGateway + 'static, + EG::Transaction: Clone, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::take_item_from_floor(character.id, *item_id)) .act(actions::add_floor_item_to_inventory(character)) @@ -46,10 +48,10 @@ pub async fn drop_item( drop_position: (f32, f32, f32)) -> Result where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::take_item_from_inventory(character.id, *item_id, 0)) .act(actions::add_inventory_item_to_shared_floor(character.id, map_area, drop_position)) @@ -70,10 +72,10 @@ pub async fn drop_partial_item<'a, EG>( amount: u32) -> Result where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::take_item_from_inventory(character.id, *item_id, amount)) .act(actions::add_inventory_item_to_shared_floor(character.id, map_area, (drop_position.0, 0.0, drop_position.1))) @@ -95,10 +97,10 @@ pub async fn drop_meseta<'a, EG>( amount: u32) -> Result where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::take_meseta_from_inventory(character.id, amount)) .act(actions::add_meseta_to_shared_floor(character.id, amount, map_area, drop_position)) @@ -117,10 +119,10 @@ pub async fn withdraw_meseta<'a, EG>( amount: u32) -> Result<(), ItemStateError> where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::take_meseta_from_bank(character.id, amount)) .act(actions::add_meseta_from_bank_to_inventory(character.id, amount)) @@ -139,10 +141,10 @@ pub async fn deposit_meseta<'a, EG>( amount: u32) -> Result<(), ItemStateError> where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), _) = ItemStateAction::default() .act(actions::take_meseta_from_inventory(character.id, amount)) .act(actions::add_meseta_to_bank(character.id, amount)) @@ -162,10 +164,10 @@ pub async fn withdraw_item<'a, EG>( amount: u32) -> Result where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::take_item_from_bank(character.id, *item_id, amount)) //.act(bank_item_to_inventory_item) @@ -187,10 +189,10 @@ pub async fn deposit_item<'a, EG> ( amount: u32) -> Result<(), ItemStateError> where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::take_item_from_inventory(character.id, *item_id, amount)) .act(actions::add_inventory_item_to_bank(character.id)) @@ -209,10 +211,10 @@ pub async fn equip_item<'a, EG> ( equip_slot: u8, ) -> Result<(), ItemStateError> where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::equip_inventory_item(character.id, *item_id, equip_slot)) .commit((item_state_proxy, transaction)) @@ -230,10 +232,10 @@ pub async fn unequip_item<'a, EG> ( item_id: &ClientItemId, ) -> Result<(), ItemStateError> where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::unequip_inventory_item(character.id, *item_id)) .commit((item_state_proxy, transaction)) @@ -251,10 +253,10 @@ pub async fn sort_inventory<'a, EG> ( item_ids: Vec, ) -> Result<(), ItemStateError> where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::sort_inventory_items(character.id, item_ids)) .commit((item_state_proxy, transaction)) @@ -269,22 +271,28 @@ pub async fn use_item<'a, EG> ( item_state: &'a mut ItemState, entity_gateway: &mut EG, character: &mut CharacterEntity, + area_client: AreaClient, item_id: &ClientItemId, amount: u32, -) -> Result<(), ItemStateError> +) -> Result, ItemStateError> where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), new_character) = ItemStateAction::default() + let item_state_proxy = ItemStateProxy::new(item_state.clone()); + let ((item_state_proxy, transaction), (pkts, new_character)) = ItemStateAction::default() .act(actions::take_item_from_inventory(character.id, *item_id, amount)) - .act(actions::use_consumed_item(character.clone())) + .act(actions::use_consumed_item(character)) + .act(actions::fork( + actions::foreach(actions::apply_item_action_packets(character.id, area_client)), + actions::apply_item_action_character(character) + )) .commit((item_state_proxy, transaction)) .await?; item_state_proxy.commit().await; + *character = new_character; - Ok((transaction, ())) + Ok((transaction, pkts.into_iter().flatten().collect())) }).await } @@ -297,10 +305,10 @@ pub async fn feed_mag<'a, EG> ( tool_item_id: &ClientItemId, ) -> Result<(), ItemStateError> where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), _) = ItemStateAction::default() .act(actions::take_item_from_inventory(character.id, *tool_item_id, 1)) .act(actions::feed_mag_item(character.clone(), *mag_item_id)) @@ -321,11 +329,11 @@ pub async fn buy_shop_item<'a, EG> ( amount: u32, ) -> Result where - EG: EntityGateway, + EG: EntityGateway + 'static, { let item_price = shop_item.price() as u32 * amount; entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::take_meseta_from_inventory(character.id, item_price)) //.act(bought_item_to_inventory_item) @@ -347,10 +355,10 @@ pub async fn sell_item<'a, EG> ( amount: u32, ) -> Result where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(actions::take_item_from_inventory(character.id, item_id, amount)) .act(actions::sell_inventory_item(character.id)) @@ -367,7 +375,7 @@ pub async fn trade_items<'a, EG> ( p2: (&AreaClient, &CharacterEntity, &Vec, Meseta)) -> Result<(Vec, Vec), ItemStateError> where - EG: EntityGateway, + EG: EntityGateway + 'static, { let p1_trade_items = p1.2 .iter() @@ -391,7 +399,7 @@ where let p1_id = p1.1.id; let p2_id = p2.1.id; let trade = transaction.gateway().create_trade(&p1_id, &p2_id).await?; - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), p1_removed_items) = ItemStateAction::default() .act(actions::iterate(p1_trade_items, move |p1_trade_item| actions::take_item_from_inventory(p1_id, p1_trade_item.0, p1_trade_item.1) )) .act(actions::foreach(actions::assign_new_item_id())) @@ -437,10 +445,10 @@ pub async fn take_meseta<'a, EG> ( meseta: Meseta) -> Result<(), ItemStateError> where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), _) = ItemStateAction::default() .act(actions::take_meseta_from_inventory(*character_id, meseta.0)) .commit((item_state_proxy, transaction)) @@ -458,10 +466,10 @@ pub async fn enemy_drops_item<'a, EG> ( item_drop: ItemDrop) -> Result where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), floor_item) = ItemStateAction::default() .act(actions::convert_item_drop_to_floor_item(character_id, item_drop)) .act(actions::add_item_to_local_floor(character_id)) @@ -482,10 +490,10 @@ pub async fn apply_modifier<'a, EG> ( modifier: ItemModifier) -> Result where - EG: EntityGateway, + EG: EntityGateway + 'static, { entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); + let item_state_proxy = ItemStateProxy::new(item_state.clone()); let ((item_state_proxy, transaction), item) = ItemStateAction::default() .act(actions::take_item_from_inventory(character.id, item_id, 1)) .act(actions::apply_modifier_to_inventory_item(modifier)) diff --git a/src/ship/location.rs b/src/ship/location.rs index f58a6f9..b3f16cc 100644 --- a/src/ship/location.rs +++ b/src/ship/location.rs @@ -69,9 +69,10 @@ pub enum ClientRemovalError { } #[derive(Error, Debug, PartialEq, Eq)] -#[error("get clients")] pub enum GetClientsError { + #[error("invalid client")] InvalidClient, + #[error("invalid area")] InvalidArea, } @@ -147,7 +148,7 @@ pub enum RoomLobby { #[derive(Clone, Debug)] pub struct ClientLocation { lobbies: [Arc>; 15], - rooms: [Option>>; MAX_ROOMS], + rooms: [Arc>>; MAX_ROOMS], client_location: Arc>>, } @@ -155,7 +156,7 @@ impl Default for ClientLocation { fn default() -> ClientLocation { ClientLocation { lobbies: core::array::from_fn(|_| Arc::new(RwLock::new(Lobby([None; 12])))), - rooms: core::array::from_fn(|_| None), + rooms: core::array::from_fn(|_| Arc::new(RwLock::new(None))), client_location: Arc::new(RwLock::new(HashMap::new())), } } @@ -210,15 +211,16 @@ impl ClientLocation { } pub async fn create_new_room(&mut self, id: ClientId) -> Result { - let (index, empty_slot) = self.rooms.iter_mut() + let (index, empty_slot) = Box::pin(stream::iter(self.rooms.iter()) .enumerate() - .find(|(_, r)| r.is_none()) + .filter(|(_, r)| async {r.read().await.is_none()})) + .next() + .await .ok_or(CreateRoomError::NoOpenSlots)?; - *empty_slot = Some(Arc::new(RwLock::new(Room([None; 4])))); + *empty_slot.write().await = Some(Room([None; 4])); self.add_client_to_room(id, RoomId(index)) .await .map_err(|_err| CreateRoomError::JoinError)?; - Ok(RoomId(index)) } @@ -226,9 +228,10 @@ impl ClientLocation { let mut r = self.rooms.get(room.0) .ok_or(JoinRoomError::RoomDoesNotExist)? .as_ref() - .ok_or(JoinRoomError::RoomDoesNotExist)? .write() .await; + let r = r.as_mut() + .ok_or(JoinRoomError::RoomDoesNotExist)?; let (index, empty_slot) = r.0.iter_mut() .enumerate() .find(|(_, k)| k.is_none()) @@ -293,9 +296,9 @@ impl ClientLocation { pub async fn get_room_leader(&self, room: RoomId) -> Result { let r = self.rooms[room.0] .as_ref() - .ok_or(GetLeaderError::InvalidArea)? .read() - .await; + .await + .ok_or(GetLeaderError::InvalidArea)?; let mut r = r .0 .iter() @@ -368,9 +371,9 @@ impl ClientLocation { Ok(self.rooms.get(room.0) .ok_or(GetClientsError::InvalidArea)? .as_ref() - .ok_or(GetClientsError::InvalidArea)? .read() .await + .ok_or(GetClientsError::InvalidArea)? .0 .iter() .filter_map(|client| { @@ -456,9 +459,13 @@ impl ClientLocation { .ok_or(ClientRemovalError::ClientNotInArea)?; match area { RoomLobby::Room(room) => { - let r = self.rooms.get(room.0).ok_or(ClientRemovalError::InvalidArea)?; - if let Some(r) = r { - remove_client(id, &mut r.write().await.0) + let mut r = self.rooms.get(room.0) + .ok_or(ClientRemovalError::InvalidArea)? + .as_ref() + .write() + .await; + if let Some(r) = r.as_mut() { + remove_client(id, &mut r.0) } else { return Err(ClientRemovalError::InvalidArea) diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index f565d41..5c49a53 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -84,7 +84,7 @@ pub async fn request_item(id: ClientId, item_state: &mut ItemState) -> Result, ShipError> where - EG: EntityGateway + EG: EntityGateway + 'static, { let room_id = client_location.get_room(id).await?; let monster = rooms.with(room_id, |room| Box::pin(async move { diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index df8b7d0..8256a91 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -330,20 +330,32 @@ where pub async fn player_uses_item(id: ClientId, player_use_tool: PlayerUseItem, entity_gateway: &mut EG, - _client_location: &ClientLocation, + client_location: &ClientLocation, clients: &Clients, item_state: &mut ItemState) -> Result, ShipError> where EG: EntityGateway + Clone + 'static, { - clients.with_mut(id, |client| { + let neighbors = client_location.get_all_clients_by_client(id).await?.into_iter(); + let area_client = client_location.get_local_client(id).await?; + + Ok(clients.with_mut(id, |client| { let mut entity_gateway = entity_gateway.clone(); let mut item_state = item_state.clone(); Box::pin(async move { - use_item(&mut item_state, &mut entity_gateway, &mut client.character, &ClientItemId(player_use_tool.item_id), 1).await - })}).await??; - Ok(Vec::new()) + use_item(&mut item_state, &mut entity_gateway, &mut client.character, area_client, &ClientItemId(player_use_tool.item_id), 1).await + })}).await?? + .into_iter() + .flat_map(move |pkt| { + let player_use_tool = player_use_tool.clone(); + neighbors.clone().map(move |client| { + vec![(client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerUseItem(player_use_tool.clone())))), (client.client, pkt.clone())] + }) + }) + .flatten() + .collect::>() + ) } pub async fn player_used_medical_center(id: ClientId, diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 71469fe..a71cc14 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -104,7 +104,7 @@ pub enum ShipError { InvalidSlot(ClientId, u32), #[error("too many clients")] TooManyClients, - #[error("client error location {0}")] + #[error("client location {0}")] ClientLocationError(ClientLocationError), #[error("maps error {0}")] MapsError(#[from] MapsError), diff --git a/tests/test_item_use.rs b/tests/test_item_use.rs index 574f948..2289822 100644 --- a/tests/test_item_use.rs +++ b/tests/test_item_use.rs @@ -251,6 +251,59 @@ async fn test_use_materials() { assert!(char.materials.luck == 2); } +#[async_std::test] +async fn test_jackolantern() { + let mut entity_gateway = InMemoryGateway::default(); + let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; + + let p1_inv = vec![ + item::InventoryItemEntity::Stacked( + vec![ + entity_gateway.create_item( + item::NewItemEntity { + item: item::ItemDetail::Tool( + item::tool::Tool { + tool: item::tool::ToolType::JackOLantern, + } + ), + }).await.unwrap(), + entity_gateway.create_item(item::NewItemEntity { + item: item::ItemDetail::Tool( + item::tool::Tool { + tool: item::tool::ToolType::JackOLantern, + } + ), + }).await.unwrap(), + ])]; + entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); + + let mut ship = Box::new(ShipServerState::builder() + .gateway(entity_gateway.clone()) + .build()); + log_in_char(&mut ship, ClientId(1), "a1", "a").await; + join_lobby(&mut ship, ClientId(1)).await; + create_room(&mut ship, ClientId(1), "room", "").await; + + ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem { + client: 0, + target: 0, + item_id: 0x10000, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem { + client: 0, + target: 0, + item_id: 0x10000, + })))).await.unwrap(); + + let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); + for item in inventory_items.items { + for sitem in item.stacked().unwrap() { + assert!(sitem.item.clone().as_tool().unwrap().tool.is_mag_cell()); + } + } +} + // TODO: tests for ALL ITEMS WOW /* diff --git a/tests/test_trade.rs b/tests/test_trade.rs index 9175716..61bff99 100644 --- a/tests/test_trade.rs +++ b/tests/test_trade.rs @@ -1792,7 +1792,7 @@ async fn test_trade_multiple_individual() { msg: GameMessage::CreateItem(CreateItem { client: 1, item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber - item_id: 0x810003, + item_id: 0x810004, .. }), .. @@ -1801,7 +1801,7 @@ async fn test_trade_multiple_individual() { msg: GameMessage::CreateItem(CreateItem { client: 1, item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber - item_id: 0x810003, + item_id: 0x810004, .. }), .. @@ -1810,7 +1810,7 @@ async fn test_trade_multiple_individual() { msg: GameMessage::CreateItem(CreateItem { client: 1, item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber - item_id: 0x810004, + item_id: 0x810003, .. }), .. @@ -1819,7 +1819,7 @@ async fn test_trade_multiple_individual() { msg: GameMessage::CreateItem(CreateItem { client: 1, item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber - item_id: 0x810004, + item_id: 0x810003, .. }), .. @@ -1828,7 +1828,7 @@ async fn test_trade_multiple_individual() { msg: GameMessage::CreateItem(CreateItem { client: 0, item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun - item_id: 0x810001, + item_id: 0x810002, .. }), .. @@ -1837,7 +1837,7 @@ async fn test_trade_multiple_individual() { msg: GameMessage::CreateItem(CreateItem { client: 0, item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun - item_id: 0x810001, + item_id: 0x810002, .. }), .. @@ -1846,7 +1846,7 @@ async fn test_trade_multiple_individual() { msg: GameMessage::CreateItem(CreateItem { client: 0, item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun - item_id: 0x810002, + item_id: 0x810001, .. }), .. @@ -1855,7 +1855,7 @@ async fn test_trade_multiple_individual() { msg: GameMessage::CreateItem(CreateItem { client: 0, item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun - item_id: 0x810002, + item_id: 0x810001, .. }), .. @@ -2063,7 +2063,7 @@ async fn test_trade_multiple_stacked() { assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x810003, + item_id: 0x810004, .. }), .. @@ -2071,7 +2071,7 @@ async fn test_trade_multiple_stacked() { assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x810003, + item_id: 0x810004, .. }), .. @@ -2079,7 +2079,7 @@ async fn test_trade_multiple_stacked() { assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x810004, + item_id: 0x810003, .. }), .. @@ -2087,7 +2087,7 @@ async fn test_trade_multiple_stacked() { assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x810004, + item_id: 0x810003, .. }), .. @@ -2095,7 +2095,7 @@ async fn test_trade_multiple_stacked() { assert!(matches!(ack[8], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x810001, + item_id: 0x810002, .. }), .. @@ -2103,7 +2103,7 @@ async fn test_trade_multiple_stacked() { assert!(matches!(ack[9], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x810001, + item_id: 0x810002, .. }), .. @@ -2111,7 +2111,7 @@ async fn test_trade_multiple_stacked() { assert!(matches!(ack[10], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x810002, + item_id: 0x810001, .. }), .. @@ -2119,7 +2119,7 @@ async fn test_trade_multiple_stacked() { assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x810002, + item_id: 0x810001, .. }), ..