use std::collections::BTreeMap; use std::convert::TryInto; use futures::Future; use crate::entity::account::*; use crate::entity::character::*; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError}; use crate::entity::item::*; use std::sync::{Arc, Mutex}; // TODO: implement multiple banks pub struct InMemoryGatewayTransaction<'a> { working_gateway: InMemoryGateway, original_gateway: &'a mut InMemoryGateway, } #[async_trait::async_trait] impl<'a> EntityGatewayTransaction for InMemoryGatewayTransaction<'a> { fn gateway(&mut self) -> &mut dyn EntityGateway { &mut self.working_gateway } async fn commit(mut self: Box) -> Result<(), GatewayError> { self.original_gateway.users.lock().unwrap().clear(); self.original_gateway.users.lock().unwrap().extend(self.working_gateway.users.lock().unwrap().clone()); self.original_gateway.user_settings.lock().unwrap().clear(); self.original_gateway.user_settings.lock().unwrap().extend(self.working_gateway.user_settings.lock().unwrap().clone()); self.original_gateway.characters.lock().unwrap().clear(); self.original_gateway.characters.lock().unwrap().extend(self.working_gateway.characters.lock().unwrap().clone()); self.original_gateway.character_meseta.lock().unwrap().clear(); self.original_gateway.character_meseta.lock().unwrap().extend(self.working_gateway.character_meseta.lock().unwrap().clone()); self.original_gateway.bank_meseta.lock().unwrap().clear(); self.original_gateway.bank_meseta.lock().unwrap().extend(self.working_gateway.bank_meseta.lock().unwrap().clone()); self.original_gateway.items.lock().unwrap().clear(); self.original_gateway.items.lock().unwrap().extend(self.working_gateway.items.lock().unwrap().clone()); self.original_gateway.inventories.lock().unwrap().clear(); self.original_gateway.inventories.lock().unwrap().extend(self.working_gateway.inventories.lock().unwrap().clone()); self.original_gateway.banks.lock().unwrap().clear(); self.original_gateway.banks.lock().unwrap().extend(self.working_gateway.banks.lock().unwrap().clone()); self.original_gateway.equips.lock().unwrap().clear(); self.original_gateway.equips.lock().unwrap().extend(self.working_gateway.equips.lock().unwrap().clone()); self.original_gateway.mag_modifiers.lock().unwrap().clear(); self.original_gateway.mag_modifiers.lock().unwrap().extend(self.working_gateway.mag_modifiers.lock().unwrap().clone()); self.original_gateway.weapon_modifiers.lock().unwrap().clear(); self.original_gateway.weapon_modifiers.lock().unwrap().extend(self.working_gateway.weapon_modifiers.lock().unwrap().clone()); self.original_gateway.trades.lock().unwrap().clear(); self.original_gateway.trades.lock().unwrap().extend(self.working_gateway.trades.lock().unwrap().clone()); Ok(()) } } #[derive(Clone)] pub struct InMemoryGateway { users: Arc>>, user_settings: Arc>>, characters: Arc>>, character_meseta: Arc>>, bank_meseta: Arc>>, items: Arc>>, inventories: Arc>>, banks: Arc>>, equips: Arc>>, mag_modifiers: Arc>>>, weapon_modifiers: Arc>>>, trades: Arc>>, } impl Default for InMemoryGateway { fn default() -> InMemoryGateway { InMemoryGateway { users: Arc::new(Mutex::new(BTreeMap::new())), user_settings: Arc::new(Mutex::new(BTreeMap::new())), characters: Arc::new(Mutex::new(BTreeMap::new())), character_meseta: Arc::new(Mutex::new(BTreeMap::new())), bank_meseta: Arc::new(Mutex::new(BTreeMap::new())), items: Arc::new(Mutex::new(BTreeMap::new())), inventories: Arc::new(Mutex::new(BTreeMap::new())), banks: Arc::new(Mutex::new(BTreeMap::new())), equips: Arc::new(Mutex::new(BTreeMap::new())), mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())), weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())), trades: Arc::new(Mutex::new(Vec::new())), } } } impl InMemoryGateway { fn apply_modifiers(&self, inventory: InventoryEntity ) -> InventoryEntity { let items = self.items.lock().unwrap(); let inventory_items = inventory.items.into_iter() .map(|item| { item.map_individual(|mut item| { item.item = match item.item { ItemDetail::Weapon(mut weapon) => { if let Some(weapon_modifiers) = self.weapon_modifiers.lock().unwrap().get(&item.id) { for weapon_modifier in weapon_modifiers.iter() { weapon.apply_modifier(weapon_modifier); } } ItemDetail::Weapon(weapon) }, ItemDetail::Mag(mag) => { let mut mag = mag::Mag::baby_mag(mag.color as u16); if let Some(mag_modifiers) = self.mag_modifiers.lock().unwrap().get(&item.id) { for mag_modifier in mag_modifiers.iter() { match mag_modifier { mag::MagModifier::FeedMag {food} => { if let Some(mag_feed) = items.get(food) { if let ItemDetail::Tool(mag_feed) = mag_feed.item { mag.feed(mag_feed.tool) } } }, mag::MagModifier::OwnerChange(class, section_id) => { mag.change_owner(*class, *section_id) }, mag::MagModifier::MagCell(mag_cell_id) => { if let Some(mag_cell) = items.get(mag_cell_id) { if let ItemDetail::Tool(mag_cell) = mag_cell.item { mag.apply_mag_cell(mag_cell.tool.try_into().unwrap()).unwrap() } } }, _ => {} } } } ItemDetail::Mag(mag) } _ => { item.item } }; item }) }) .collect(); InventoryEntity::new(inventory_items) } } #[async_trait::async_trait] impl EntityGateway for InMemoryGateway { async fn transaction<'a>(&'a mut self) -> Result, GatewayError> { let working_gateway = { let users = self.users.lock().unwrap().clone(); let user_settings = self.user_settings.lock().unwrap().clone(); let characters = self.characters.lock().unwrap().clone(); let character_meseta = self.character_meseta.lock().unwrap().clone(); let bank_meseta = self.bank_meseta.lock().unwrap().clone(); let items = self.items.lock().unwrap().clone(); let inventories = self.inventories.lock().unwrap().clone(); let banks = self.banks.lock().unwrap().clone(); let equips = self.equips.lock().unwrap().clone(); let mag_modifiers = self.mag_modifiers.lock().unwrap().clone(); let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone(); let trades = self.trades.lock().unwrap().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 where Fut: Future, R), E>> + Send + 'a, F: FnOnce(Box) -> Fut + Send, R: Send, E: From, { let users = self.users.lock().unwrap().clone(); let user_settings = self.user_settings.lock().unwrap().clone(); let characters = self.characters.lock().unwrap().clone(); let character_meseta = self.character_meseta.lock().unwrap().clone(); let bank_meseta = self.bank_meseta.lock().unwrap().clone(); let items = self.items.lock().unwrap().clone(); let inventories = self.inventories.lock().unwrap().clone(); let banks = self.banks.lock().unwrap().clone(); let equips = self.equips.lock().unwrap().clone(); let mag_modifiers = self.mag_modifiers.lock().unwrap().clone(); let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone(); let trades = self.trades.lock().unwrap().clone(); let working_gateway = 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)), }; let transaction = Box::new(InMemoryGatewayTransaction { working_gateway, original_gateway: self, }); let (transaction, result) = func(transaction).await?; transaction.commit().await?; Ok(result) } async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { let mut users = self.users.lock().unwrap(); let id = users .iter() .fold(0, |sum, (i, _)| std::cmp::max(sum, i.0)) + 1; let user = UserAccountEntity { id: UserAccountId(id), username: user.username, password: user.password, guildcard: user.guildcard, team_id: user.team_id, banned_until: user.banned_until, muted_until: user.muted_until, created_at: chrono::Utc::now(), flags: user.flags, activated: user.activated, at_login: false, at_character: false, at_ship: false, }; users.insert(user.id, user.clone()); Ok(user) } async fn get_user_by_id(&mut self, id: UserAccountId) -> Result { let users = self.users.lock().unwrap(); users.get(&id).cloned().ok_or(GatewayError::Error) } async fn get_user_by_name(&mut self, username: String) -> Result { let users = self.users.lock().unwrap(); users .iter() .find(|(_, k)| k.username == username) .map(|(_, k)| k.clone()) .ok_or(GatewayError::Error) } async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> { let mut users = self.users.lock().unwrap(); users.insert(user.id, user.clone()); Ok(()) } async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result { let mut user_settings = self.user_settings.lock().unwrap(); let id = user_settings .iter() .fold(0, |sum, (i, _)| std::cmp::max(sum, i.0)) + 1; let new_settings = UserSettingsEntity { id: UserSettingsId(id), user_id: settings.user_id, settings: settings.settings, }; user_settings.insert(new_settings.id, new_settings.clone()); Ok(new_settings) } async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result { let user_settings = self.user_settings.lock().unwrap(); user_settings .iter() .find(|(_, k)| k.user_id == user.id) .map(|(_, k)| k.clone()) .ok_or(GatewayError::Error) } async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { let characters = self.characters.lock().unwrap(); const NONE: Option = None; let mut chars = [NONE; 4]; characters .iter() .filter(|(_, c)| c.user_id == user.id) .for_each(|(_, c)| chars[c.slot as usize] = Some(c.clone())); Ok(chars) } async fn create_character(&mut self, character: NewCharacterEntity) -> Result { let mut characters = self.characters.lock().unwrap(); let id = characters .iter() .fold(0, |sum, (i, _)| std::cmp::max(sum, i.0)) + 1; let new_character = CharacterEntity { id: CharacterEntityId(id), user_id: character.user_id, slot: character.slot, name: character.name, exp: character.exp, char_class: character.char_class, section_id: character.section_id, appearance: character.appearance, techs: character.techs, config: character.config, info_board: character.info_board, guildcard: character.guildcard, materials: character.materials, tech_menu: character.tech_menu, option_flags: character.option_flags, keyboard_config: character.keyboard_config, gamepad_config: character.gamepad_config, }; characters.insert(new_character.id, new_character.clone()); Ok(new_character) } async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> { let mut characters = self.characters.lock().unwrap(); characters.insert(char.id, char.clone()); Ok(()) } async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result { Ok(GuildCardDataEntity::new(user.id)) } async fn create_item(&mut self, item: NewItemEntity) -> Result { let mut items = self.items.lock().unwrap(); let id = items .iter() .fold(0, |sum, (i, _)| std::cmp::max(sum, i.0)) + 1; let new_item = ItemEntity { id: ItemEntityId(id), item: item.item, }; items.insert(ItemEntityId(id), new_item.clone()); Ok(new_item) } async fn add_item_note(&mut self, _item_id: &ItemEntityId, _item_note: ItemNote) -> Result<(), GatewayError> { Ok(()) } async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> { self.mag_modifiers.lock().unwrap() .entry(*mag_item_id) .or_insert_with(Vec::new) .push(mag::MagModifier::FeedMag { food: *tool_item_id }); Ok(()) } async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> { self.mag_modifiers.lock().unwrap() .entry(*mag_item_id) .or_insert_with(Vec::new) .push(mag::MagModifier::OwnerChange(character.char_class, character.section_id)); Ok(()) } async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> { self.mag_modifiers.lock().unwrap() .entry(*mag_item_id) .or_insert_with(Vec::new) .push(mag::MagModifier::MagCell(*mag_cell_id)); Ok(()) } async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: weapon::WeaponModifier) -> Result<(), GatewayError> { self.weapon_modifiers.lock().unwrap() .entry(*item_id) .or_insert_with(Vec::new) .push(modifier); Ok(()) } async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result { let inventories = self.inventories.lock().unwrap(); Ok(inventories .iter() .find(|(id, _)| **id == *char_id) .map(|(_, inv)| inv.clone()) .map(|inv| self.apply_modifiers(inv)) .unwrap_or_default()) } async fn get_character_bank(&mut self, char_id: &CharacterEntityId, _bank_name: &BankName) -> Result { let banks = self.banks.lock().unwrap(); Ok(banks .iter() .find(|(id, _)| **id == *char_id) .map(|(_, b)| b.clone()) .unwrap_or_default()) } async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> { let mut inventories = self.inventories.lock().unwrap(); inventories.insert(*char_id, inventory.clone()); Ok(()) } // TOOD: impl bank name async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, _bank_name: &BankName) -> Result<(), GatewayError> { let mut banks = self.banks.lock().unwrap(); banks.insert(*char_id, bank.clone()); Ok(()) } async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result { let equips = self.equips.lock().unwrap(); Ok(equips .iter() .find(|(id, _)| **id == *char_id) .map(|(_, inv)| inv.clone()) .unwrap_or_default()) } async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equipped: &EquippedEntity) -> Result<(), GatewayError> { let mut equips = self.equips.lock().unwrap(); equips.insert(*char_id, equipped.clone()); Ok(()) } async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> { let mut character_meseta = self.character_meseta.lock().unwrap(); character_meseta.insert(*char_id, meseta); Ok(()) } async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result { let mut character_meseta = self.character_meseta.lock().unwrap(); if let Some(meseta) = character_meseta.get_mut(char_id) { Ok(*meseta) } else { Err(GatewayError::Error) } } async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> { let mut bank_meseta = self.bank_meseta.lock().unwrap(); bank_meseta.insert((*char_id, bank.clone()), meseta); Ok(()) } async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName) -> Result { let mut bank_meseta = self.bank_meseta.lock().unwrap(); if let Some(meseta) = bank_meseta.get_mut(&(*char_id, bank.clone())) { Ok(*meseta) } else { Err(GatewayError::Error) } } async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result { let mut trades = self.trades.lock().unwrap(); let id = trades.len() as u32; let new_trade = TradeEntity { id: TradeId(id), character1: *char_id1, character2: *char_id2, }; trades.push(new_trade.clone()); Ok(new_trade) } }