use std::collections::BTreeMap; use std::convert::TryInto; use futures::future::{Future, BoxFuture}; use crate::entity::account::*; use crate::entity::character::*; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError}; use crate::entity::item::*; use crate::entity::room::*; use async_std::sync::{Arc, Mutex}; #[derive(Clone)] pub struct InMemoryGatewayTransaction { working_gateway: InMemoryGateway, original_gateway: InMemoryGateway, } fn copy_if_needed(working_table: &mut BTreeMap, original_table: &BTreeMap, key: K) -> Option<()> where K: Ord + Copy, V: Clone, { if let std::collections::btree_map::Entry::Vacant(e) = working_table.entry(key) { e.insert(original_table.get(&key)?.clone()); } Some(()) } // functions here have been skipped as they are not used in transactions, add as needed #[async_trait::async_trait] impl EntityGateway for InMemoryGatewayTransaction { type Transaction<'t> = InMemoryGatewayTransaction where Self: 't; async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { self.working_gateway.create_user(user).await } async fn get_user_by_id(&mut self, id: UserAccountId) -> Result { match self.working_gateway.get_user_by_id(id).await { Ok(user) => Ok(user), Err(_) => { self.original_gateway.get_user_by_id(id).await } } } async fn get_user_by_name(&mut self, username: String) -> Result { match self.working_gateway.get_user_by_name(username.clone()).await { Ok(user) => Ok(user), Err(_) => { self.original_gateway.get_user_by_name(username).await } } } async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> { self.working_gateway.save_user(user).await } async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result { self.working_gateway.create_user_settings(settings).await } async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result { match self.working_gateway.get_user_settings_by_user(user).await { Ok(user) => Ok(user), Err(_) => { self.original_gateway.get_user_settings_by_user(user).await } } } async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> { self.original_gateway.save_user_settings(settings).await } async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> { copy_if_needed(&mut *self.working_gateway.characters.lock().await, &*self.original_gateway.characters.lock().await, char.id ); self.working_gateway.save_character(char).await } async fn create_item(&mut self, item: NewItemEntity) -> Result { self.working_gateway.create_item(item).await } async fn add_item_note(&mut self, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> { self.working_gateway.add_item_note(item_id, item_note).await } async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> { self.working_gateway.feed_mag(mag_item_id, tool_item_id).await } async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> { self.working_gateway.change_mag_owner(mag_item_id, character).await } async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> { self.working_gateway.use_mag_cell(mag_item_id, mag_cell_id).await } async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: &weapon::WeaponModifier) -> Result<(), GatewayError> { self.working_gateway.add_weapon_modifier(item_id, modifier).await } async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result { match self.working_gateway.get_character_inventory(char_id).await { Ok(inventory) => Ok(inventory), Err(_) => { self.original_gateway.get_character_inventory(char_id).await } } } async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result { match self.working_gateway.get_character_bank(char_id, bank_identifier).await { Ok(bank) => Ok(bank), Err(_) => { self.original_gateway.get_character_bank(char_id, bank_identifier).await } } } async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> { self.working_gateway.set_character_inventory(char_id, inventory).await } async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_identifier: &BankIdentifier) -> Result<(), GatewayError> { self.working_gateway.set_character_bank(char_id, bank, bank_identifier).await } async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result { match self.working_gateway.get_character_equips(char_id).await { Ok(equips) => Ok(equips), Err(_) => { self.original_gateway.get_character_equips(char_id).await } } } async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equipped: &EquippedEntity) -> Result<(), GatewayError> { self.working_gateway.set_character_equips(char_id, equipped).await } async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> { self.working_gateway.set_character_meseta(char_id, meseta).await } async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result { match self.working_gateway.get_character_meseta(char_id).await { Ok(meseta) => Ok(meseta), Err(_) => { self.original_gateway.get_character_meseta(char_id).await } } } async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier, meseta: Meseta) -> Result<(), GatewayError> { self.working_gateway.set_bank_meseta(char_id, bank_identifier, meseta).await } async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result { match self.working_gateway.get_bank_meseta(char_id, bank_identifier).await { Ok(meseta) => Ok(meseta), Err(_) => { self.original_gateway.get_bank_meseta(char_id, bank_identifier).await } } } async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result { self.working_gateway.create_trade(char_id1, char_id2).await } async fn set_character_playtime(&mut self, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError> { copy_if_needed(&mut *self.working_gateway.characters.lock().await, &*self.original_gateway.characters.lock().await, *char_id ); self.working_gateway.set_character_playtime(char_id, playtime).await } } #[async_trait::async_trait] impl EntityGatewayTransaction for InMemoryGatewayTransaction { type ParentGateway = InMemoryGatewayTransaction; fn gateway(&mut self) -> &mut Self::ParentGateway { self } async fn commit(mut self) -> Result<(), GatewayError> { self.original_gateway.users.lock().await.extend(self.working_gateway.users.lock().await.clone()); self.original_gateway.user_settings.lock().await.extend(self.working_gateway.user_settings.lock().await.clone()); self.original_gateway.characters.lock().await.extend(self.working_gateway.characters.lock().await.clone()); self.original_gateway.character_meseta.lock().await.extend(self.working_gateway.character_meseta.lock().await.clone()); self.original_gateway.bank_meseta.lock().await.extend(self.working_gateway.bank_meseta.lock().await.clone()); self.original_gateway.shared_bank_meseta.lock().await.extend(self.working_gateway.shared_bank_meseta.lock().await.clone()); self.original_gateway.items.lock().await.extend(self.working_gateway.items.lock().await.clone()); self.original_gateway.inventories.lock().await.extend(self.working_gateway.inventories.lock().await.clone()); self.original_gateway.character_banks.lock().await.extend(self.working_gateway.character_banks.lock().await.clone()); self.original_gateway.shared_banks.lock().await.extend(self.working_gateway.shared_banks.lock().await.clone()); self.original_gateway.equips.lock().await.extend(self.working_gateway.equips.lock().await.clone()); self.original_gateway.mag_modifiers.lock().await.extend(self.working_gateway.mag_modifiers.lock().await.clone()); self.original_gateway.weapon_modifiers.lock().await.extend(self.working_gateway.weapon_modifiers.lock().await.clone()); self.original_gateway.trades.lock().await.extend(self.working_gateway.trades.lock().await.clone()); Ok(()) } } #[derive(Clone)] enum InventoryItemElement { Individual(ItemEntityId), Stacked(Vec), } #[derive(Clone)] pub struct InMemoryGateway { users: Arc>>, user_settings: Arc>>, characters: Arc>>, character_meseta: Arc>>, bank_meseta: Arc>>, shared_bank_meseta: Arc>>, items: Arc>>, inventories: Arc>>>, character_banks: Arc>>, shared_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())), shared_bank_meseta: Arc::new(Mutex::new(BTreeMap::new())), items: Arc::new(Mutex::new(BTreeMap::new())), inventories: Arc::new(Mutex::new(BTreeMap::new())), character_banks: Arc::new(Mutex::new(BTreeMap::new())), shared_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())), } } } fn apply_modifiers(items: &BTreeMap, weapon_modifiers: &BTreeMap>, mag_modifiers: &BTreeMap>, inventory: InventoryEntity ) -> InventoryEntity { 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) = weapon_modifiers.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) = mag_modifiers.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 { type Transaction<'t> = InMemoryGatewayTransaction where Self: 't; fn with_transaction<'a, F, Fut, R>(&'a mut self, func: F) -> BoxFuture<'a, Result> where Fut: Future, R), anyhow::Error>> + Send + 'a, F: FnOnce(Self::Transaction<'a>) -> Fut + Send + 'a, R: Send, { Box::pin(async move { 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 shared_bank_meseta = self.shared_bank_meseta.lock().await.clone(); let items = self.items.lock().await.clone(); let inventories = self.inventories.lock().await.clone(); let character_banks = self.character_banks.lock().await.clone(); let shared_banks = self.shared_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(); 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)), shared_bank_meseta: Arc::new(Mutex::new(shared_bank_meseta)), items: Arc::new(Mutex::new(items)), inventories: Arc::new(Mutex::new(inventories)), character_banks: Arc::new(Mutex::new(character_banks)), shared_banks: Arc::new(Mutex::new(shared_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 = InMemoryGatewayTransaction { working_gateway, original_gateway: self.clone(), }; 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().await; 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().await; users.get(&id).cloned().ok_or(GatewayError::Error) } async fn get_user_by_name(&mut self, username: String) -> Result { let users = self.users.lock().await; 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().await; 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().await; 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().await; user_settings .iter() .find(|(_, k)| k.user_id == user.id) .map(|(_, k)| k.clone()) .ok_or(GatewayError::Error) } async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> { let mut user_settings = self.user_settings.lock().await; user_settings.insert(settings.id, settings.clone()); Ok(()) } async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { let characters = self.characters.lock().await; 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().await; 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, playtime: 0, }; 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().await; 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().await; 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().await .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().await .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().await .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().await .entry(*item_id) .or_insert_with(Vec::new) .push(modifier.clone()); Ok(()) } async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result { let inventories = self.inventories.lock().await; let items = self.items.lock().await; let weapon_modifiers = self.weapon_modifiers.lock().await; let mag_modifiers = self.mag_modifiers.lock().await; Ok(inventories .iter() .find(|(id, _)| **id == *char_id) .map(|(_, inv)| { InventoryEntity { items: inv .iter() .map(|inv_item_id| { match inv_item_id { InventoryItemElement::Individual(individual_id) => { InventoryItemEntity::Individual(items.get(individual_id).unwrap().clone()) }, InventoryItemElement::Stacked(stacked_ids) => { InventoryItemEntity::Stacked( stacked_ids.iter() .map(|stacked_id| { items.get(stacked_id).unwrap().clone() }) .collect() ) } } }) .collect() } }) .map(|inv| apply_modifiers(&items, &weapon_modifiers, &mag_modifiers, inv)) .unwrap_or_default()) } async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result { match bank_identifier { BankIdentifier::Character => { let character_banks = self.character_banks.lock().await; Ok(character_banks .iter() .find(|(id, _)| **id == *char_id) .map(|(_, b)| b.clone()) .unwrap_or_default()) }, BankIdentifier::Shared(bank_name) => { let user_id = self.characters .lock() .await .iter() .find(|(id, _)| **id == *char_id) .unwrap() .1 .user_id; let shared_banks = self.shared_banks.lock().await; Ok(shared_banks .iter() .find(|((id, name), _)| *id == user_id && *name == *bank_name) .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().await; inventories.insert(*char_id, inventory.items .iter() .map(|inventory_item| { match inventory_item { InventoryItemEntity::Individual(individual) => { InventoryItemElement::Individual(individual.id) }, InventoryItemEntity::Stacked(stacked) => { InventoryItemElement::Stacked( stacked.iter() .map(|stacked| { stacked.id }) .collect() ) } } }) .collect()); Ok(()) } async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_identifier: &BankIdentifier) -> Result<(), GatewayError> { match bank_identifier { BankIdentifier::Character => { let mut character_banks = self.character_banks.lock().await; character_banks.insert(*char_id, bank.clone()); }, BankIdentifier::Shared(bank_name) => { let user_id = self.characters .lock() .await .iter() .find(|(id, _)| **id == *char_id) .unwrap() .1 .user_id; let mut shared_banks = self.shared_banks.lock().await; shared_banks.insert((user_id, bank_name.clone()), bank.clone()); } } Ok(()) } async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result { let equips = self.equips.lock().await; 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().await; 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().await; 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().await; 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_identifier: &BankIdentifier, meseta: Meseta) -> Result<(), GatewayError> { match bank_identifier { BankIdentifier::Character => { let mut bank_meseta = self.bank_meseta.lock().await; bank_meseta.insert(*char_id, meseta); } BankIdentifier::Shared(bank_name) => { let user_id = self.characters .lock() .await .iter() .find(|(id, _)| **id == *char_id) .unwrap() .1 .user_id; self.shared_bank_meseta .lock() .await .insert((user_id, bank_name.clone()), meseta); } } Ok(()) } async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result { match bank_identifier { BankIdentifier::Character => { let mut bank_meseta = self.bank_meseta.lock().await; if let Some(meseta) = bank_meseta.get_mut(char_id) { Ok(*meseta) } else { Err(GatewayError::Error) } }, BankIdentifier::Shared(bank_name) => { let mut shared_bank_meseta = self.shared_bank_meseta.lock().await; let user_id = self.characters .lock() .await .iter() .find(|(id, _)| **id == *char_id) .unwrap() .1 .user_id; if let Some(meseta) = shared_bank_meseta.get_mut(&(user_id, bank_name.clone())) { Ok(*meseta) } else { Ok(Meseta(0)) } } } } async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result { let mut trades = self.trades.lock().await; 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) } async fn set_character_playtime(&mut self, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError> { let mut characters = self.characters.lock().await; if let Some(character) = characters.get_mut(char_id) { character.playtime = playtime; Ok(()) } else { Err(GatewayError::Error) } } // I do not care to replicate this in testing async fn create_room(&mut self, room: NewRoomEntity) -> Result { Ok(RoomEntity { id: RoomEntityId(0), name: room.name, section_id: room.section_id, episode: room.episode, difficulty: room.difficulty, mode: room.mode, }) } // I do not care to replicate this in testing async fn add_room_note(&mut self, _room_id: RoomEntityId, _note: RoomNote) -> Result<(), GatewayError> { Ok(()) } }