From 2351b31125c04adf964bf560a01458363f1dc692 Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 24 Oct 2020 17:45:59 -0600 Subject: [PATCH] entitygateway uses results for its functions now --- src/entity/gateway/entitygateway.rs | 49 ++++---- src/entity/gateway/inmemory.rs | 64 ++++++----- src/entity/gateway/mod.rs | 2 +- src/entity/gateway/postgres/postgres.rs | 143 +++++++++++++----------- src/login/character.rs | 14 ++- src/login/login.rs | 2 +- src/ship/items/manager.rs | 33 +++--- src/ship/ship.rs | 3 +- 8 files changed, 175 insertions(+), 135 deletions(-) diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index 71c23fd..7a28563 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -1,80 +1,91 @@ +use thiserror::Error; + use crate::entity::account::*; use crate::entity::character::*; use crate::entity::item::*; -// TODO: all these Options should be Results + +// TODO: better granularity? +//#[derive(Error, Debug)] +#[derive(Error, Debug)] +#[error("")] +pub enum GatewayError { + Error, + PgError(#[from] sqlx::Error) +} + #[async_trait::async_trait] pub trait EntityGateway: Send + Sync + Clone { - async fn create_user(&mut self, _user: NewUserAccountEntity) -> Option { + async fn create_user(&mut self, _user: NewUserAccountEntity) -> Result { unimplemented!() } - async fn get_user_by_id(&self, _id: UserAccountId) -> Option { + async fn get_user_by_id(&self, _id: UserAccountId) -> Result { unimplemented!(); } - async fn get_user_by_name(&self, _username: String) -> Option { + async fn get_user_by_name(&self, _username: String) -> Result { unimplemented!(); } - async fn save_user(&mut self, _user: &UserAccountEntity) { + async fn save_user(&mut self, _user: &UserAccountEntity) -> Result<(), GatewayError> { unimplemented!(); } - async fn create_user_settings(&mut self, _settings: NewUserSettingsEntity) -> Option { + async fn create_user_settings(&mut self, _settings: NewUserSettingsEntity) -> Result { unimplemented!(); } - async fn get_user_settings_by_user(&self, _user: &UserAccountEntity) -> Option { + async fn get_user_settings_by_user(&self, _user: &UserAccountEntity) -> Result { unimplemented!(); } - async fn save_user_settings(&mut self, _settings: &UserSettingsEntity) { + async fn save_user_settings(&mut self, _settings: &UserSettingsEntity) -> Result<(), GatewayError> { unimplemented!(); } - async fn create_character(&mut self, _char: NewCharacterEntity) -> Option { + async fn create_character(&mut self, _char: NewCharacterEntity) -> Result { unimplemented!(); } // TODO: just make this a vec sorted by slot order? - async fn get_characters_by_user(&self, _user: &UserAccountEntity) -> [Option; 4] { + async fn get_characters_by_user(&self, _user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { unimplemented!(); } - async fn save_character(&mut self, _char: &CharacterEntity) { + async fn save_character(&mut self, _char: &CharacterEntity) -> Result<(), GatewayError> { unimplemented!(); } - async fn get_guild_card_data_by_user(&self, _user: &UserAccountEntity) -> GuildCardDataEntity { + async fn get_guild_card_data_by_user(&self, _user: &UserAccountEntity) -> Result { unimplemented!(); } - async fn create_item(&mut self, _item: NewItemEntity) -> Option { + async fn create_item(&mut self, _item: NewItemEntity) -> Result { unimplemented!(); } - async fn save_item(&mut self, _item: &ItemEntity) { + async fn save_item(&mut self, _item: &ItemEntity) -> Result<(), GatewayError> { unimplemented!(); } - async fn change_item_location(&mut self, _item_id: &ItemEntityId, _item_location: ItemLocation) { + async fn change_item_location(&mut self, _item_id: &ItemEntityId, _item_location: ItemLocation) -> Result<(), GatewayError> { unimplemented!(); } - async fn feed_mag(&mut self, _mag_item_id: &ItemEntityId, _tool_item_id: &ItemEntityId) { + async fn feed_mag(&mut self, _mag_item_id: &ItemEntityId, _tool_item_id: &ItemEntityId) -> Result<(), GatewayError> { unimplemented!(); } - async fn change_mag_owner(&mut self, _mag_item_id: &ItemEntityId, _character: &CharacterEntity) { + async fn change_mag_owner(&mut self, _mag_item_id: &ItemEntityId, _character: &CharacterEntity) -> Result<(), GatewayError> { unimplemented!(); } - async fn use_mag_cell(&mut self, _mag_item_id: &ItemEntityId, _mag_cell_id: &ItemEntityId) { + async fn use_mag_cell(&mut self, _mag_item_id: &ItemEntityId, _mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> { unimplemented!(); } - async fn get_items_by_character(&self, _char: &CharacterEntity) -> Vec { + async fn get_items_by_character(&self, _char: &CharacterEntity) -> Result, GatewayError> { unimplemented!(); } } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 2801eb1..01ec302 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -3,7 +3,7 @@ use std::convert::TryInto; use crate::entity::account::*; use crate::entity::character::*; -use crate::entity::gateway::EntityGateway; +use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::item::*; use std::sync::{Arc, Mutex}; @@ -31,7 +31,7 @@ impl InMemoryGateway { #[async_trait::async_trait] impl EntityGateway for InMemoryGateway { - async fn create_user(&mut self, user: NewUserAccountEntity) -> Option { + async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { let mut users = self.users.lock().unwrap(); let id = users .iter() @@ -47,30 +47,33 @@ impl EntityGateway for InMemoryGateway { muted_until: user.muted_until, created_at: user.created_at, flags: user.flags, + activated: true, }; users.insert(user.id, user.clone()); - Some(user) + Ok(user) } - async fn get_user_by_id(&self, id: UserAccountId) -> Option { + async fn get_user_by_id(&self, id: UserAccountId) -> Result { let users = self.users.lock().unwrap(); - users.get(&id).map(|k| k.clone()) + users.get(&id).map(|k| k.clone()).ok_or(GatewayError::Error) } - async fn get_user_by_name(&self, username: String) -> Option { + async fn get_user_by_name(&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) { + 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) -> Option { + async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result { let mut user_settings = self.user_settings.lock().unwrap(); let id = user_settings .iter() @@ -82,28 +85,29 @@ impl EntityGateway for InMemoryGateway { settings: settings.settings, }; user_settings.insert(new_settings.id, new_settings.clone()); - Some(new_settings) + Ok(new_settings) } - async fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Option { + async fn get_user_settings_by_user(&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(&self, user: &UserAccountEntity) -> [Option; 4] { + async fn get_characters_by_user(&self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { let characters = self.characters.lock().unwrap(); 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())); - chars + Ok(chars) } - async fn create_character(&mut self, character: NewCharacterEntity) -> Option { + async fn create_character(&mut self, character: NewCharacterEntity) -> Result { let mut characters = self.characters.lock().unwrap(); let id = characters .iter() @@ -130,19 +134,20 @@ impl EntityGateway for InMemoryGateway { option_flags: character.option_flags, }; characters.insert(new_character.id, new_character.clone()); - Some(new_character) + Ok(new_character) } - async fn save_character(&mut self, char: &CharacterEntity) { + 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(&self, user: &UserAccountEntity) -> GuildCardDataEntity { - GuildCardDataEntity::new(user.id) + async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result { + Ok(GuildCardDataEntity::new(user.id)) } - async fn create_item(&mut self, item: NewItemEntity) -> Option { + async fn create_item(&mut self, item: NewItemEntity) -> Result { let mut items = self.items.lock().unwrap(); let id = items .iter() @@ -154,47 +159,52 @@ impl EntityGateway for InMemoryGateway { item: item.item, }; items.insert(ItemEntityId(id), new_item.clone()); - Some(new_item) + Ok(new_item) } - async fn save_item(&mut self, item: &ItemEntity) { + async fn save_item(&mut self, item: &ItemEntity) -> Result<(), GatewayError> { let mut items = self.items.lock().unwrap(); items.insert(item.id, item.clone()); + Ok(()) } - async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) { + async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> { self.items.lock().unwrap().get_mut(&item_id) .map(|item_entity| { item_entity.location = item_location }); + Ok(()) } - async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) { + 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(Vec::new()) .push(mag::MagModifier::FeedMag { food: *tool_item_id }); + Ok(()) } - async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) { + 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(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) { + 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(Vec::new()) .push(mag::MagModifier::MagCell(mag_cell_id.clone())); + Ok(()) } - async fn get_items_by_character(&self, character: &CharacterEntity) -> Vec { + async fn get_items_by_character(&self, character: &CharacterEntity) -> Result, GatewayError> { let items = self.items.lock().unwrap(); - items + Ok(items .iter() .filter(|(_, k)| { match k.location { @@ -241,6 +251,6 @@ impl EntityGateway for InMemoryGateway { }; item }) - .collect() + .collect()) } } diff --git a/src/entity/gateway/mod.rs b/src/entity/gateway/mod.rs index 528dcea..d819ef5 100644 --- a/src/entity/gateway/mod.rs +++ b/src/entity/gateway/mod.rs @@ -2,6 +2,6 @@ pub mod entitygateway; pub mod inmemory; pub mod postgres; -pub use entitygateway::EntityGateway; +pub use entitygateway::{EntityGateway, GatewayError}; pub use inmemory::InMemoryGateway; pub use self::postgres::PostgresGateway; diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 2a32d0d..a9f2f2a 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -9,7 +9,7 @@ use libpso::character::{settings, guildcard}; use libpso::util::vec_to_array; use crate::entity::account::*; use crate::entity::character::*; -use crate::entity::gateway::EntityGateway; +use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::item::*; use super::models::*; @@ -99,30 +99,30 @@ impl PostgresGateway { #[async_trait::async_trait] impl EntityGateway for PostgresGateway { - async fn create_user(&mut self, user: NewUserAccountEntity) -> Option { + async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { let new_user = sqlx::query_as::<_, PgUserAccount>("insert into user_accounts (email, username, password) values ($1, $2, $3) returning *;") .bind(user.email) .bind(user.username) .bind(user.password) - .fetch_one(&self.pool).await.unwrap(); - Some(new_user.into()) + .fetch_one(&self.pool).await?; + Ok(new_user.into()) } - async fn get_user_by_id(&self, id: UserAccountId) -> Option { + async fn get_user_by_id(&self, id: UserAccountId) -> Result { let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where id = $1") .bind(id.0) - .fetch_one(&self.pool).await.unwrap(); - Some(user.into()) + .fetch_one(&self.pool).await?; + Ok(user.into()) } - async fn get_user_by_name(&self, username: String) -> Option { + async fn get_user_by_name(&self, username: String) -> Result { let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where username = $1") .bind(username) - .fetch_one(&self.pool).await.unwrap(); - Some(user.into()) + .fetch_one(&self.pool).await?; + Ok(user.into()) } - async fn save_user(&mut self, user: &UserAccountEntity) { + async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> { sqlx::query("UPDATE user_accounts set name=$1, password=$2, banned=$3, muted=$4, flags=$5 where id=$6") .bind(&user.username) .bind(&user.password) @@ -130,10 +130,11 @@ impl EntityGateway for PostgresGateway { .bind(&user.muted_until) .bind(&user.flags) .bind(&user.id.0) - .fetch_one(&self.pool).await.unwrap(); + .fetch_one(&self.pool).await?; + Ok(()) } - async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Option { + async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result { let new_settings = sqlx::query_as::<_, PgUserSettings>("insert into user_settings (user_account, blocked_users, key_config, joystick_config, option_flags, shortcuts, symbol_chats, team_name) values ($1, $2, $3, $4, $5, $6, $7, $8) returning *;") .bind(settings.user_id.0) @@ -144,18 +145,18 @@ impl EntityGateway for PostgresGateway { .bind(settings.settings.shortcuts.to_vec()) .bind(settings.settings.symbol_chats.to_vec()) .bind(settings.settings.team_name.to_vec().into_iter().map(|i| i.to_le_bytes().to_vec()).flatten().collect::>()) - .fetch_one(&self.pool).await.unwrap(); - Some(new_settings.into()) + .fetch_one(&self.pool).await?; + Ok(new_settings.into()) } - async fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Option { + async fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Result { let settings = sqlx::query_as::<_, PgUserSettings>("select * from user_settings where id = $1") .bind(user.id.0) - .fetch_one(&self.pool).await.unwrap(); - Some(settings.into()) + .fetch_one(&self.pool).await?; + Ok(settings.into()) } - async fn save_user_settings(&mut self, settings: &UserSettingsEntity) { + async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> { sqlx::query("update user_settings set blocked_users=$1, key_config=$2, joystick_config=$3, option_flags=$4, shortcuts=$5, symbol_chats=$6, team_name=$7 where id=$8") .bind(settings.settings.blocked_users.to_vec().into_iter().map(|i| i.to_le_bytes().to_vec()).flatten().collect::>()) .bind(&settings.settings.key_config.to_vec()) @@ -165,10 +166,11 @@ impl EntityGateway for PostgresGateway { .bind(&settings.settings.symbol_chats.to_vec()) .bind(settings.settings.team_name.to_vec().into_iter().map(|i| i.to_le_bytes().to_vec()).flatten().collect::>()) .bind(&settings.id.0) - .fetch_one(&self.pool).await.unwrap(); + .fetch_one(&self.pool).await?; + Ok(()) } - async fn create_character(&mut self, char: NewCharacterEntity) -> Option { + async fn create_character(&mut self, char: NewCharacterEntity) -> Result { let q = r#"insert into player_character (user_account, slot, name, exp, class, section_id, costume, skin, face, head, hair, hair_r, hair_g, hair_b, prop_x, prop_y, techs, config, infoboard, guildcard, power, mind, def, evade, luck, hp, tp, tech_menu, meseta, bank_meseta, option_flags) @@ -208,28 +210,28 @@ impl EntityGateway for PostgresGateway { .bind(char.meseta as i32) .bind(char.bank_meseta as i32) .bind(char.option_flags as i32) - .fetch_one(&self.pool).await.unwrap(); + .fetch_one(&self.pool).await?; sqlx::query("insert into inventory_slots (pchar) values ($1)") .bind(character.id) - .execute(&self.pool).await.unwrap(); - Some(character.into()) + .execute(&self.pool).await?; + Ok(character.into()) } - async fn get_characters_by_user(&self, user: &UserAccountEntity) -> [Option; 4] { + async fn get_characters_by_user(&self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { let mut stream = sqlx::query_as::<_, PgCharacter>("select * from player_character where user_account = $1 and slot < 4 order by slot") .bind(user.id.0) .fetch(&self.pool); let mut result = [None; 4]; - while let Some(character) = stream.try_next().await.unwrap() { + while let Some(character) = stream.try_next().await? { let index = character.slot as usize; result[index] = Some(character.into()) } - result + Ok(result) } - async fn save_character(&mut self, char: &CharacterEntity) { + async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> { let q = r#"update player_character set user_account=$1, slot=$2, name=$3, exp=$4, class=$5, section_id=$6, costume=$7, skin=$8, face=$9, head=$10, hair=$11, hair_r=$12, hair_g=$13, hair_b=$14, prop_x=$15, prop_y=$16, techs=$17, config=$18, infoboard=$19, guildcard=$20, power=$21, mind=$22, def=$23, @@ -267,31 +269,33 @@ impl EntityGateway for PostgresGateway { .bind(char.meseta as i32) .bind(char.bank_meseta as i32) .bind(char.id.0 as i32) - .execute(&self.pool).await.unwrap(); + .execute(&self.pool).await?; + Ok(()) } - async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> GuildCardDataEntity { - GuildCardDataEntity { + async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result { + Ok(GuildCardDataEntity { id: GuildCardDataId(0), user_id: user.id, guildcard: guildcard::GuildCardData::default(), - } + }) } - async fn create_item(&mut self, item: NewItemEntity) -> Option { + async fn create_item(&mut self, item: NewItemEntity) -> Result { + let mut tx = self.pool.begin().await?; let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;") .bind(sqlx::types::Json(PgItemDetail::from(item.item))) - .fetch_one(&self.pool).await.unwrap(); + .fetch_one(&mut tx).await?; let location = if let ItemLocation::Inventory{character_id, slot, ..} = &item.location { sqlx::query("insert into item_location (item, location) values ($1, $2)") .bind(new_item.id) .bind(sqlx::types::Json(PgItemLocationDetail::from(item.location.clone()))) - .execute(&self.pool).await.unwrap(); + .execute(&mut tx).await?; sqlx::query("update inventory_slots set items[$2] = $1 where pchar = $3") .bind(new_item.id) .bind(*slot as i32) .bind(character_id.0 as i32) - .execute(&self.pool).await.unwrap(); + .execute(&mut tx).await?; sqlx::query_as::<_, PgItemLocation>(r#"select item_location.item, jsonb_set(item_location.location, '{Inventory,slot}', (array_position(inventory_slots.items, item.id))::text::jsonb) as location, @@ -303,70 +307,77 @@ impl EntityGateway for PostgresGateway { order by item_location.created_at limit 1"#) .bind(new_item.id) - .fetch_one(&self.pool).await.unwrap() + .fetch_one(&mut tx).await? } else { sqlx::query_as::<_, PgItemLocation>("insert into item_location (item, location) values ($1, $2) returning *") .bind(new_item.id) .bind(sqlx::types::Json(PgItemLocationDetail::from(item.location))) - .fetch_one(&self.pool).await.unwrap() + .fetch_one(&mut tx).await? }; - Some(ItemEntity { + tx.commit().await?; + Ok(ItemEntity { id: ItemEntityId(new_item.id as u32), item: new_item.item.0.into(), location: location.location.0.into(), }) } - async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) { + async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> { + let mut tx = self.pool.begin().await?; if let ItemLocation::Inventory{character_id, slot, ..} = &item_location { sqlx::query("update inventory_slots set items[array_position(items, $1)] = null where pchar = $2 and items[array_position(items, $1)] is not null") .bind(item_id.0 as i32) .bind(character_id.0 as i32) - .execute(&self.pool).await.unwrap(); + .execute(&mut tx).await?; sqlx::query("update inventory_slots set items[$2] = $1 where pchar = $3") .bind(item_id.0 as i32) .bind(*slot as i32) .bind(character_id.0 as i32) - .execute(&self.pool).await.unwrap(); + .execute(&mut tx).await?; sqlx::query(r#"insert into item_location (item, location) select $1, $2 where (select jsonb_object_keys(location) from item_location where item=$1 order by created_at desc limit 1) != 'Inventory'"#) .bind(item_id.0) .bind(sqlx::types::Json(PgItemLocationDetail::from(item_location))) - .execute(&self.pool).await.unwrap(); + .execute(&mut tx).await?; } else { sqlx::query("insert into item_location (item, location) values ($1, $2)") .bind(item_id.0) .bind(sqlx::types::Json(PgItemLocationDetail::from(item_location))) - .execute(&self.pool).await.unwrap(); + .execute(&mut tx).await?; } + tx.commit().await?; + Ok(()) } - async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) { + async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> { sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") .bind(mag_item_id.0) .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::FeedMag{food: *tool_item_id}))) - .execute(&self.pool).await.unwrap(); + .execute(&self.pool).await?; + Ok(()) } - async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) { + async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> { sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") .bind(mag_item_id.0) .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::OwnerChange(character.char_class, character.section_id)))) - .execute(&self.pool).await.unwrap(); + .execute(&self.pool).await?; + Ok(()) } - async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) { + async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> { sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") .bind(mag_item_id.0) .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::MagCell(*mag_cell_id)))) - .execute(&self.pool).await.unwrap(); + .execute(&self.pool).await?; + Ok(()) } - async fn get_items_by_character(&self, char: &CharacterEntity) -> Vec { + async fn get_items_by_character(&self, char: &CharacterEntity) -> Result, GatewayError> { let q = r#"select * from ( select distinct on (item_location.item) item.id, @@ -388,20 +399,20 @@ impl EntityGateway for PostgresGateway { let items = sqlx::query_as::<_, PgItemWithLocation>(q) .bind(char.id.0) .fetch(&self.pool); - join_all(items - .filter_map(|item: Result| { - let item = item.ok()?; - Some(ItemEntity { - id: ItemEntityId(item.id as u32), - item: item.item.0.into(), - location: item.location.0.into() - }) - }) - .map(|item: ItemEntity| { - self.apply_item_modifications(item) - }) - .collect::>() - .await - ).await + Ok(join_all(items + .filter_map(|item: Result| { + let item = item.ok()?; + Some(ItemEntity { + id: ItemEntityId(item.id as u32), + item: item.item.0.into(), + location: item.location.0.into() + }) + }) + .map(|item: ItemEntity| { + self.apply_item_modifications(item) + }) + .collect::>() + .await + ).await) } } diff --git a/src/login/character.rs b/src/login/character.rs index a06a513..99b6ebf 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -36,6 +36,9 @@ const SHIP_MENU_ID: u32 = 1; pub enum CharacterError { InvalidMenuSelection(u32, u32), ClientNotFound(ClientId), + CouldNotLoadSettings, + CouldNotLoadCharacters, + CouldNotLoadGuildcard, } #[derive(Debug)] @@ -310,12 +313,11 @@ impl CharacterServerState { let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?; let user = client.user.as_ref().unwrap(); - // TODO: this should error (data should be added on account creation, why did I copy this silly sylv logic?) let settings = match self.entity_gateway.get_user_settings_by_user(&user).await { - Some(settings) => settings, - None => { + Ok(settings) => settings, + Err(_) => { let user_settings = NewUserSettingsEntity::new(user.id); - self.entity_gateway.create_user_settings(user_settings).await.unwrap() + self.entity_gateway.create_user_settings(user_settings).await.map_err(|_| CharacterError::CouldNotLoadSettings)? } }; @@ -329,7 +331,7 @@ impl CharacterServerState { async fn char_select(&mut self, id: ClientId, select: &CharSelect) -> Result, CharacterError> { let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?; if client.characters.is_none() { - client.characters = Some(self.entity_gateway.get_characters_by_user(client.user.as_ref().unwrap()).await); + client.characters = Some(self.entity_gateway.get_characters_by_user(client.user.as_ref().unwrap()).await.map_err(|_| CharacterError::CouldNotLoadCharacters)?); } if select.reason == 0 { @@ -374,7 +376,7 @@ impl CharacterServerState { async fn guildcard_data_header(&mut self, id: ClientId) -> Result, CharacterError> { let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?; - let guildcard_data = self.entity_gateway.get_guild_card_data_by_user(client.user.as_ref().unwrap()).await; + let guildcard_data = self.entity_gateway.get_guild_card_data_by_user(client.user.as_ref().unwrap()).await.map_err(|_| CharacterError::CouldNotLoadGuildcard)?; let bytes = guildcard_data.guildcard.as_bytes(); let mut crc = crc32::Digest::new(crc32::IEEE); diff --git a/src/login/login.rs b/src/login/login.rs index d105b64..a3efb2d 100644 --- a/src/login/login.rs +++ b/src/login/login.rs @@ -60,7 +60,7 @@ impl SendServerPacket for SendLoginPacket { pub async fn get_login_status(entity_gateway: &impl EntityGateway, pkt: &Login) -> Result { let username = array_to_utf8(pkt.username).map_err(|_err| AccountStatus::Error)?; let password = array_to_utf8(pkt.password).map_err(|_err| AccountStatus::Error)?; - let user = entity_gateway.get_user_by_name(username).await.ok_or(AccountStatus::InvalidUser)?; + let user = entity_gateway.get_user_by_name(username).await.map_err(|_| AccountStatus::InvalidUser)?; let verified = bcrypt::verify(password, user.password.as_str()).map_err(|_err| AccountStatus::Error)?; match verified { true => if user.banned_until.map(|banned| banned > chrono::Utc::now()).unwrap_or(false) { diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs index cd777fb..8e7ebc8 100644 --- a/src/ship/items/manager.rs +++ b/src/ship/items/manager.rs @@ -48,10 +48,12 @@ pub enum ItemManagerError { CannotGetIndividualItem, InvalidSlot(u8, u8), // slots available, slot attempted NoArmorEquipped, + ItemIdNotInInventory(ClientItemId), + GatewayError(#[from] crate::entity::gateway::GatewayError) } -async fn update_inventory_slots(entity_gateway: &mut EG, character: &CharacterEntity, inventory: &CharacterInventory) { +async fn update_inventory_slots(entity_gateway: &mut EG, character: &CharacterEntity, inventory: &CharacterInventory) -> Result<(), ItemManagerError> { for (slot, item) in inventory.iter().enumerate() { match item { InventoryItem::Individual(individual_inventory_item) => { @@ -62,7 +64,7 @@ async fn update_inventory_slots(entity_gateway: &mut EG, char slot: slot, equipped: individual_inventory_item.equipped, } - ).await + ).await? }, InventoryItem::Stacked(stacked_inventory_item) => { for entity_id in stacked_inventory_item.entity_ids.iter() { @@ -72,10 +74,12 @@ async fn update_inventory_slots(entity_gateway: &mut EG, char character_id: character.id, slot: slot, equipped: false, - }).await; - }} + }).await? + } + } } } + Ok(()) } @@ -111,8 +115,8 @@ impl ItemManager { } // TODO: Result - pub async fn load_character(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) { - let items = entity_gateway.get_items_by_character(&character).await; + pub async fn load_character(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), ItemManagerError> { + let items = entity_gateway.get_items_by_character(&character).await?; let inventory_items = items.clone().into_iter() .filter_map(|item| { match item.location { @@ -204,6 +208,7 @@ impl ItemManager { let inventory = CharacterInventory::new(inventory_items.into_iter().map(|(_k, v)| v).take(30).collect()); self.character_inventory.insert(character.id, inventory); self.character_bank.insert(character.id, bank_items.remove(&BankName("".to_string())).unwrap_or(CharacterBank::new(Vec::new()))); + Ok(()) } pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) { @@ -392,7 +397,7 @@ impl ItemManager { y: item_drop.y, z: item_drop.z, } - }).await.ok_or(ItemManagerError::EntityGatewayError)?; + }).await?; FloorItem::Individual(IndividualFloorItem { entity_id: entity.id, item_id: item_id, @@ -413,7 +418,7 @@ impl ItemManager { y: item_drop.y, z: item_drop.z, } - }).await.ok_or(ItemManagerError::EntityGatewayError)?; + }).await?; FloorItem::Stacked(StackedFloorItem { entity_ids: vec![entity.id], item_id: item_id, @@ -483,7 +488,7 @@ impl ItemManager { }, } - update_inventory_slots(entity_gateway, character, &inventory).await; + update_inventory_slots(entity_gateway, character, &inventory).await?; Ok(()) } @@ -813,7 +818,7 @@ impl ItemManager { item_entities.push(entity_gateway.create_item(NewItemEntity { location: ItemLocation::Shop, item: ItemDetail::Tool(tool), - }).await.ok_or(ItemManagerError::EntityGatewayError)?); + }).await?); } let floor_item = StackedFloorItem { entity_ids: item_entities.into_iter().map(|i| i.id).collect(), @@ -843,7 +848,7 @@ impl ItemManager { let item_entity = entity_gateway.create_item(NewItemEntity { location: ItemLocation::Shop, item: ItemDetail::Tool(tool), - }).await.ok_or(ItemManagerError::EntityGatewayError)?; + }).await?; let floor_item = IndividualFloorItem { entity_id: item_entity.id, item_id: item_id, @@ -861,7 +866,7 @@ impl ItemManager { character_id: character.id, slot: slot.0, equipped: false, - }).await;//.ok_or(ItemManagerError::EntityGatewayError)?; + }).await?; picked_up_item.item_id }; inventory.get_item_by_id(item_id).ok_or(ItemManagerError::ItemIdNotInInventory(item_id))? @@ -871,7 +876,7 @@ impl ItemManager { let item_entity = entity_gateway.create_item(NewItemEntity { location: ItemLocation::Shop, item: item_detail.clone(), - }).await.ok_or(ItemManagerError::EntityGatewayError)?; + }).await?; let floor_item = IndividualFloorItem { entity_id: item_entity.id, item_id: item_id, @@ -889,7 +894,7 @@ impl ItemManager { character_id: character.id, slot: slot.0, equipped: false, - }).await;//.ok_or(ItemManagerError::EntityGatewayError)?; + }).await?; picked_up_item.item_id }; inventory.get_item_by_id(item_id).ok_or(ItemManagerError::ItemIdNotInInventory(item_id))? diff --git a/src/ship/ship.rs b/src/ship/ship.rs index dc6b6b2..7f1e3b2 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -18,7 +18,7 @@ use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState use crate::common::leveltable::CharacterLevelTable; use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage}; -use crate::entity::gateway::EntityGateway; +use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::account::{UserAccountEntity, UserSettingsEntity}; use crate::entity::character::{CharacterEntity, SectionID}; @@ -62,6 +62,7 @@ pub enum ShipError { IoError(#[from] std::io::Error), NotEnoughMeseta(ClientId, u32), ShopError, + GatewayError(#[from] GatewayError), } #[derive(Debug)]