use_item_jackolantern #124
| @ -191,7 +191,6 @@ where | |||||||
|     C: PSOCipher, |     C: PSOCipher, | ||||||
| { | { | ||||||
|     let buf = pkt.as_bytes(); |     let buf = pkt.as_bytes(); | ||||||
|     trace!("[send buf] {:?}", buf); |  | ||||||
|     let cbuf = cipher.encrypt(&buf)?; |     let cbuf = cipher.encrypt(&buf)?; | ||||||
|     socket.write_all(&cbuf).await?; |     socket.write_all(&cbuf).await?; | ||||||
|     Ok(()) |     Ok(()) | ||||||
|  | |||||||
| @ -19,15 +19,12 @@ pub enum GatewayError { | |||||||
| 
 | 
 | ||||||
| #[async_trait::async_trait] | #[async_trait::async_trait] | ||||||
| pub trait EntityGateway: Send + Sync { | pub trait EntityGateway: Send + Sync { | ||||||
|     async fn transaction<'a>(&'a mut self) -> Result<Box<dyn EntityGatewayTransaction + 'a>, GatewayError> |     type Transaction: EntityGatewayTransaction + Clone; | ||||||
|     { |  | ||||||
|         unimplemented!(); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, _func: F) -> Result<R, E> |     async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, _func: F) -> Result<R, E> | ||||||
|     where |     where | ||||||
|         Fut: Future<Output = Result<(Box<dyn EntityGatewayTransaction + 'a>, R), E>> + Send + 'a, |         Fut: Future<Output = Result<(Self::Transaction, R), E>> + Send + 'a, | ||||||
|         F: FnOnce(Box<dyn EntityGatewayTransaction + 'a>) -> Fut + Send, |         F: FnOnce(Self::Transaction) -> Fut + Send, | ||||||
|         R: Send, |         R: Send, | ||||||
|         E: From<GatewayError>, |         E: From<GatewayError>, | ||||||
|         Self: Sized |         Self: Sized | ||||||
| @ -155,12 +152,14 @@ pub trait EntityGateway: Send + Sync { | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #[async_trait::async_trait] | #[async_trait::async_trait] | ||||||
| pub trait EntityGatewayTransaction: Send + Sync { | pub trait EntityGatewayTransaction: Send + Sync + Sized { | ||||||
|     fn gateway(&mut self) -> &mut dyn EntityGateway { |     type ParentGateway: EntityGateway + Clone; | ||||||
|  | 
 | ||||||
|  |     fn gateway(&mut self) -> &mut Self::ParentGateway { | ||||||
|         unimplemented!() |         unimplemented!() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn commit(self: Box<Self>) -> Result<(), GatewayError> { |     async fn commit(self) -> Result<(), GatewayError> { | ||||||
|         unimplemented!() |         unimplemented!() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -9,10 +9,10 @@ use crate::entity::item::*; | |||||||
| 
 | 
 | ||||||
| use async_std::sync::{Arc, Mutex}; | use async_std::sync::{Arc, Mutex}; | ||||||
| 
 | 
 | ||||||
| // TODO: implement multiple banks
 | #[derive(Clone)] | ||||||
| pub struct InMemoryGatewayTransaction<'a> { | pub struct InMemoryGatewayTransaction { | ||||||
|     working_gateway: InMemoryGateway, |     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
 | // functions here have been skipped as they are not used in transactions, add as needed
 | ||||||
| #[async_trait::async_trait] | #[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<UserAccountEntity, GatewayError> { |     async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> { | ||||||
|         self.working_gateway.create_user(user).await |         self.working_gateway.create_user(user).await | ||||||
|     } |     } | ||||||
| @ -182,12 +184,14 @@ impl<'a> EntityGateway for InMemoryGatewayTransaction<'a> { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait::async_trait] | #[async_trait::async_trait] | ||||||
| impl<'a> EntityGatewayTransaction for InMemoryGatewayTransaction<'a> { | impl EntityGatewayTransaction for InMemoryGatewayTransaction { | ||||||
|     fn gateway(&mut self) -> &mut dyn EntityGateway { |     type ParentGateway = InMemoryGatewayTransaction; | ||||||
|  | 
 | ||||||
|  |     fn gateway(&mut self) -> &mut Self::ParentGateway { | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn commit(mut self: Box<Self>) -> 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.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.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()); |         self.original_gateway.characters.lock().await.extend(self.working_gateway.characters.lock().await.clone()); | ||||||
| @ -298,49 +302,12 @@ fn apply_modifiers(items: &BTreeMap<ItemEntityId, ItemEntity>, | |||||||
| 
 | 
 | ||||||
| #[async_trait::async_trait] | #[async_trait::async_trait] | ||||||
| impl EntityGateway for InMemoryGateway { | impl EntityGateway for InMemoryGateway { | ||||||
|     async fn transaction<'a>(&'a mut self) -> Result<Box<dyn EntityGatewayTransaction + 'a>, GatewayError> |     type Transaction = InMemoryGatewayTransaction; | ||||||
|     { |  | ||||||
|         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, |  | ||||||
|         })) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|     async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, func: F) -> Result<R, E> |     async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, func: F) -> Result<R, E> | ||||||
|     where |     where | ||||||
|         Fut: Future<Output = Result<(Box<dyn EntityGatewayTransaction + 'a>, R), E>> + Send + 'a, |         Fut: Future<Output = Result<(Self::Transaction, R), E>> + Send + 'a, | ||||||
|         F: FnOnce(Box<dyn EntityGatewayTransaction + 'a>) -> Fut + Send, |         F: FnOnce(Self::Transaction) -> Fut + Send, | ||||||
|         R: Send, |         R: Send, | ||||||
|         E: From<GatewayError>, |         E: From<GatewayError>, | ||||||
|     { |     { | ||||||
| @ -372,10 +339,10 @@ impl EntityGateway for InMemoryGateway { | |||||||
|             trades: Arc::new(Mutex::new(trades)), |             trades: Arc::new(Mutex::new(trades)), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         let transaction = Box::new(InMemoryGatewayTransaction { |         let transaction = InMemoryGatewayTransaction { | ||||||
|             working_gateway, |             working_gateway, | ||||||
|             original_gateway: self, |             original_gateway: self.clone(), | ||||||
|         }); |         }; | ||||||
| 
 | 
 | ||||||
|         let (transaction, result) = func(transaction).await?; |         let (transaction, result) = func(transaction).await?; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ | |||||||
| use std::convert::{From, TryFrom, Into}; | use std::convert::{From, TryFrom, Into}; | ||||||
| use futures::{Future, TryStreamExt}; | use futures::{Future, TryStreamExt}; | ||||||
| use async_std::stream::StreamExt; | use async_std::stream::StreamExt; | ||||||
|  | use async_std::sync::{Arc, Mutex}; | ||||||
| use libpso::character::guildcard; | use libpso::character::guildcard; | ||||||
| use crate::entity::account::*; | use crate::entity::account::*; | ||||||
| use crate::entity::character::*; | use crate::entity::character::*; | ||||||
| @ -21,31 +22,41 @@ mod embedded { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #[derive(Clone)] | ||||||
| pub struct PostgresTransaction<'t> { | pub struct PostgresTransaction<'t> { | ||||||
|     pgtransaction: sqlx::Transaction<'t, sqlx::Postgres>, |     pgtransaction: Arc<Mutex<sqlx::Transaction<'t, sqlx::Postgres>>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #[async_trait::async_trait] | #[async_trait::async_trait] | ||||||
| impl<'t> EntityGatewayTransaction for PostgresTransaction<'t> { | 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 |         self | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn commit(self: Box<Self>) -> Result<(), GatewayError> { |     //async fn commit(self: Box<Self>) -> Result<(), GatewayError> {
 | ||||||
|         self.pgtransaction.commit().await?; |     async fn commit(self) -> Result<(), GatewayError> { | ||||||
|  |         //self.pgtransaction.lock().await.commit().await?;
 | ||||||
|  |         Arc::try_unwrap(self.pgtransaction) | ||||||
|  |             .unwrap() | ||||||
|  |             .into_inner() | ||||||
|  |             .commit() | ||||||
|  |             .await?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| pub struct PostgresGateway { | pub struct PostgresGateway<'t> { | ||||||
|     pool: sqlx::Pool<sqlx::Postgres>, |     pool: sqlx::Pool<sqlx::Postgres>, | ||||||
|  |     _t: std::marker::PhantomData<&'t ()>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl PostgresGateway { | impl<'t> PostgresGateway<'t> { | ||||||
|     pub fn new(host: &str, dbname: &str, username: &str, password: &str) -> PostgresGateway { |     pub fn new(host: &str, dbname: &str, username: &str, password: &str) -> PostgresGateway<'t> { | ||||||
|         let mut conn = refinery::config::Config::new(refinery::config::ConfigDbType::Postgres) |         let mut conn = refinery::config::Config::new(refinery::config::ConfigDbType::Postgres) | ||||||
|             .set_db_host(host) |             .set_db_host(host) | ||||||
|             .set_db_user(username) |             .set_db_user(username) | ||||||
| @ -61,6 +72,7 @@ impl PostgresGateway { | |||||||
| 
 | 
 | ||||||
|         PostgresGateway { |         PostgresGateway { | ||||||
|             pool, |             pool, | ||||||
|  |             _t: Default::default(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -581,24 +593,19 @@ async fn set_character_playtime(conn: &mut sqlx::PgConnection, char_id: &Charact | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait::async_trait] | #[async_trait::async_trait] | ||||||
| impl EntityGateway for PostgresGateway { | impl<'t> EntityGateway for PostgresGateway<'t> { | ||||||
|     async fn transaction<'a>(&'a mut self) -> Result<Box<dyn EntityGatewayTransaction + 'a>, GatewayError> |     type Transaction = PostgresTransaction<'t>; | ||||||
|     { |  | ||||||
|         Ok(Box::new(PostgresTransaction { |  | ||||||
|             pgtransaction: self.pool.begin().await?, |  | ||||||
|         })) |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, func: F) -> Result<R, E> |     async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, func: F) -> Result<R, E> | ||||||
|     where |     where | ||||||
|         Fut: Future<Output = Result<(Box<dyn EntityGatewayTransaction + 'a>, R), E>> + Send + 'a, |         Fut: Future<Output = Result<(Self::Transaction, R), E>> + Send + 'a, | ||||||
|         F: FnOnce(Box<dyn EntityGatewayTransaction + 'a>) -> Fut + Send, |         F: FnOnce(Self::Transaction) -> Fut + Send, | ||||||
|         R: Send, |         R: Send, | ||||||
|         E: From<GatewayError>, |         E: From<GatewayError>, | ||||||
|     { |     { | ||||||
|         let transaction = Box::new(PostgresTransaction { |         let transaction = PostgresTransaction { | ||||||
|             pgtransaction: self.pool.begin().await.map_err(|_| ()).unwrap() |             pgtransaction: Arc::new(Mutex::new(self.pool.begin().await.map_err(|_| ()).unwrap())) | ||||||
|         }); |         }; | ||||||
|         let (transaction, result) = func(transaction).await.map_err(|_| ()).unwrap(); |         let (transaction, result) = func(transaction).await.map_err(|_| ()).unwrap(); | ||||||
|         transaction.commit().await.map_err(|_| ()).unwrap(); |         transaction.commit().await.map_err(|_| ()).unwrap(); | ||||||
|         Ok(result) |         Ok(result) | ||||||
| @ -728,44 +735,46 @@ impl EntityGateway for PostgresGateway { | |||||||
| 
 | 
 | ||||||
| #[async_trait::async_trait] | #[async_trait::async_trait] | ||||||
| impl<'c> EntityGateway for PostgresTransaction<'c> { | impl<'c> EntityGateway for PostgresTransaction<'c> { | ||||||
|  |     type Transaction = PostgresTransaction<'c>; | ||||||
|  | 
 | ||||||
|     async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> { |     async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> { | ||||||
|         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<UserAccountEntity, GatewayError> { |     async fn get_user_by_id(&mut self, id: UserAccountId) -> Result<UserAccountEntity, GatewayError> { | ||||||
|         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<UserAccountEntity, GatewayError> { |     async fn get_user_by_name(&mut self, username: String) -> Result<UserAccountEntity, GatewayError> { | ||||||
|         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> { |     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<UserSettingsEntity, GatewayError> { |     async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result<UserSettingsEntity, GatewayError> { | ||||||
|         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<UserSettingsEntity, GatewayError> { |     async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> { | ||||||
|         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> { |     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<CharacterEntity, GatewayError> { |     async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> { | ||||||
|         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<CharacterEntity>; 4], GatewayError> { |     async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 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> { |     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<GuildCardDataEntity, GatewayError> { |     async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> { | ||||||
| @ -777,75 +786,75 @@ impl<'c> EntityGateway for PostgresTransaction<'c> { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn create_item(&mut self, item: NewItemEntity) -> Result<ItemEntity, GatewayError> { |     async fn create_item(&mut self, item: NewItemEntity) -> Result<ItemEntity, GatewayError> { | ||||||
|         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> { |     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> { |     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> { |     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> { |     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> { |     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<InventoryEntity, GatewayError> { |     async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> { | ||||||
|         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<BankEntity, GatewayError> { |     async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: &BankName) -> Result<BankEntity, GatewayError> { | ||||||
|         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> { |     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> { |     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<EquippedEntity, GatewayError> { |     async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result<EquippedEntity, GatewayError> { | ||||||
|         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> { |     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> { |     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<Meseta, GatewayError> { |     async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result<Meseta, GatewayError> { | ||||||
|         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> { |     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<Meseta, GatewayError> { |     async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName) -> Result<Meseta, GatewayError> { | ||||||
|         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<TradeEntity, GatewayError> { |     async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result<TradeEntity, GatewayError> { | ||||||
|         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> { |     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 | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -241,6 +241,13 @@ impl InventoryItemEntity { | |||||||
|             _ => None, |             _ => None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     pub fn stacked(&self) -> Option<&Vec<ItemEntity>> { | ||||||
|  |         match self { | ||||||
|  |             InventoryItemEntity::Stacked(i) => Some(i), | ||||||
|  |             _ => None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Debug, Default)] | #[derive(Clone, Debug, Default)] | ||||||
|  | |||||||
| @ -212,7 +212,43 @@ impl ToolType { | |||||||
|                  ToolType::Addslot | |                  ToolType::Addslot | | ||||||
|                  ToolType::PhotonDrop | |                  ToolType::PhotonDrop | | ||||||
|                  ToolType::PhotonSphere | |                  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 { |     pub fn max_stack(&self) -> usize { | ||||||
| @ -244,6 +280,41 @@ impl ToolType { | |||||||
|             ToolType::PhotonDrop => 99, |             ToolType::PhotonDrop => 99, | ||||||
|             ToolType::PhotonSphere => 99, |             ToolType::PhotonSphere => 99, | ||||||
|             ToolType::PhotonCrystal => 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, |             _ => 1, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -255,6 +326,7 @@ impl ToolType { | |||||||
|                  ToolType::HeartOfOpaOpa | |                  ToolType::HeartOfOpaOpa | | ||||||
|                  ToolType::HeartOfPian | |                  ToolType::HeartOfPian | | ||||||
|                  ToolType::HeartOfChao | |                  ToolType::HeartOfChao | | ||||||
|  |                  ToolType::HeartOfChuChu | | ||||||
|                  ToolType::HeartOfAngel | |                  ToolType::HeartOfAngel | | ||||||
|                  ToolType::HeartOfDevil | |                  ToolType::HeartOfDevil | | ||||||
|                  ToolType::KitOfHamburger | |                  ToolType::KitOfHamburger | | ||||||
| @ -264,6 +336,7 @@ impl ToolType { | |||||||
|                  ToolType::KitOfGenesis | |                  ToolType::KitOfGenesis | | ||||||
|                  ToolType::KitOfSegaSaturn | |                  ToolType::KitOfSegaSaturn | | ||||||
|                  ToolType::KitOfDreamcast | |                  ToolType::KitOfDreamcast | | ||||||
|  |                  ToolType::HeartOfKapuKapu | | ||||||
|                  ToolType::Tablet | |                  ToolType::Tablet | | ||||||
|                  ToolType::DragonScale | |                  ToolType::DragonScale | | ||||||
|                  ToolType::HeavenStrikerCoat | |                  ToolType::HeavenStrikerCoat | | ||||||
|  | |||||||
| @ -838,6 +838,7 @@ mod test { | |||||||
| 
 | 
 | ||||||
|         #[async_trait::async_trait] |         #[async_trait::async_trait] | ||||||
|         impl EntityGateway for TestData { |         impl EntityGateway for TestData { | ||||||
|  |             type Transaction = (); | ||||||
|             async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> { |             async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> { | ||||||
|                 Ok(UserSettingsEntity { |                 Ok(UserSettingsEntity { | ||||||
|                     id: UserSettingsId(0), |                     id: UserSettingsId(0), | ||||||
| @ -879,7 +880,9 @@ mod test { | |||||||
|     async fn test_user_checksum() { |     async fn test_user_checksum() { | ||||||
|         #[derive(Clone)] |         #[derive(Clone)] | ||||||
|         struct TestData; |         struct TestData; | ||||||
|         impl EntityGateway for TestData {} |         impl EntityGateway for TestData { | ||||||
|  |             type Transaction = (); | ||||||
|  |         } | ||||||
|         let mut server = CharacterServerState::new(TestData {}, AuthToken("".into())); |         let mut server = CharacterServerState::new(TestData {}, AuthToken("".into())); | ||||||
|         let send = server.handle(ClientId(1), RecvCharacterPacket::Checksum(Checksum {checksum: 1234, |         let send = server.handle(ClientId(1), RecvCharacterPacket::Checksum(Checksum {checksum: 1234, | ||||||
|                                                                                       padding: 0, |                                                                                       padding: 0, | ||||||
|  | |||||||
| @ -179,7 +179,7 @@ impl<EG: EntityGateway + Clone> ServerState for LoginServerState<EG> { | |||||||
| mod test { | mod test { | ||||||
|     use super::*; |     use super::*; | ||||||
|     use crate::entity::account::{UserAccountId}; |     use crate::entity::account::{UserAccountId}; | ||||||
|     use crate::entity::gateway::GatewayError; |     use crate::entity::gateway::{EntityGatewayTransaction, GatewayError}; | ||||||
| 
 | 
 | ||||||
|     const LOGIN_PACKET: RecvLoginPacket = RecvLoginPacket::Login(Login { |     const LOGIN_PACKET: RecvLoginPacket = RecvLoginPacket::Login(Login { | ||||||
|         tag: 65536, |         tag: 65536, | ||||||
| @ -205,6 +205,14 @@ mod test { | |||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     impl EntityGateway for () { | ||||||
|  |         type Transaction = (); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     impl EntityGatewayTransaction for () { | ||||||
|  |         type ParentGateway = (); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     #[async_std::test] |     #[async_std::test] | ||||||
|     async fn test_correct_login() { |     async fn test_correct_login() { | ||||||
|         #[derive(Clone)] |         #[derive(Clone)] | ||||||
| @ -213,6 +221,7 @@ mod test { | |||||||
| 
 | 
 | ||||||
|         #[async_trait::async_trait] |         #[async_trait::async_trait] | ||||||
|         impl EntityGateway for TestData { |         impl EntityGateway for TestData { | ||||||
|  |             type Transaction = (); | ||||||
|             async fn get_user_by_name(&mut self, name: String) -> Result<UserAccountEntity, GatewayError> { |             async fn get_user_by_name(&mut self, name: String) -> Result<UserAccountEntity, GatewayError> { | ||||||
|                 assert!(name == "testuser"); |                 assert!(name == "testuser"); | ||||||
|                 Ok(UserAccountEntity { |                 Ok(UserAccountEntity { | ||||||
| @ -271,6 +280,7 @@ mod test { | |||||||
| 
 | 
 | ||||||
|         #[async_trait::async_trait] |         #[async_trait::async_trait] | ||||||
|         impl EntityGateway for TestData { |         impl EntityGateway for TestData { | ||||||
|  |             type Transaction = (); | ||||||
|             async fn get_user_by_name(&mut self, _name: String) -> Result<UserAccountEntity, GatewayError> { |             async fn get_user_by_name(&mut self, _name: String) -> Result<UserAccountEntity, GatewayError> { | ||||||
|                 Err(GatewayError::Error) |                 Err(GatewayError::Error) | ||||||
|             } |             } | ||||||
| @ -305,6 +315,7 @@ mod test { | |||||||
| 
 | 
 | ||||||
|         #[async_trait::async_trait] |         #[async_trait::async_trait] | ||||||
|         impl EntityGateway for TestData { |         impl EntityGateway for TestData { | ||||||
|  |             type Transaction = (); | ||||||
|             async fn get_user_by_name(&mut self, name: String) -> Result<UserAccountEntity, GatewayError> { |             async fn get_user_by_name(&mut self, name: String) -> Result<UserAccountEntity, GatewayError> { | ||||||
|                 assert!(name == "testuser"); |                 assert!(name == "testuser"); | ||||||
|                 Ok(UserAccountEntity { |                 Ok(UserAccountEntity { | ||||||
| @ -354,6 +365,7 @@ mod test { | |||||||
| 
 | 
 | ||||||
|         #[async_trait::async_trait] |         #[async_trait::async_trait] | ||||||
|         impl EntityGateway for TestData { |         impl EntityGateway for TestData { | ||||||
|  |             type Transaction = (); | ||||||
|             async fn get_user_by_name(&mut self, name: String) -> Result<UserAccountEntity, GatewayError> { |             async fn get_user_by_name(&mut self, name: String) -> Result<UserAccountEntity, GatewayError> { | ||||||
|                 assert!(name == "testuser"); |                 assert!(name == "testuser"); | ||||||
|                 Ok(UserAccountEntity { |                 Ok(UserAccountEntity { | ||||||
|  | |||||||
| @ -1,33 +1,46 @@ | |||||||
| // TODO: replace various u32s and usizes denoting item amounts for ItemAmount(u32) for consistency
 | // TODO: replace various u32s and usizes denoting item amounts for ItemAmount(u32) for consistency
 | ||||||
| use crate::ship::items::ClientItemId; | use crate::ship::items::ClientItemId; | ||||||
| use crate::entity::item::{Meseta, ItemNote}; | use crate::entity::item::{Meseta, ItemNote}; | ||||||
|  | use async_std::sync::Arc; | ||||||
| use std::future::Future; | use std::future::Future; | ||||||
| use std::pin::Pin; | use std::pin::Pin; | ||||||
|  | use std::iter::IntoIterator; | ||||||
| 
 | 
 | ||||||
|  | use libpso::packet::{ship::Message, messages::GameMessage}; | ||||||
| use crate::ship::map::MapArea; | use crate::ship::map::MapArea; | ||||||
|  | use crate::ship::ship::SendShipPacket; | ||||||
| use crate::entity::character::{CharacterEntity, CharacterEntityId}; | 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::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail}; | ||||||
| use crate::ship::items::bank::{BankItem, BankItemDetail}; | use crate::ship::items::bank::{BankItem, BankItemDetail}; | ||||||
| use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; | use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; | ||||||
| use crate::ship::items::floor::{FloorItem, FloorItemDetail}; | 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::{ItemDetail, NewItemEntity, TradeId}; | ||||||
| use crate::entity::item::tool::Tool; | use crate::entity::item::tool::Tool; | ||||||
| use crate::entity::item::ItemModifier; | use crate::entity::item::ItemModifier; | ||||||
| use crate::ship::shops::ShopItem; | use crate::ship::shops::ShopItem; | ||||||
| use crate::ship::drops::{ItemDrop, ItemDropType}; | use crate::ship::drops::{ItemDrop, ItemDropType}; | ||||||
|  | use crate::ship::packet::builder; | ||||||
|  | use crate::ship::location::AreaClient; | ||||||
|  | 
 | ||||||
|  | type BoxFuture<T> = Pin<Box<dyn Future<Output=T> + Send>>; | ||||||
| 
 | 
 | ||||||
| pub enum TriggerCreateItem { | pub enum TriggerCreateItem { | ||||||
|     Yes, |     Yes, | ||||||
|     No |     No | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(super) fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) | pub(super) fn take_item_from_floor<EG, TR>( | ||||||
|                         -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                            -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>> |     item_id: ClientItemId | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway + Send, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, transaction): (ItemStateProxy<'_>, Box<dyn EntityGatewayTransaction + '_>) , _| { |     move |(mut item_state, transaction): (ItemStateProxy, TR) , _| { | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
|             let mut floor = item_state.floor(&character_id).await?; |             let mut floor = item_state.floor(&character_id).await?; | ||||||
|             let item = floor.take_item(&item_id).ok_or_else(|| ItemStateError::NoFloorItem(item_id))?; |             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) | pub(super) fn add_floor_item_to_inventory<EG, TR>( | ||||||
|                                -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem) |     character: &CharacterEntity | ||||||
|                                                   -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), TriggerCreateItem), ItemStateError>> + Send + 'a>> | ) -> impl Fn((ItemStateProxy, TR), FloorItem) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), TriggerCreateItem), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + Clone + 'static, | ||||||
| { | { | ||||||
|     let character = character.clone(); |     let character = character.clone(); | ||||||
|     move |(mut item_state, transaction), floor_item| { |     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)?; |             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_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; | ||||||
|             transaction.gateway().set_character_meseta(&character_id, inventory.meseta).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), |             Ok(((item_state, transaction), | ||||||
|                 match add_result { |                 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) | pub(super) fn take_item_from_inventory<EG, TR>( | ||||||
|                             -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                                -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> |     item_id: ClientItemId, | ||||||
|  |     amount: u32, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         Box::pin(async move { |         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))?; |             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?; |             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)) |             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)) | pub(super) fn add_inventory_item_to_shared_floor<EG, TR>( | ||||||
|                                   -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem) |     character_id: CharacterEntityId, | ||||||
|                                                      -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>> |     map_area: MapArea, | ||||||
|  |     drop_position: (f32, f32, f32), | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), InventoryItem) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, transaction), inventory_item| { |     move |(mut item_state, transaction), inventory_item| { | ||||||
|         Box::pin(async move { |         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) | pub(super) fn take_meseta_from_inventory<EG, TR>( | ||||||
|                               -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                                  -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>> |     amount: u32, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
|             let mut inventory = item_state.inventory(&character_id).await?; |             let mut inventory = item_state.inventory(&character_id).await?; | ||||||
|             inventory.remove_meseta(amount)?; |             inventory.remove_meseta(amount)?; | ||||||
|             transaction.gateway().set_character_meseta(&character_id, inventory.meseta).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), ())) |             Ok(((item_state, transaction), ())) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(super) fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u32) | pub(super) fn add_meseta_to_inventory<EG, TR>( | ||||||
|                               -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                                  -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>> |     amount: u32 | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
|             let mut inventory = item_state.inventory(&character_id).await?; |             let mut inventory = item_state.inventory(&character_id).await?; | ||||||
|             inventory.add_meseta(amount)?; |             inventory.add_meseta(amount)?; | ||||||
|             transaction.gateway().set_character_meseta(&character_id, inventory.meseta).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), ())) |             Ok(((item_state, transaction), ())) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(super) fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_area: MapArea, drop_position: (f32, f32)) | pub(super) fn add_meseta_to_shared_floor<EG, TR>( | ||||||
|                               -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                                  -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>> |     amount: u32, | ||||||
|  |     map_area: MapArea, | ||||||
|  |     drop_position: (f32, f32) | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
| 
 |  | ||||||
|     move |(mut item_state, transaction), _| { |     move |(mut item_state, transaction), _| { | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
|             let floor_item = FloorItem { |             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) | pub(super) fn take_meseta_from_bank<EG, TR>( | ||||||
|                          -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                             -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>> |     amount: u32, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         Box::pin(async move { |         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) | pub(super) fn add_meseta_from_bank_to_inventory<EG, TR>( | ||||||
|                          -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                             -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>> |     amount: u32, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         Box::pin(async move { |         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) | pub(super) fn add_meseta_to_bank<EG, TR>( | ||||||
|                       -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                          -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>> |     amount: u32, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         Box::pin(async move { |         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) | pub(super) fn take_item_from_bank<EG, TR>( | ||||||
|                         -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                                -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), BankItem), ItemStateError>> + Send + 'a>> |     item_id: ClientItemId, | ||||||
|  |     amount: u32, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), BankItem), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
|             let mut bank = item_state.bank(&character_id).await?; |             let mut bank = item_state.bank(&character_id).await?; | ||||||
|             let item = bank.take_item(&item_id, amount).ok_or_else(|| ItemStateError::NoBankItem(item_id))?; |             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?; |             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)) |             Ok(((item_state, transaction), item)) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(super) fn add_bank_item_to_inventory(character: &CharacterEntity) | pub(super) fn add_bank_item_to_inventory<EG, TR>( | ||||||
|                               -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), BankItem) |     character: &CharacterEntity, | ||||||
|                                                  -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> | ) -> impl Fn((ItemStateProxy, TR), BankItem) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     let character = character.clone(); |     let character = character.clone(); | ||||||
|     move |(mut item_state, transaction), bank_item| { |     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())?; |             inventory.add_item(inventory_item.clone())?; | ||||||
|             transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; |             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)) |             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) | pub(super) fn add_inventory_item_to_bank<EG, TR>( | ||||||
|                               -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem) |     character_id: CharacterEntityId, | ||||||
|                                                  -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>> | ) -> impl Fn((ItemStateProxy, TR), InventoryItem) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, transaction), inventory_item| { |     move |(mut item_state, transaction), inventory_item| { | ||||||
|         Box::pin(async move { |         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)?; |             bank.add_inventory_item(inventory_item)?; | ||||||
| 
 | 
 | ||||||
|             transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?; |             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), ())) |             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) | pub(super) fn equip_inventory_item<EG, TR>( | ||||||
|                         -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                            -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>> |     item_id: ClientItemId, | ||||||
|  |     equip_slot: u8, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |                      -> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
|             let mut inventory = item_state.inventory(&character_id).await?; |             let mut inventory = item_state.inventory(&character_id).await?; | ||||||
|             inventory.equip(&item_id, equip_slot); |             inventory.equip(&item_id, equip_slot); | ||||||
|             transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?; |             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), ())) |             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) | pub(super) fn unequip_inventory_item<EG, TR>( | ||||||
|                           -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                              -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>> |     item_id: ClientItemId, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
|             let mut inventory = item_state.inventory(&character_id).await?; |             let mut inventory = item_state.inventory(&character_id).await?; | ||||||
|             inventory.unequip(&item_id); |             inventory.unequip(&item_id); | ||||||
|             transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?; |             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), ())) |             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<ClientItemId>) | pub(super) fn sort_inventory_items<EG, TR>( | ||||||
|                         -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                            -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>> |     item_ids: Vec<ClientItemId>, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         let item_ids = item_ids.clone(); |         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?; |             let mut inventory = item_state.inventory(&character_id).await?; | ||||||
|             inventory.sort(&item_ids); |             inventory.sort(&item_ids); | ||||||
|             transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; |             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), ())) |             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) | pub(super) fn use_consumed_item<EG, TR>( | ||||||
|                       -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem) |     character: &CharacterEntity, | ||||||
|                                          -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), CharacterEntity), ItemStateError>> + Send + 'a>> | ) -> impl Fn((ItemStateProxy, TR), InventoryItem) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), Vec<ApplyItemAction>), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway + Clone + 'static, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|  |     let character = character.clone(); | ||||||
|     move |(mut item_state, transaction), inventory_item| { |     move |(mut item_state, transaction), inventory_item| { | ||||||
|         let mut character = character.clone(); |         let mut character = character.clone(); | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
| @ -390,17 +485,22 @@ pub(super) fn use_consumed_item(character: CharacterEntity) | |||||||
|                     Ok(transaction) |                     Ok(transaction) | ||||||
|                 }}).await?; |                 }}).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) | pub(super) fn feed_mag_item<EG, TR>( | ||||||
|                       -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem) |     character: CharacterEntity, | ||||||
|                                          -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), CharacterEntity), ItemStateError>> + Send + 'a>> |     mag_item_id: ClientItemId, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), InventoryItem) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), CharacterEntity), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, transaction), tool| { |     move |(mut item_state, transaction), tool| { | ||||||
|         let character = character.clone(); |         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); |             mag_entity.feed(food_tool); | ||||||
| 
 | 
 | ||||||
|             transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; |             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)) |             Ok(((item_state, transaction), character)) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | #[allow(clippy::needless_lifetimes)] // clippy this lifetime is not needless get off my case
 | ||||||
| pub(super) fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId, | pub(super) fn add_bought_item_to_inventory<'a, EG, TR>( | ||||||
|  |     character_id: CharacterEntityId, | ||||||
|     shop_item: &'a (dyn ShopItem + Send + Sync), |     shop_item: &'a (dyn ShopItem + Send + Sync), | ||||||
|     item_id: ClientItemId, |     item_id: ClientItemId, | ||||||
|                                     amount: u32) |     amount: u32, | ||||||
|                                     -> impl Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|                                                -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> |              -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>> + Send + 'a>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         Box::pin(async move { |         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?; |             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)) |             Ok(((item_state, transaction), inventory_item)) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| pub(super) fn sell_inventory_item<'a>(character_id: CharacterEntityId) | pub(super) fn sell_inventory_item<EG, TR>( | ||||||
|                            -> impl Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem) |     character_id: CharacterEntityId, | ||||||
|                                       -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> | ) -> impl Fn((ItemStateProxy, TR), InventoryItem) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, transaction), inventory_item| { |     move |(mut item_state, transaction), inventory_item| { | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
| @ -522,7 +630,7 @@ pub(super) fn sell_inventory_item<'a>(character_id: CharacterEntityId) | |||||||
|                     Ok(transaction) |                     Ok(transaction) | ||||||
|                 }}).await?; |                 }}).await?; | ||||||
|             transaction.gateway().set_character_meseta(&character_id, inventory.meseta).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)) |             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_recursion::async_recursion] | ||||||
| async fn iterate_inner<'a, I, O, T, F, FR>(state: (ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), | async fn iterate_inner<'a, EG, TR, I, O, T, F, FR>( | ||||||
|  |     state: (ItemStateProxy, TR), | ||||||
|     mut input: Vec<I>, |     mut input: Vec<I>, | ||||||
|     func: F, |     func: F, | ||||||
|                                            arg: T) |     arg: T, | ||||||
|                                            -> Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError> | ) -> Result<((ItemStateProxy, TR), Vec<O>), ItemStateError> | ||||||
| where | where | ||||||
|     'a: 'async_recursion, |     'a: 'async_recursion, | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG>, | ||||||
|     I: Send, |     I: Send, | ||||||
|     O: Send, |     O: Send, | ||||||
|     T: Clone + Send + Sync, |     T: Clone + Send + Sync, | ||||||
|     F: Fn(I) -> FR + Send + Sync + Clone + 'static, |     F: Fn(I) -> FR + Send + Sync + Clone + 'static, | ||||||
|     FR: Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T) |     FR: Fn((ItemStateProxy, TR), T) | ||||||
|            -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync, |            -> BoxFuture<Result<((ItemStateProxy, TR), O), ItemStateError>> + Send + Sync, | ||||||
| { | { | ||||||
|     let item = match input.pop() { |     let item = match input.pop() { | ||||||
|         Some(item) => item, |         Some(item) => item, | ||||||
| @ -558,18 +669,20 @@ where | |||||||
|     Ok((state, output)) |     Ok((state, output)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(super) fn iterate<'k, I, O, T, F, FR>( | pub(super) fn iterate<EG, TR, I, O, T, F, FR>( | ||||||
|     input: Vec<I>, |     input: Vec<I>, | ||||||
|     func: F) |     func: F, | ||||||
|     -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T) | ) -> impl Fn((ItemStateProxy, TR), T) | ||||||
|                        -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>> + Send + 'a>> |                      -> BoxFuture<Result<((ItemStateProxy, TR), Vec<O>), ItemStateError>> | ||||||
| where | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
|     O: Send, |     O: Send, | ||||||
|     I: Send + Clone + 'static + std::fmt::Debug, |     I: Send + Clone + 'static + std::fmt::Debug, | ||||||
|     T: Send + Clone +  'static + std::fmt::Debug, |     T: Send + Clone +  'static + std::fmt::Debug, | ||||||
|     F: Fn(I) -> FR + Send + Sync + Clone + 'static, |     F: Fn(I) -> FR + Send + Sync + Clone + 'static, | ||||||
|     FR: for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T) |     FR: Fn((ItemStateProxy, TR), T) | ||||||
|                    -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync, |            -> BoxFuture<Result<((ItemStateProxy, TR), O), ItemStateError>> + Send + Sync, | ||||||
|     T: Clone + Send + Sync, |     T: Clone + Send + Sync, | ||||||
| { | { | ||||||
|     move |(item_state, transaction), arg| { |     move |(item_state, transaction), arg| { | ||||||
| @ -584,19 +697,22 @@ where | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #[async_recursion::async_recursion] | #[async_recursion::async_recursion] | ||||||
| async fn foreach_inner<'a, O, T, F>(state: (ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), | async fn foreach_inner<'a, EG, TR, O, T, F, I>( | ||||||
|                                         mut input: Vec<T>, |     state: (ItemStateProxy, TR), | ||||||
|                                         func: F) |     mut input: I, | ||||||
|                                         -> Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError> |     func: Arc<F>, | ||||||
|  | ) -> Result<((ItemStateProxy, TR), Vec<O>), ItemStateError> | ||||||
| where | where | ||||||
|     'a: 'async_recursion, |     'a: 'async_recursion, | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
|     O: Send, |     O: Send, | ||||||
|     T: Clone + Send, |     T: Send, | ||||||
|     F: Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T) |     F: Fn((ItemStateProxy, TR), T) | ||||||
|                    -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync, |                    -> BoxFuture<Result<((ItemStateProxy, TR), O), ItemStateError>> + Send + Sync, | ||||||
|     F: Clone, |     I: Iterator<Item = T> + Send + Sync + 'static, | ||||||
| { | { | ||||||
|     let item = match input.pop() { |     let item = match input.next() { | ||||||
|         Some(item) => item, |         Some(item) => item, | ||||||
|         None => return Ok((state, Vec::new())) |         None => return Ok((state, Vec::new())) | ||||||
|     }; |     }; | ||||||
| @ -609,19 +725,25 @@ where | |||||||
|     Ok((state, output)) |     Ok((state, output)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(super) fn foreach<'k, O, T, F>(func: F) | pub(super) fn foreach<EG, TR, O, T, F, I>( | ||||||
|                                 -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<T>) |     func: F | ||||||
|                                                    -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>> + Send + 'a>> | ) -> impl Fn((ItemStateProxy, TR), I) | ||||||
|  |                      -> BoxFuture<Result<((ItemStateProxy, TR), Vec<O>), ItemStateError>> | ||||||
| where | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
|     O: Send, |     O: Send, | ||||||
|     T: Send + Clone +  'static + std::fmt::Debug, |     T: Send + Clone +  'static + std::fmt::Debug, | ||||||
|     F: for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T) |     F: Fn((ItemStateProxy, TR), T) | ||||||
|                    -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync + 'static, |           -> BoxFuture<Result<((ItemStateProxy, TR), O), ItemStateError>> + Send + Sync + 'static, | ||||||
|     F: Clone, |     T: Send + Sync, | ||||||
|     T: Clone + Send + Sync, |     I: IntoIterator<Item = T> + Send + Sync + 'static, | ||||||
|  |     I::IntoIter: Send + Sync, | ||||||
| { | { | ||||||
|  |     let func = Arc::new(func); | ||||||
|     move |(item_state, transaction), items| { |     move |(item_state, transaction), items| { | ||||||
|         let func = func.clone(); |         let func = func.clone(); | ||||||
|  |         let items = items.into_iter(); | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
|             let (state, result) = foreach_inner((item_state, transaction), items, func).await?; |             let (state, result) = foreach_inner((item_state, transaction), items, func).await?; | ||||||
|             Ok((state, result)) |             Ok((state, result)) | ||||||
| @ -629,9 +751,14 @@ where | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(super) fn insert<'a, T: Send + Clone + 'a>(element: T) | pub(super) fn insert<'a, EG, TR, T>( | ||||||
|          -> impl Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     element: T | ||||||
|                             -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T), ItemStateError>> + Send + 'a>> | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy, TR), T), ItemStateError>> + Send + 'a>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
|  |     T: Send + Clone + 'a, | ||||||
| { | { | ||||||
|     move |state, _| { |     move |state, _| { | ||||||
|         let element = element.clone(); |         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) | pub(super) fn fork<EG, TR, F1, F2, T, O1, O2>( | ||||||
|                          -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem) |     func1: F1, | ||||||
|                                             -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> + Clone |     func2: F2, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), T) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), (O1, O2)), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
|  |     F1: Fn((ItemStateProxy, TR), T) -> BoxFuture<Result<((ItemStateProxy, TR), O1), ItemStateError>> + Send + Sync + 'static, | ||||||
|  |     F2: Fn((ItemStateProxy, TR), T) -> BoxFuture<Result<((ItemStateProxy, TR), O2), ItemStateError>> + 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<EG, TR>( | ||||||
|  |     character: CharacterEntity, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), InventoryItem) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>> + Clone | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, transaction), inventory_item| { |     move |(mut item_state, transaction), inventory_item| { | ||||||
|         let character = character.clone(); |         let character = character.clone(); | ||||||
| @ -658,16 +817,22 @@ pub(super) fn add_item_to_inventory(character: CharacterEntity) | |||||||
| 
 | 
 | ||||||
|             inventory.add_item(inventory_item.clone())?; |             inventory.add_item(inventory_item.clone())?; | ||||||
|             transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; |             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)) |             Ok(((item_state, transaction), inventory_item)) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(super) fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, character_from: CharacterEntityId) | pub(super) fn record_trade<EG, TR>( | ||||||
|                 -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<InventoryItem>) |     trade_id: TradeId, | ||||||
|                                    -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<InventoryItem>), ItemStateError>> + Send + 'a>> + Clone |     character_to: CharacterEntityId, | ||||||
|  |     character_from: CharacterEntityId, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), Vec<InventoryItem>) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), Vec<InventoryItem>), ItemStateError>> + Clone | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(item_state, mut transaction), traded_items| { |     move |(item_state, mut transaction), traded_items| { | ||||||
|         Box::pin(async move { |         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() | pub(super) fn assign_new_item_id<EG, TR>( | ||||||
|                       -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem) | ) -> impl Fn((ItemStateProxy, TR), InventoryItem) | ||||||
|                                          -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> + Clone |              -> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>> + Clone | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, transaction), mut inventory_item| { |     move |(mut item_state, transaction), mut inventory_item| { | ||||||
|         Box::pin(async move { |         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) | pub(super) fn convert_item_drop_to_floor_item<EG, TR>( | ||||||
|                       -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) |     character_id: CharacterEntityId, | ||||||
|                                          -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>> + Clone |     item_drop: ItemDrop, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ()) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), ItemStateError>> + Clone | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, mut transaction), _| { |     move |(mut item_state, mut transaction), _| { | ||||||
|         let item_drop = item_drop.clone(); |         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) | pub(super) fn add_item_to_local_floor<EG, TR>( | ||||||
|                         -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem) |     character_id: CharacterEntityId, | ||||||
|                                            -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>> | ) -> impl Fn((ItemStateProxy, TR), FloorItem) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(mut item_state, transaction) , floor_item| { |     move |(mut item_state, transaction) , floor_item| { | ||||||
|         Box::pin(async move { |         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) | pub(super) fn apply_modifier_to_inventory_item<EG, TR>( | ||||||
|                         -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem) |     modifier: ItemModifier, | ||||||
|                                            -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> | ) -> impl Fn((ItemStateProxy, TR), InventoryItem) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(item_state, mut transaction), mut inventory_item| { |     move |(item_state, mut transaction), mut inventory_item| { | ||||||
|         let modifier = modifier.clone(); |         let modifier = modifier.clone(); | ||||||
| @ -833,9 +1014,12 @@ pub(super) fn apply_modifier_to_inventory_item(modifier: ItemModifier) | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(super) fn as_individual_item() | pub(super) fn as_individual_item<EG, TR>( | ||||||
|                       -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem) | ) -> impl Fn((ItemStateProxy, TR), InventoryItem) | ||||||
|                                          -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), IndividualItemDetail), ItemStateError>> + Send + 'a>> |              -> BoxFuture<Result<((ItemStateProxy, TR), IndividualItemDetail), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + 'static, | ||||||
| { | { | ||||||
|     move |(item_state, transaction), inventory_item| { |     move |(item_state, transaction), inventory_item| { | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
| @ -848,3 +1032,85 @@ pub(super) fn as_individual_item() | |||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | pub(super) fn apply_item_action_packets<EG, TR>( | ||||||
|  |     character_id: CharacterEntityId, | ||||||
|  |     area_client: AreaClient, | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), ApplyItemAction) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), Vec<SendShipPacket>), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + '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<EG, TR>( | ||||||
|  |     character: &CharacterEntity | ||||||
|  | ) -> impl Fn((ItemStateProxy, TR), Vec<ApplyItemAction>) | ||||||
|  |              -> BoxFuture<Result<((ItemStateProxy, TR), CharacterEntity), ItemStateError>> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway, | ||||||
|  |     TR: EntityGatewayTransaction<ParentGateway = EG> + '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)) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,9 +1,12 @@ | |||||||
| use thiserror::Error; |  | ||||||
| use std::convert::TryInto; | 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::gateway::{EntityGateway, GatewayError}; | ||||||
| use crate::entity::character::CharacterEntity; | use crate::entity::character::CharacterEntity; | ||||||
| use crate::entity::item::mag::{MagCell, MagCellError}; | 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::entity::item::{ItemDetail, ItemEntityId}; | ||||||
| use crate::ship::items::state::{ItemStateProxy, ItemStateError}; | use crate::ship::items::state::{ItemStateProxy, ItemStateError}; | ||||||
| use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; | use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; | ||||||
| @ -27,53 +30,60 @@ pub enum ApplyItemError { | |||||||
|     MagCellError(#[from] MagCellError), |     MagCellError(#[from] MagCellError), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub enum ApplyItemAction { | ||||||
|  |     UpdateCharacter(Box<CharacterEntity>), | ||||||
|  |     CreateItem(ItemDetail), | ||||||
|  |     //TransformItem,
 | ||||||
|  |     //RemoveItem,
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| impl From<ItemStateError> for ApplyItemError { | impl From<ItemStateError> for ApplyItemError { | ||||||
|     fn from(other: ItemStateError) -> ApplyItemError { |     fn from(other: ItemStateError) -> ApplyItemError { | ||||||
|         ApplyItemError::ItemStateError(Box::new(other)) |         ApplyItemError::ItemStateError(Box::new(other)) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO: make all these functions not-pub
 | async fn power_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> { | ||||||
| pub async fn power_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { |  | ||||||
|     character.materials.power += 1; |     character.materials.power += 1; | ||||||
|     entity_gateway.save_character(character).await?; |     entity_gateway.save_character(character).await?; | ||||||
|     Ok(()) |     Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn mind_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { | async fn mind_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> { | ||||||
|     character.materials.mind += 1; |     character.materials.mind += 1; | ||||||
|     entity_gateway.save_character(character).await.unwrap(); |     entity_gateway.save_character(character).await.unwrap(); | ||||||
|     Ok(()) |     Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn evade_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { | async fn evade_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> { | ||||||
|     character.materials.evade += 1; |     character.materials.evade += 1; | ||||||
|     entity_gateway.save_character(character).await.unwrap(); |     entity_gateway.save_character(character).await.unwrap(); | ||||||
|     Ok(()) |     Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn def_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { | async fn def_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> { | ||||||
|     character.materials.def += 1; |     character.materials.def += 1; | ||||||
|     entity_gateway.save_character(character).await.unwrap(); |     entity_gateway.save_character(character).await.unwrap(); | ||||||
|     Ok(()) |     Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn luck_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { | async fn luck_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> { | ||||||
|     character.materials.luck += 1; |     character.materials.luck += 1; | ||||||
|     entity_gateway.save_character(character).await.unwrap(); |     entity_gateway.save_character(character).await.unwrap(); | ||||||
|     Ok(()) |     Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn hp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { | async fn hp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> { | ||||||
|     character.materials.hp += 1; |     character.materials.hp += 1; | ||||||
|     entity_gateway.save_character(character).await.unwrap(); |     entity_gateway.save_character(character).await.unwrap(); | ||||||
|     Ok(()) |     Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn tp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { | async fn tp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> { | ||||||
|     character.materials.tp += 1; |     character.materials.tp += 1; | ||||||
|     entity_gateway.save_character(character).await.unwrap(); |     entity_gateway.save_character(character).await.unwrap(); | ||||||
|     Ok(()) |     Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
| @ -98,12 +108,12 @@ async fn mag_cell<EG: EntityGateway>(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, |                           entity_gateway: &mut EG, | ||||||
|                           character: &CharacterEntity, |                           character: &CharacterEntity, | ||||||
|                           cell_entity_id: ItemEntityId, |                           cell_entity_id: ItemEntityId, | ||||||
|                           mag_cell_type: MagCell) |                           mag_cell_type: MagCell) | ||||||
|                           -> Result<(), ApplyItemError> |                           -> Result<Vec<ApplyItemAction>, ApplyItemError> | ||||||
| where | where | ||||||
|     EG: EntityGateway + ?Sized, |     EG: EntityGateway + ?Sized, | ||||||
| { | { | ||||||
| @ -115,9 +125,9 @@ where | |||||||
| 
 | 
 | ||||||
|     entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await?; |     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?; |     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<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: | |||||||
| } | } | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
| async fn apply_tool<'a, EG>(item_state: &mut ItemStateProxy<'a>, | 
 | ||||||
|  | fn jack_o_lantern() -> Result<Vec<ApplyItemAction>, 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, |                             entity_gateway: &mut EG, | ||||||
|                             character: &mut CharacterEntity, |                             character: &mut CharacterEntity, | ||||||
|                             entity_id: ItemEntityId, |                             entity_id: ItemEntityId, | ||||||
|                             tool: ToolType) |                             tool: ToolType) | ||||||
|                             -> Result<(), ApplyItemError> |                             -> Result<Vec<ApplyItemAction>, ApplyItemError> | ||||||
| where | where | ||||||
|     EG: EntityGateway + ?Sized, |     EG: EntityGateway + ?Sized, | ||||||
| { | { | ||||||
| @ -235,13 +264,13 @@ where | |||||||
|         ToolType::LuckMaterial => luck_material(entity_gateway, character).await, |         ToolType::LuckMaterial => luck_material(entity_gateway, character).await, | ||||||
|         ToolType::HpMaterial => hp_material(entity_gateway, character).await, |         ToolType::HpMaterial => hp_material(entity_gateway, character).await, | ||||||
|         ToolType::TpMaterial => tp_material(entity_gateway, character).await, |         ToolType::TpMaterial => tp_material(entity_gateway, character).await, | ||||||
|         ToolType::Monomate => Ok(()), |         ToolType::Monomate => Ok(Vec::new()), | ||||||
|         ToolType::Dimate => Ok(()), |         ToolType::Dimate => Ok(Vec::new()), | ||||||
|         ToolType::Trimate => Ok(()), |         ToolType::Trimate => Ok(Vec::new()), | ||||||
|         ToolType::Monofluid => Ok(()), |         ToolType::Monofluid => Ok(Vec::new()), | ||||||
|         ToolType::Difluid => Ok(()), |         ToolType::Difluid => Ok(Vec::new()), | ||||||
|         ToolType::Trifluid => Ok(()), |         ToolType::Trifluid => Ok(Vec::new()), | ||||||
|         ToolType::HuntersReport => Ok(()), |         ToolType::HuntersReport => Ok(Vec::new()), | ||||||
|         ToolType::CellOfMag502 |         ToolType::CellOfMag502 | ||||||
|             | ToolType::CellOfMag213 |             | ToolType::CellOfMag213 | ||||||
|             | ToolType::PartsOfRobochao |             | ToolType::PartsOfRobochao | ||||||
| @ -268,6 +297,7 @@ where | |||||||
|             | ToolType::LibertaKit => { |             | ToolType::LibertaKit => { | ||||||
|                 mag_cell(item_state, entity_gateway, character, entity_id, tool.try_into()?).await |                 mag_cell(item_state, entity_gateway, character, entity_id, tool.try_into()?).await | ||||||
|             } |             } | ||||||
|  |         ToolType::JackOLantern => jack_o_lantern(), | ||||||
|         // TODO: rest of these
 |         // TODO: rest of these
 | ||||||
|         _ => Err(ApplyItemError::InvalidItem) |         _ => 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<Vec<ApplyItemAction>, ApplyItemError> | ||||||
|  | where | ||||||
|  |     EG: EntityGateway + ?Sized + Clone + 'static | ||||||
|  | { | ||||||
|     match item.item { |     match item.item { | ||||||
|         InventoryItemDetail::Individual(individual_item) => { |         InventoryItemDetail::Individual(individual_item) => { | ||||||
|             match individual_item.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) => { |         InventoryItemDetail::Stacked(stacked_item) => { | ||||||
|             for entity_id in stacked_item.entity_ids { |             Ok(join_all(stacked_item.entity_ids.iter() | ||||||
|                 apply_tool(item_state, entity_gateway, character, entity_id, stacked_item.tool.tool).await? |                         .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 | ||||||
|                             } |                             } | ||||||
|             Ok(()) |                         }) | ||||||
|  |                         .collect::<Vec<_>>()) | ||||||
|  |                .await | ||||||
|  |                .into_iter() | ||||||
|  |                .collect::<Result<Vec<Vec<_>>, _>>()? | ||||||
|  |                .into_iter() | ||||||
|  |                .flatten() | ||||||
|  |                .collect()) | ||||||
|         }, |         }, | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -179,7 +179,7 @@ pub enum InventoryError { | |||||||
|     MesetaFull, |     MesetaFull, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone)] | #[derive(Clone, Debug)] | ||||||
| pub struct InventoryState { | pub struct InventoryState { | ||||||
|     pub character_id: CharacterEntityId, |     pub character_id: CharacterEntityId, | ||||||
|     pub item_id_counter: u32, |     pub item_id_counter: u32, | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| use std::collections::HashMap; | use std::collections::HashMap; | ||||||
| use async_std::sync::{Arc, RwLock}; | use async_std::sync::{Arc, RwLock, Mutex}; | ||||||
| use futures::future::join_all; | use futures::future::join_all; | ||||||
| 
 | 
 | ||||||
| use crate::entity::gateway::{EntityGateway, GatewayError}; | use crate::entity::gateway::{EntityGateway, GatewayError}; | ||||||
| @ -124,7 +124,7 @@ pub enum AddItemResult { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #[derive(Clone)] | #[derive(Clone, Debug)] | ||||||
| pub struct ItemState { | pub struct ItemState { | ||||||
|     character_inventory: Arc<RwLock<HashMap<CharacterEntityId, RwLock<InventoryState>>>>, |     character_inventory: Arc<RwLock<HashMap<CharacterEntityId, RwLock<InventoryState>>>>, | ||||||
|     character_bank: Arc<RwLock<HashMap<CharacterEntityId, RwLock<BankState>>>>, |     character_bank: Arc<RwLock<HashMap<CharacterEntityId, RwLock<BankState>>>>, | ||||||
| @ -373,36 +373,38 @@ impl ItemState { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #[derive(Default)] | #[derive(Default, Clone)] | ||||||
| struct ProxiedItemState { | struct ProxiedItemState { | ||||||
|     character_inventory: HashMap<CharacterEntityId, InventoryState>, |     character_inventory: Arc<Mutex<HashMap<CharacterEntityId, InventoryState>>>, | ||||||
|     character_bank: HashMap<CharacterEntityId, BankState>, |     character_bank: Arc<Mutex<HashMap<CharacterEntityId, BankState>>>, | ||||||
| 
 | 
 | ||||||
|     //character_room: HashMap<CharacterEntityId, RoomId>,
 |     //character_room: HashMap<CharacterEntityId, RoomId>,
 | ||||||
|     character_floor: HashMap<CharacterEntityId, LocalFloor>, |     character_floor: Arc<Mutex<HashMap<CharacterEntityId, LocalFloor>>>, | ||||||
|     room_floor: HashMap<RoomId, SharedFloor>, |     room_floor: Arc<Mutex<HashMap<RoomId, SharedFloor>>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct ItemStateProxy<'a> { | #[derive(Clone)] | ||||||
|     item_state: &'a mut ItemState, | pub struct ItemStateProxy { | ||||||
|  |     item_state: ItemState, | ||||||
|     proxied_state: ProxiedItemState, |     proxied_state: ProxiedItemState, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<'a> ItemStateProxy<'a> { | impl ItemStateProxy { | ||||||
|     pub async fn commit(self) { |     pub async fn commit(self) { | ||||||
|         async fn copy_back<K, V>(master: &Arc<RwLock<HashMap<K, RwLock<V>>>>, |         async fn copy_back<K, V>(master: &Arc<RwLock<HashMap<K, RwLock<V>>>>, | ||||||
|                                  proxy: HashMap<K, V>) |                                  proxy: Arc<Mutex<HashMap<K, V>>>) | ||||||
|         where |         where | ||||||
|             K: Eq + std::hash::Hash, |             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 |                 if let Some(element) = master | ||||||
|                     .read() |                     .read() | ||||||
|                     .await |                     .await | ||||||
|                     .get(&key) { |                     .get(key) { | ||||||
|                         *element |                         *element | ||||||
|                             .write() |                             .write() | ||||||
|                             .await = value; |                             .await = value.clone(); | ||||||
|                     } |                     } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -417,7 +419,7 @@ impl<'a> ItemStateProxy<'a> { | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| async fn get_or_clone<K, V>(master: &Arc<RwLock<HashMap<K, RwLock<V>>>>, | async fn get_or_clone<K, V>(master: &Arc<RwLock<HashMap<K, RwLock<V>>>>, | ||||||
|                             proxy: &mut HashMap<K, V>, |                             proxy: &Arc<Mutex<HashMap<K, V>>>, | ||||||
|                             key: K, |                             key: K, | ||||||
|                             err: fn(K) -> ItemStateError) -> Result<V, ItemStateError> |                             err: fn(K) -> ItemStateError) -> Result<V, ItemStateError> | ||||||
| where | where | ||||||
| @ -432,14 +434,17 @@ where | |||||||
|         .read() |         .read() | ||||||
|         .await |         .await | ||||||
|         .clone(); |         .clone(); | ||||||
|     Ok(proxy.entry(key) |     Ok(proxy | ||||||
|  |        .lock() | ||||||
|  |        .await | ||||||
|  |        .entry(key) | ||||||
|        .or_insert_with(|| existing_element) |        .or_insert_with(|| existing_element) | ||||||
|        .clone()) |        .clone()) | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<'a> ItemStateProxy<'a> { | impl ItemStateProxy { | ||||||
|     pub fn new(item_state: &'a mut ItemState) -> Self { |     pub fn new(item_state: ItemState) -> Self { | ||||||
|         ItemStateProxy { |         ItemStateProxy { | ||||||
|             item_state, |             item_state, | ||||||
|             proxied_state: Default::default(), |             proxied_state: Default::default(), | ||||||
| @ -448,39 +453,39 @@ impl<'a> ItemStateProxy<'a> { | |||||||
| 
 | 
 | ||||||
|     pub async fn inventory(&mut self, character_id: &CharacterEntityId) -> Result<InventoryState, ItemStateError> { |     pub async fn inventory(&mut self, character_id: &CharacterEntityId) -> Result<InventoryState, ItemStateError> { | ||||||
|         get_or_clone(&self.item_state.character_inventory, |         get_or_clone(&self.item_state.character_inventory, | ||||||
|                      &mut self.proxied_state.character_inventory, |                      &self.proxied_state.character_inventory, | ||||||
|                      *character_id, |                      *character_id, | ||||||
|                      ItemStateError::NoCharacter).await |                      ItemStateError::NoCharacter).await | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn set_inventory(&mut self, inventory: InventoryState) { |     pub async fn set_inventory(&mut self, inventory: InventoryState) { | ||||||
|         self.proxied_state.character_inventory.insert(inventory.character_id, inventory); |         self.proxied_state.character_inventory.lock().await.insert(inventory.character_id, inventory); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn bank(&mut self, character_id: &CharacterEntityId) -> Result<BankState, ItemStateError> { |     pub async fn bank(&mut self, character_id: &CharacterEntityId) -> Result<BankState, ItemStateError> { | ||||||
|         get_or_clone(&self.item_state.character_bank, |         get_or_clone(&self.item_state.character_bank, | ||||||
|                      &mut self.proxied_state.character_bank, |                      &self.proxied_state.character_bank, | ||||||
|                      *character_id, |                      *character_id, | ||||||
|                      ItemStateError::NoCharacter).await |                      ItemStateError::NoCharacter).await | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn set_bank(&mut self, bank: BankState) { |     pub async fn set_bank(&mut self, bank: BankState) { | ||||||
|         self.proxied_state.character_bank.insert(bank.character_id, bank); |         self.proxied_state.character_bank.lock().await.insert(bank.character_id, bank); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn floor(&mut self, character_id: &CharacterEntityId) -> Result<FloorState, ItemStateError> { |     pub async fn floor(&mut self, character_id: &CharacterEntityId) -> Result<FloorState, ItemStateError> { | ||||||
|         let room_id = *self.item_state.character_room.read().await.get(character_id).unwrap(); |         let room_id = *self.item_state.character_room.read().await.get(character_id).unwrap(); | ||||||
|         Ok(FloorState { |         Ok(FloorState { | ||||||
|             character_id: *character_id, |             character_id: *character_id, | ||||||
|             local: get_or_clone(&self.item_state.character_floor, &mut self.proxied_state.character_floor, *character_id, ItemStateError::NoCharacter).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, &mut self.proxied_state.room_floor, room_id, ItemStateError::NoRoom).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) { |     pub async fn set_floor(&mut self, floor: FloorState) { | ||||||
|         let room_id = *self.item_state.character_room.read().await.get(&floor.character_id).unwrap(); |         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.character_floor.lock().await.insert(floor.character_id, floor.local); | ||||||
|         self.proxied_state.room_floor.insert(room_id, floor.shared); |         self.proxied_state.room_floor.lock().await.insert(room_id, floor.shared); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> { |     pub async fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> { | ||||||
|  | |||||||
| @ -1,9 +1,10 @@ | |||||||
| use crate::ship::items::ClientItemId; | use crate::ship::items::ClientItemId; | ||||||
| use crate::entity::item::Meseta; | use crate::entity::item::Meseta; | ||||||
| 
 | 
 | ||||||
|  | use crate::ship::ship::SendShipPacket; | ||||||
| use crate::ship::map::MapArea; | use crate::ship::map::MapArea; | ||||||
| use crate::entity::character::{CharacterEntity, CharacterEntityId}; | 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::state::{ItemState, ItemStateProxy, ItemStateError, IndividualItemDetail}; | ||||||
| use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction}; | use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction}; | ||||||
| use crate::ship::items::inventory::InventoryItem; | use crate::ship::items::inventory::InventoryItem; | ||||||
| @ -23,10 +24,11 @@ pub async fn pick_up_item<EG>( | |||||||
|     item_id: &ClientItemId) |     item_id: &ClientItemId) | ||||||
|     -> Result<actions::TriggerCreateItem, ItemStateError> |     -> Result<actions::TriggerCreateItem, ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
|  |     EG::Transaction: Clone, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::take_item_from_floor(character.id, *item_id)) |             .act(actions::take_item_from_floor(character.id, *item_id)) | ||||||
|             .act(actions::add_floor_item_to_inventory(character)) |             .act(actions::add_floor_item_to_inventory(character)) | ||||||
| @ -46,10 +48,10 @@ pub async fn drop_item<EG>( | |||||||
|     drop_position: (f32, f32, f32)) |     drop_position: (f32, f32, f32)) | ||||||
|     -> Result<FloorItem, ItemStateError> |     -> Result<FloorItem, ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::take_item_from_inventory(character.id, *item_id, 0)) |             .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)) |             .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) |     amount: u32) | ||||||
|     -> Result<FloorItem, ItemStateError> |     -> Result<FloorItem, ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::take_item_from_inventory(character.id, *item_id, amount)) |             .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))) |             .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) |     amount: u32) | ||||||
|     -> Result<FloorItem, ItemStateError> |     -> Result<FloorItem, ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::take_meseta_from_inventory(character.id, amount)) |             .act(actions::take_meseta_from_inventory(character.id, amount)) | ||||||
|             .act(actions::add_meseta_to_shared_floor(character.id, amount, map_area, drop_position)) |             .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) |     amount: u32) | ||||||
|     -> Result<(), ItemStateError> |     -> Result<(), ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::take_meseta_from_bank(character.id, amount)) |             .act(actions::take_meseta_from_bank(character.id, amount)) | ||||||
|             .act(actions::add_meseta_from_bank_to_inventory(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) |     amount: u32) | ||||||
|     -> Result<(), ItemStateError> |     -> Result<(), ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), _) = ItemStateAction::default() | ||||||
|             .act(actions::take_meseta_from_inventory(character.id, amount)) |             .act(actions::take_meseta_from_inventory(character.id, amount)) | ||||||
|             .act(actions::add_meseta_to_bank(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) |     amount: u32) | ||||||
|     -> Result<InventoryItem, ItemStateError> |     -> Result<InventoryItem, ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::take_item_from_bank(character.id, *item_id, amount)) |             .act(actions::take_item_from_bank(character.id, *item_id, amount)) | ||||||
|             //.act(bank_item_to_inventory_item)
 |             //.act(bank_item_to_inventory_item)
 | ||||||
| @ -187,10 +189,10 @@ pub async fn deposit_item<'a, EG> ( | |||||||
|     amount: u32) |     amount: u32) | ||||||
|     -> Result<(), ItemStateError> |     -> Result<(), ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::take_item_from_inventory(character.id, *item_id, amount)) |             .act(actions::take_item_from_inventory(character.id, *item_id, amount)) | ||||||
|             .act(actions::add_inventory_item_to_bank(character.id)) |             .act(actions::add_inventory_item_to_bank(character.id)) | ||||||
| @ -209,10 +211,10 @@ pub async fn equip_item<'a, EG> ( | |||||||
|     equip_slot: u8, |     equip_slot: u8, | ||||||
| ) -> Result<(), ItemStateError> | ) -> Result<(), ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::equip_inventory_item(character.id, *item_id, equip_slot)) |             .act(actions::equip_inventory_item(character.id, *item_id, equip_slot)) | ||||||
|             .commit((item_state_proxy, transaction)) |             .commit((item_state_proxy, transaction)) | ||||||
| @ -230,10 +232,10 @@ pub async fn unequip_item<'a, EG> ( | |||||||
|     item_id: &ClientItemId, |     item_id: &ClientItemId, | ||||||
| ) -> Result<(), ItemStateError> | ) -> Result<(), ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::unequip_inventory_item(character.id, *item_id)) |             .act(actions::unequip_inventory_item(character.id, *item_id)) | ||||||
|             .commit((item_state_proxy, transaction)) |             .commit((item_state_proxy, transaction)) | ||||||
| @ -251,10 +253,10 @@ pub async fn sort_inventory<'a, EG> ( | |||||||
|     item_ids: Vec<ClientItemId>, |     item_ids: Vec<ClientItemId>, | ||||||
| ) -> Result<(), ItemStateError> | ) -> Result<(), ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::sort_inventory_items(character.id, item_ids)) |             .act(actions::sort_inventory_items(character.id, item_ids)) | ||||||
|             .commit((item_state_proxy, transaction)) |             .commit((item_state_proxy, transaction)) | ||||||
| @ -269,22 +271,28 @@ pub async fn use_item<'a, EG> ( | |||||||
|     item_state: &'a mut ItemState, |     item_state: &'a mut ItemState, | ||||||
|     entity_gateway: &mut EG, |     entity_gateway: &mut EG, | ||||||
|     character: &mut CharacterEntity, |     character: &mut CharacterEntity, | ||||||
|  |     area_client: AreaClient, | ||||||
|     item_id: &ClientItemId, |     item_id: &ClientItemId, | ||||||
|     amount: u32, |     amount: u32, | ||||||
| ) -> Result<(), ItemStateError> | ) -> Result<Vec<SendShipPacket>, ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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), new_character) = ItemStateAction::default() |         let ((item_state_proxy, transaction), (pkts, new_character)) = ItemStateAction::default() | ||||||
|             .act(actions::take_item_from_inventory(character.id, *item_id, amount)) |             .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)) |             .commit((item_state_proxy, transaction)) | ||||||
|             .await?; |             .await?; | ||||||
|         item_state_proxy.commit().await; |         item_state_proxy.commit().await; | ||||||
|  | 
 | ||||||
|         *character = new_character; |         *character = new_character; | ||||||
|         Ok((transaction, ())) |         Ok((transaction, pkts.into_iter().flatten().collect())) | ||||||
|     }).await |     }).await | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -297,10 +305,10 @@ pub async fn feed_mag<'a, EG> ( | |||||||
|     tool_item_id: &ClientItemId, |     tool_item_id: &ClientItemId, | ||||||
| ) -> Result<(), ItemStateError> | ) -> Result<(), ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), _) = ItemStateAction::default() | ||||||
|             .act(actions::take_item_from_inventory(character.id, *tool_item_id, 1)) |             .act(actions::take_item_from_inventory(character.id, *tool_item_id, 1)) | ||||||
|             .act(actions::feed_mag_item(character.clone(), *mag_item_id)) |             .act(actions::feed_mag_item(character.clone(), *mag_item_id)) | ||||||
| @ -321,11 +329,11 @@ pub async fn buy_shop_item<'a, EG> ( | |||||||
|     amount: u32, |     amount: u32, | ||||||
| ) -> Result<InventoryItem, ItemStateError> | ) -> Result<InventoryItem, ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     let item_price = shop_item.price() as u32 * amount; |     let item_price = shop_item.price() as u32 * amount; | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::take_meseta_from_inventory(character.id, item_price)) |             .act(actions::take_meseta_from_inventory(character.id, item_price)) | ||||||
|             //.act(bought_item_to_inventory_item)
 |             //.act(bought_item_to_inventory_item)
 | ||||||
| @ -347,10 +355,10 @@ pub async fn sell_item<'a, EG> ( | |||||||
|     amount: u32, |     amount: u32, | ||||||
| ) -> Result<InventoryItem, ItemStateError> | ) -> Result<InventoryItem, ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), result) = ItemStateAction::default() | ||||||
|             .act(actions::take_item_from_inventory(character.id, item_id, amount)) |             .act(actions::take_item_from_inventory(character.id, item_id, amount)) | ||||||
|             .act(actions::sell_inventory_item(character.id)) |             .act(actions::sell_inventory_item(character.id)) | ||||||
| @ -367,7 +375,7 @@ pub async fn trade_items<'a, EG> ( | |||||||
|     p2: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta)) |     p2: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta)) | ||||||
|     -> Result<(Vec<InventoryItem>, Vec<InventoryItem>), ItemStateError> |     -> Result<(Vec<InventoryItem>, Vec<InventoryItem>), ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     let p1_trade_items = p1.2 |     let p1_trade_items = p1.2 | ||||||
|         .iter() |         .iter() | ||||||
| @ -391,7 +399,7 @@ where | |||||||
|         let p1_id = p1.1.id; |         let p1_id = p1.1.id; | ||||||
|         let p2_id = p2.1.id; |         let p2_id = p2.1.id; | ||||||
|         let trade = transaction.gateway().create_trade(&p1_id, &p2_id).await?; |         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() |         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::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())) |             .act(actions::foreach(actions::assign_new_item_id())) | ||||||
| @ -437,10 +445,10 @@ pub async fn take_meseta<'a, EG> ( | |||||||
|     meseta: Meseta) |     meseta: Meseta) | ||||||
|     -> Result<(), ItemStateError> |     -> Result<(), ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), _) = ItemStateAction::default() | ||||||
|             .act(actions::take_meseta_from_inventory(*character_id, meseta.0)) |             .act(actions::take_meseta_from_inventory(*character_id, meseta.0)) | ||||||
|             .commit((item_state_proxy, transaction)) |             .commit((item_state_proxy, transaction)) | ||||||
| @ -458,10 +466,10 @@ pub async fn enemy_drops_item<'a, EG> ( | |||||||
|     item_drop: ItemDrop) |     item_drop: ItemDrop) | ||||||
|     -> Result<FloorItem, ItemStateError> |     -> Result<FloorItem, ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), floor_item) = ItemStateAction::default() | ||||||
|             .act(actions::convert_item_drop_to_floor_item(character_id, item_drop)) |             .act(actions::convert_item_drop_to_floor_item(character_id, item_drop)) | ||||||
|             .act(actions::add_item_to_local_floor(character_id)) |             .act(actions::add_item_to_local_floor(character_id)) | ||||||
| @ -482,10 +490,10 @@ pub async fn apply_modifier<'a, EG> ( | |||||||
|     modifier: ItemModifier) |     modifier: ItemModifier) | ||||||
|     -> Result<IndividualItemDetail, ItemStateError> |     -> Result<IndividualItemDetail, ItemStateError> | ||||||
| where | where | ||||||
|     EG: EntityGateway, |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     entity_gateway.with_transaction(|transaction| async move { |     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() |         let ((item_state_proxy, transaction), item) = ItemStateAction::default() | ||||||
|             .act(actions::take_item_from_inventory(character.id, item_id, 1)) |             .act(actions::take_item_from_inventory(character.id, item_id, 1)) | ||||||
|             .act(actions::apply_modifier_to_inventory_item(modifier)) |             .act(actions::apply_modifier_to_inventory_item(modifier)) | ||||||
|  | |||||||
| @ -69,9 +69,10 @@ pub enum ClientRemovalError { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Error, Debug, PartialEq, Eq)] | #[derive(Error, Debug, PartialEq, Eq)] | ||||||
| #[error("get clients")] |  | ||||||
| pub enum GetClientsError { | pub enum GetClientsError { | ||||||
|  |     #[error("invalid client")] | ||||||
|     InvalidClient, |     InvalidClient, | ||||||
|  |     #[error("invalid area")] | ||||||
|     InvalidArea, |     InvalidArea, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -147,7 +148,7 @@ pub enum RoomLobby { | |||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| pub struct ClientLocation { | pub struct ClientLocation { | ||||||
|     lobbies: [Arc<RwLock<Lobby>>; 15], |     lobbies: [Arc<RwLock<Lobby>>; 15], | ||||||
|     rooms: [Option<Arc<RwLock<Room>>>; MAX_ROOMS], |     rooms: [Arc<RwLock<Option<Room>>>; MAX_ROOMS], | ||||||
|     client_location: Arc<RwLock<HashMap<ClientId, RoomLobby>>>, |     client_location: Arc<RwLock<HashMap<ClientId, RoomLobby>>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -155,7 +156,7 @@ impl Default for ClientLocation { | |||||||
|     fn default() -> ClientLocation { |     fn default() -> ClientLocation { | ||||||
|         ClientLocation { |         ClientLocation { | ||||||
|             lobbies: core::array::from_fn(|_| Arc::new(RwLock::new(Lobby([None; 12])))), |             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())), |             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<RoomId, CreateRoomError> { |     pub async fn create_new_room(&mut self, id: ClientId) -> Result<RoomId, CreateRoomError> { | ||||||
|         let (index, empty_slot) = self.rooms.iter_mut() |         let (index, empty_slot) = Box::pin(stream::iter(self.rooms.iter()) | ||||||
|             .enumerate() |             .enumerate() | ||||||
|             .find(|(_, r)| r.is_none()) |             .filter(|(_, r)| async {r.read().await.is_none()})) | ||||||
|  |             .next() | ||||||
|  |             .await | ||||||
|             .ok_or(CreateRoomError::NoOpenSlots)?; |             .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)) |         self.add_client_to_room(id, RoomId(index)) | ||||||
|             .await |             .await | ||||||
|             .map_err(|_err| CreateRoomError::JoinError)?; |             .map_err(|_err| CreateRoomError::JoinError)?; | ||||||
| 
 |  | ||||||
|         Ok(RoomId(index)) |         Ok(RoomId(index)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -226,9 +228,10 @@ impl ClientLocation { | |||||||
|         let mut r = self.rooms.get(room.0) |         let mut r = self.rooms.get(room.0) | ||||||
|             .ok_or(JoinRoomError::RoomDoesNotExist)? |             .ok_or(JoinRoomError::RoomDoesNotExist)? | ||||||
|             .as_ref() |             .as_ref() | ||||||
|             .ok_or(JoinRoomError::RoomDoesNotExist)? |  | ||||||
|             .write() |             .write() | ||||||
|             .await; |             .await; | ||||||
|  |         let r = r.as_mut() | ||||||
|  |             .ok_or(JoinRoomError::RoomDoesNotExist)?; | ||||||
|         let (index, empty_slot) = r.0.iter_mut() |         let (index, empty_slot) = r.0.iter_mut() | ||||||
|             .enumerate() |             .enumerate() | ||||||
|             .find(|(_, k)| k.is_none()) |             .find(|(_, k)| k.is_none()) | ||||||
| @ -293,9 +296,9 @@ impl ClientLocation { | |||||||
|     pub async fn get_room_leader(&self, room: RoomId) -> Result<AreaClient, GetLeaderError> { |     pub async fn get_room_leader(&self, room: RoomId) -> Result<AreaClient, GetLeaderError> { | ||||||
|         let r = self.rooms[room.0] |         let r = self.rooms[room.0] | ||||||
|             .as_ref() |             .as_ref() | ||||||
|             .ok_or(GetLeaderError::InvalidArea)? |  | ||||||
|             .read() |             .read() | ||||||
|             .await; |             .await | ||||||
|  |             .ok_or(GetLeaderError::InvalidArea)?; | ||||||
|         let mut r = r |         let mut r = r | ||||||
|             .0 |             .0 | ||||||
|             .iter() |             .iter() | ||||||
| @ -368,9 +371,9 @@ impl ClientLocation { | |||||||
|         Ok(self.rooms.get(room.0) |         Ok(self.rooms.get(room.0) | ||||||
|            .ok_or(GetClientsError::InvalidArea)? |            .ok_or(GetClientsError::InvalidArea)? | ||||||
|            .as_ref() |            .as_ref() | ||||||
|            .ok_or(GetClientsError::InvalidArea)? |  | ||||||
|            .read() |            .read() | ||||||
|            .await |            .await | ||||||
|  |            .ok_or(GetClientsError::InvalidArea)? | ||||||
|            .0 |            .0 | ||||||
|            .iter() |            .iter() | ||||||
|            .filter_map(|client| { |            .filter_map(|client| { | ||||||
| @ -456,9 +459,13 @@ impl ClientLocation { | |||||||
|             .ok_or(ClientRemovalError::ClientNotInArea)?; |             .ok_or(ClientRemovalError::ClientNotInArea)?; | ||||||
|         match area { |         match area { | ||||||
|             RoomLobby::Room(room) => { |             RoomLobby::Room(room) => { | ||||||
|                 let r = self.rooms.get(room.0).ok_or(ClientRemovalError::InvalidArea)?; |                 let mut r = self.rooms.get(room.0) | ||||||
|                 if let Some(r) = r { |                     .ok_or(ClientRemovalError::InvalidArea)? | ||||||
|                     remove_client(id, &mut r.write().await.0) |                     .as_ref() | ||||||
|  |                     .write() | ||||||
|  |                     .await; | ||||||
|  |                 if let Some(r) = r.as_mut() { | ||||||
|  |                     remove_client(id, &mut r.0) | ||||||
|                 } |                 } | ||||||
|                 else { |                 else { | ||||||
|                     return Err(ClientRemovalError::InvalidArea) |                     return Err(ClientRemovalError::InvalidArea) | ||||||
|  | |||||||
| @ -84,7 +84,7 @@ pub async fn request_item<EG>(id: ClientId, | |||||||
|                               item_state: &mut ItemState) |                               item_state: &mut ItemState) | ||||||
|                               -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> |                               -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> | ||||||
| where | where | ||||||
|     EG: EntityGateway |     EG: EntityGateway + 'static, | ||||||
| { | { | ||||||
|     let room_id = client_location.get_room(id).await?; |     let room_id = client_location.get_room(id).await?; | ||||||
|     let monster = rooms.with(room_id, |room| Box::pin(async move { |     let monster = rooms.with(room_id, |room| Box::pin(async move { | ||||||
|  | |||||||
| @ -330,20 +330,32 @@ where | |||||||
| pub async fn player_uses_item<EG>(id: ClientId, | pub async fn player_uses_item<EG>(id: ClientId, | ||||||
|                                   player_use_tool: PlayerUseItem, |                                   player_use_tool: PlayerUseItem, | ||||||
|                                   entity_gateway: &mut EG, |                                   entity_gateway: &mut EG, | ||||||
|                                   _client_location: &ClientLocation, |                                   client_location: &ClientLocation, | ||||||
|                                   clients: &Clients, |                                   clients: &Clients, | ||||||
|                                   item_state: &mut ItemState) |                                   item_state: &mut ItemState) | ||||||
|                                   -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> |                                   -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> | ||||||
| where | where | ||||||
|     EG: EntityGateway + Clone + 'static, |     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 entity_gateway = entity_gateway.clone(); | ||||||
|         let mut item_state = item_state.clone(); |         let mut item_state = item_state.clone(); | ||||||
|         Box::pin(async move { |         Box::pin(async move { | ||||||
|             use_item(&mut item_state, &mut entity_gateway, &mut client.character, &ClientItemId(player_use_tool.item_id), 1).await |             use_item(&mut item_state, &mut entity_gateway, &mut client.character, area_client, &ClientItemId(player_use_tool.item_id), 1).await | ||||||
|         })}).await??; |         })}).await?? | ||||||
|     Ok(Vec::new()) |        .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::<Vec<_>>() | ||||||
|  |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn player_used_medical_center<EG>(id: ClientId, | pub async fn player_used_medical_center<EG>(id: ClientId, | ||||||
|  | |||||||
| @ -104,7 +104,7 @@ pub enum ShipError { | |||||||
|     InvalidSlot(ClientId, u32), |     InvalidSlot(ClientId, u32), | ||||||
|     #[error("too many clients")] |     #[error("too many clients")] | ||||||
|     TooManyClients, |     TooManyClients, | ||||||
|     #[error("client error location {0}")] |     #[error("client location {0}")] | ||||||
|     ClientLocationError(ClientLocationError), |     ClientLocationError(ClientLocationError), | ||||||
|     #[error("maps error {0}")] |     #[error("maps error {0}")] | ||||||
|     MapsError(#[from] MapsError), |     MapsError(#[from] MapsError), | ||||||
|  | |||||||
| @ -251,6 +251,59 @@ async fn test_use_materials() { | |||||||
|     assert!(char.materials.luck == 2); |     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
 | // TODO: tests for ALL ITEMS WOW
 | ||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
|  | |||||||
| @ -1792,7 +1792,7 @@ async fn test_trade_multiple_individual() { | |||||||
|         msg: GameMessage::CreateItem(CreateItem { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 1, |             client: 1, | ||||||
|             item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
 |             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 { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 1, |             client: 1, | ||||||
|             item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
 |             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 { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 1, |             client: 1, | ||||||
|             item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
 |             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 { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 1, |             client: 1, | ||||||
|             item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
 |             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 { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 0, |             client: 0, | ||||||
|             item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
 |             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 { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 0, |             client: 0, | ||||||
|             item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
 |             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 { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 0, |             client: 0, | ||||||
|             item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
 |             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 { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 0, |             client: 0, | ||||||
|             item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
 |             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 { |     assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { | ||||||
|         msg: GameMessage::CreateItem(CreateItem { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 1, |             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 { |     assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { | ||||||
|         msg: GameMessage::CreateItem(CreateItem { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 1, |             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 { |     assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message { | ||||||
|         msg: GameMessage::CreateItem(CreateItem { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 1, |             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 { |     assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message { | ||||||
|         msg: GameMessage::CreateItem(CreateItem { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 1, |             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 { |     assert!(matches!(ack[8], (ClientId(1), SendShipPacket::Message(Message { | ||||||
|         msg: GameMessage::CreateItem(CreateItem { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 0, |             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 { |     assert!(matches!(ack[9], (ClientId(2), SendShipPacket::Message(Message { | ||||||
|         msg: GameMessage::CreateItem(CreateItem { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 0, |             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 { |     assert!(matches!(ack[10], (ClientId(1), SendShipPacket::Message(Message { | ||||||
|         msg: GameMessage::CreateItem(CreateItem { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 0, |             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 { |     assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message { | ||||||
|         msg: GameMessage::CreateItem(CreateItem { |         msg: GameMessage::CreateItem(CreateItem { | ||||||
|             client: 0, |             client: 0, | ||||||
|             item_id: 0x810002, |             item_id: 0x810001, | ||||||
|             .. |             .. | ||||||
|         }), |         }), | ||||||
|         .. |         .. | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user