diff --git a/src/bin/main.rs b/src/bin/main.rs index 8d8f549..e4d7ee9 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -9,7 +9,7 @@ use elseware::entity::account::{NewUserAccountEntity, NewUserSettingsEntity}; #[allow(unused_imports)] use elseware::entity::gateway::{EntityGateway, InMemoryGateway, PostgresGateway}; use elseware::entity::character::NewCharacterEntity; -use elseware::entity::item::{NewItemEntity, ItemDetail}; +use elseware::entity::item::{NewItemEntity, ItemDetail, InventoryItemEntity}; use elseware::common::interserver::AuthToken; use elseware::entity::item; @@ -67,13 +67,16 @@ fn main() { entity_gateway.create_user_settings(NewUserSettingsEntity::new(fake_user.id)).await.unwrap(); let mut character = NewCharacterEntity::new(fake_user.id); character.name = format!("Test Char {}", i*2); - entity_gateway.create_character(character).await.unwrap(); + let character = entity_gateway.create_character(character).await.unwrap(); + entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap(); + entity_gateway.set_bank_meseta(&character.id, item::BankName("".into()), item::Meseta(999999)).await.unwrap(); let mut character = NewCharacterEntity::new(fake_user.id); character.slot = 2; character.name = "ItemRefactor".into(); character.exp = 80000000; - character.meseta = 999999; let character = entity_gateway.create_character(character).await.unwrap(); + entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap(); + entity_gateway.set_bank_meseta(&character.id, item::BankName("".into()), item::Meseta(999999)).await.unwrap(); for _ in 0..3 { entity_gateway.create_item( @@ -162,8 +165,8 @@ fn main() { item: ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::DarkFlow, - grind: 5, - special: Some(item::weapon::WeaponSpecial::Charge), + grind: 0, + special: None, attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),], @@ -293,6 +296,20 @@ fn main() { } ).await.unwrap(); + let monomates = futures::future::join_all((0..6).map(|_| { + let mut entity_gateway = entity_gateway.clone(); + async move { + entity_gateway.create_item( + NewItemEntity { + item: ItemDetail::Tool ( + item::tool::Tool { + tool: item::tool::ToolType::Monomate, + } + ), + }).await.unwrap() + } + })).await; + let equipped = item::EquippedEntity { weapon: Some(item2_w.id), armor: Some(item7_a.id), @@ -302,7 +319,7 @@ fn main() { }; entity_gateway.set_character_equips(&character.id, &equipped).await.unwrap(); - let inventory = item::InventoryEntity::new(vec![item0, item1, item2_w, item3, item4, item5_m, item6, item7_a, item8_s, item9_u0, item10_u1, item11_u2, item12_u3, item13]); + let inventory = item::InventoryEntity::new(vec![InventoryItemEntity::from(item0), item1.into(), item2_w.into(), item3.into(), item4.into(), item5_m.into(), item6.into(), item7_a.into(), item8_s.into(), item9_u0.into(), item10_u1.into(), item11_u2.into(), item12_u3.into(), item13.into(), monomates.into()]); entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap(); entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), item::BankName("".into())).await.unwrap(); } diff --git a/src/entity/character.rs b/src/entity/character.rs index d6d4e3c..76346a9 100644 --- a/src/entity/character.rs +++ b/src/entity/character.rs @@ -294,8 +294,6 @@ pub struct NewCharacterEntity { pub materials: CharacterMaterials, pub tech_menu: CharacterTechMenu, - pub meseta: u32, - pub bank_meseta: u32, pub option_flags: u32, } @@ -315,8 +313,6 @@ impl NewCharacterEntity { guildcard: CharacterGuildCard::default(), materials: CharacterMaterials::default(), tech_menu: CharacterTechMenu::default(), - meseta: 0, - bank_meseta: 0, option_flags: 0, } } @@ -342,8 +338,5 @@ pub struct CharacterEntity { pub materials: CharacterMaterials, pub tech_menu: CharacterTechMenu, - pub meseta: u32, - // TODO: this should not be tied to the character - pub bank_meseta: u32, pub option_flags: u32, } diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index 46e13ed..fd8b1aa 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -116,7 +116,19 @@ pub trait EntityGateway: Send + Sync + Clone { unimplemented!(); } - async fn set_character_meseta(&mut self, _char_id: &CharacterEntityId, amount: usize) -> Result<(), GatewayError> { + async fn get_character_meseta(&mut self, _char_id: &CharacterEntityId) -> Result { + unimplemented!(); + } + + async fn set_character_meseta(&mut self, _char_id: &CharacterEntityId, _amount: Meseta) -> Result<(), GatewayError> { + unimplemented!(); + } + + async fn get_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: BankName) -> Result { + unimplemented!(); + } + + async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: BankName, _amount: Meseta) -> Result<(), GatewayError> { unimplemented!(); } } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index deb737b..57ad039 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -13,6 +13,8 @@ pub struct InMemoryGateway { users: Arc>>, user_settings: Arc>>, characters: Arc>>, + character_meseta: Arc>>, + bank_meseta: Arc>>, items: Arc>>, inventories: Arc>>, banks: Arc>>, @@ -27,6 +29,8 @@ impl Default for 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())), @@ -197,8 +201,6 @@ impl EntityGateway for InMemoryGateway { guildcard: character.guildcard, materials: character.materials, tech_menu: character.tech_menu, - meseta: character.meseta, - bank_meseta: character.bank_meseta, option_flags: character.option_flags, }; characters.insert(new_character.id, new_character.clone()); @@ -314,11 +316,35 @@ impl EntityGateway for InMemoryGateway { Ok(()) } - async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, amount: usize) -> Result<(), GatewayError> { - let mut characters = self.characters.lock().unwrap(); - if let Some(char) = characters.get_mut(&char_id) { - char.meseta = amount as u32; + 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), 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)) { + Ok(*meseta) + } + else { + Err(GatewayError::Error) + } + } } diff --git a/src/entity/gateway/postgres/migrations/V0004__meseta.sql b/src/entity/gateway/postgres/migrations/V0004__meseta.sql new file mode 100644 index 0000000..97f6b2e --- /dev/null +++ b/src/entity/gateway/postgres/migrations/V0004__meseta.sql @@ -0,0 +1,15 @@ +create table character_meseta ( + pchar integer references character (id) not null unique, + meseta integer not null, +); + +create table bank_meseta ( + pchar integer references character (id) not null, + bank varchar(128) not null, + meseta integer not null, + unique (pchar, bank) +); + + +alter table player_character + drop column meseta, bank_meseta; diff --git a/src/entity/gateway/postgres/models.rs b/src/entity/gateway/postgres/models.rs index f57e39d..2e5404e 100644 --- a/src/entity/gateway/postgres/models.rs +++ b/src/entity/gateway/postgres/models.rs @@ -216,8 +216,6 @@ pub struct PgCharacter { tp: i16, tech_menu: Vec, - meseta: i32, - bank_meseta: i32, } impl From for CharacterEntity { @@ -267,8 +265,6 @@ impl From for CharacterEntity { tech_menu: CharacterTechMenu { tech_menu: vec_to_array(other.tech_menu) }, - meseta: other.meseta as u32, - bank_meseta: other.bank_meseta as u32, } } } diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 3c17246..2fefb03 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -182,7 +182,7 @@ impl EntityGateway for PostgresGateway { 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) + config, infoboard, guildcard, power, mind, def, evade, luck, hp, tp, tech_menu, option_flags) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31) returning *;"#; @@ -215,8 +215,6 @@ impl EntityGateway for PostgresGateway { .bind(char.materials.hp as i16) .bind(char.materials.tp as i16) .bind(char.tech_menu.tech_menu.to_vec()) - .bind(char.meseta as i32) - .bind(char.bank_meseta as i32) .bind(char.option_flags as i32) .fetch_one(&self.pool).await?; @@ -241,7 +239,7 @@ impl EntityGateway for PostgresGateway { 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, - evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, meseta=$29, bank_meseta=$30, option_flags=$31 + evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29 where id=$32;"#; sqlx::query(q) .bind(char.user_id.0) @@ -272,8 +270,6 @@ impl EntityGateway for PostgresGateway { .bind(char.materials.hp as i16) .bind(char.materials.tp as i16) .bind(char.tech_menu.tech_menu.to_vec()) - .bind(char.meseta as i32) - .bind(char.bank_meseta as i32) .bind(char.option_flags as i32) .bind(char.id.0 as i32) .execute(&self.pool).await?; @@ -551,12 +547,43 @@ impl EntityGateway for PostgresGateway { Ok(()) } - async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, amount: usize) -> Result<(), GatewayError> { - sqlx::query(r#"update player_character set meseta=$2 where id = $1"#) + async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> { + sqlx::query("insert into character_meseta values ($1, $2) on conflict (pchar) do update set items = $2") .bind(char_id.0) - .bind(amount as i32) + .bind(meseta.0 as i32) .execute(&self.pool) .await?; Ok(()) } + + async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result { + #[derive(sqlx::FromRow)] + struct PgMeseta(i32); + let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1"#) + .bind(char_id.0) + .fetch_one(&self.pool) + .await?; + Ok(Meseta(meseta.0 as u32)) + } + + async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> { + sqlx::query("insert into bank_meseta values ($1, $2, $3) on conflict (pchar, bank) do update set items = $2") + .bind(char_id.0) + .bind(meseta.0 as i32) + .bind(bank.0) + .execute(&self.pool) + .await?; + Ok(()) + } + + async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName) -> Result { + #[derive(sqlx::FromRow)] + struct PgMeseta(i32); + let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1 and bank = $2"#) + .bind(char_id.0) + .bind(bank.0) + .fetch_one(&self.pool) + .await?; + Ok(Meseta(meseta.0 as u32)) + } } diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index ff722dd..5d75798 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -60,7 +60,7 @@ pub enum ItemNote { }, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct Meseta(pub u32); impl Meseta { diff --git a/src/login/character.rs b/src/login/character.rs index b3afa67..9b3cf1f 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -20,7 +20,7 @@ use libpso::{utf8_to_array, utf8_to_utf16_array}; use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::account::{UserAccountId, UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM}; -use crate::entity::item::{NewItemEntity, ItemDetail, ItemNote, InventoryItemEntity, InventoryEntity, BankEntity, BankName, EquippedEntity}; +use crate::entity::item::{NewItemEntity, ItemDetail, ItemNote, InventoryItemEntity, InventoryEntity, BankEntity, BankName, EquippedEntity, Meseta}; use crate::entity::item::weapon::Weapon; use crate::entity::item::armor::Armor; use crate::entity::item::tech::Technique; @@ -201,8 +201,8 @@ async fn new_character(entity_gateway: &mut EG, user: &UserAc _ => {} } - character.meseta = 300; let character = entity_gateway.create_character(character).await?; + entity_gateway.set_character_meseta(&character.id, Meseta(300)).await?; let new_weapon = match character.char_class { CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber, diff --git a/src/ship/character.rs b/src/ship/character.rs index bbd2ddc..2fdeb35 100644 --- a/src/ship/character.rs +++ b/src/ship/character.rs @@ -2,11 +2,13 @@ use libpso::character::character; use crate::common::leveltable::CharacterStats; use crate::entity::character::CharacterEntity; use crate::ship::items::{CharacterInventory, CharacterBank}; +use crate::entity::item::Meseta; pub struct CharacterBytesBuilder<'a> { character: Option<&'a CharacterEntity>, stats: Option<&'a CharacterStats>, level: Option, + meseta: Option, } impl<'a> Default for CharacterBytesBuilder<'a> { @@ -15,6 +17,7 @@ impl<'a> Default for CharacterBytesBuilder<'a> { character: None, stats: None, level: None, + meseta: None, } } } @@ -42,10 +45,18 @@ impl<'a> CharacterBytesBuilder<'a> { } } + pub fn meseta(self, meseta: Meseta) -> CharacterBytesBuilder<'a> { + CharacterBytesBuilder { + meseta: Some(meseta), + ..self + } + } + pub fn build(self) -> character::Character { let character = self.character.unwrap(); let stats = self.stats.unwrap(); let level = self.level.unwrap(); + let meseta = self.meseta.unwrap(); character::Character { name: libpso::utf8_to_utf16_array!(character.name, 16), hp: stats.hp, @@ -70,7 +81,7 @@ impl<'a> CharacterBytesBuilder<'a> { prop_y: character.appearance.prop_y, config: character.config.as_bytes(), techniques: character.techs.as_bytes(), - meseta: character.meseta, + meseta: meseta.0 as u32, exp: character.exp, ..character::Character::default() } @@ -82,6 +93,7 @@ pub struct FullCharacterBytesBuilder<'a> { character: Option<&'a CharacterEntity>, stats: Option<&'a CharacterStats>, level: Option, + meseta: Option, inventory: Option<&'a CharacterInventory>, bank: Option<&'a CharacterBank>, key_config: Option<&'a [u8; 0x16C]>, @@ -97,6 +109,7 @@ impl<'a> Default for FullCharacterBytesBuilder<'a> { character: None, stats: None, level: None, + meseta: None, inventory: None, bank: None, key_config: None, @@ -131,6 +144,13 @@ impl<'a> FullCharacterBytesBuilder<'a> { } } + pub fn meseta(self, meseta: Meseta) -> FullCharacterBytesBuilder<'a> { + FullCharacterBytesBuilder { + meseta: Some(meseta), + ..self + } + } + pub fn inventory(self, inventory: &'a CharacterInventory) -> FullCharacterBytesBuilder<'a> { FullCharacterBytesBuilder { inventory: Some(inventory), @@ -184,6 +204,7 @@ impl<'a> FullCharacterBytesBuilder<'a> { let character = self.character.unwrap(); let stats = self.stats.unwrap(); let level = self.level.unwrap(); + let meseta = self.meseta.unwrap(); let inventory = self.inventory.unwrap(); let bank = self.bank.unwrap(); let key_config = self.key_config.unwrap(); @@ -204,6 +225,7 @@ impl<'a> FullCharacterBytesBuilder<'a> { .character(character) .stats(stats) .level(level - 1) + .meseta(meseta) .build(), inventory: character::Inventory { item_count: inventory.count() as u8, diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs index efd9a64..044aabd 100644 --- a/src/ship/items/manager.rs +++ b/src/ship/items/manager.rs @@ -106,6 +106,8 @@ pub struct ItemManager { pub(super) id_counter: u32, pub(self) character_inventory: HashMap, + pub(self) character_meseta: HashMap, + pub(self) bank_meseta: HashMap, //character_bank: HashMap>, pub(self) character_bank: HashMap, pub(self) character_floor: HashMap, @@ -120,6 +122,8 @@ impl Default for ItemManager { ItemManager { id_counter: 0, character_inventory: HashMap::new(), + character_meseta: HashMap::new(), + bank_meseta: HashMap::new(), character_bank: HashMap::new(), character_floor: HashMap::new(), character_room: HashMap::new(), @@ -194,8 +198,13 @@ impl ItemManager { .collect::, _>>()?; let character_bank = CharacterBank::new(bank_items); + let character_meseta = entity_gateway.get_character_meseta(&character.id).await?; + let bank_meseta = entity_gateway.get_bank_meseta(&character.id, BankName("".into())).await?; + self.character_inventory.insert(character.id, character_inventory); self.character_bank.insert(character.id, character_bank); + self.character_meseta.insert(character.id, character_meseta); + self.bank_meseta.insert(character.id, bank_meseta); Ok(()) } @@ -237,6 +246,35 @@ impl ItemManager { //.ok_or(ItemManagerError::InvalidBankName(BankName("".to_string())))?) } + pub fn get_character_meseta(&self, character_id: &CharacterEntityId) -> Result<&Meseta, ItemManagerError> { + Ok(self.character_meseta.get(&character_id) + .ok_or(ItemManagerError::NoCharacter(*character_id))?) + } + + pub fn get_character_meseta_mut<'a>(&'a mut self, character_id: &CharacterEntityId) -> Result<&'a mut Meseta, ItemManagerError> { + Ok(self.character_meseta.get_mut(&character_id) + .ok_or(ItemManagerError::NoCharacter(*character_id))?) + } + + pub fn get_bank_meseta(&self, character_id: &CharacterEntityId) -> Result<&Meseta, ItemManagerError> { + Ok(self.bank_meseta.get(&character_id) + .ok_or(ItemManagerError::NoCharacter(*character_id))?) + } + + pub fn get_bank_meseta_mut<'a>(&'a mut self, character_id: &CharacterEntityId) -> Result<&'a mut Meseta, ItemManagerError> { + Ok(self.bank_meseta.get_mut(&character_id) + .ok_or(ItemManagerError::NoCharacter(*character_id))?) + } + + pub fn get_character_and_bank_meseta_mut<'a>(&'a mut self, character_id: &CharacterEntityId) -> Result<(&'a mut Meseta, &'a mut Meseta), ItemManagerError> { + Ok(( + self.character_meseta.get_mut(&character_id) + .ok_or(ItemManagerError::NoCharacter(*character_id))?, + self.bank_meseta.get_mut(&character_id) + .ok_or(ItemManagerError::NoCharacter(*character_id))? + )) + } + /*pub fn get_character_bank_mut(&mut self, character: &CharacterEntity) -> Result<&CharacterBank, ItemManagerError> { Ok(self.character_bank .get_mut(&character.id) @@ -336,11 +374,12 @@ impl ItemManager { } }, FloorItem::Meseta(meseta_floor_item) => { - if character.meseta >= 999999 { + let character_meseta = it.manager.character_meseta.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; + if character_meseta.0 >= 999999 { return Err(ItemManagerError::CouldNotAddToInventory(*item_id).into()); } it.action(Box::new(AddMesetaFloorItemToInventory { - character: (**character).clone(), + character_id: character.id, item: meseta_floor_item.clone() })); @@ -493,11 +532,12 @@ impl ItemManager { -> Result { let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let shared_floor = self.room_floor.get_mut(room_id).ok_or(ItemManagerError::NoCharacter(character.id))?; - if character.meseta < amount { + let character_meseta = self.character_meseta.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; + if character_meseta.0 < amount { return Err(ItemManagerError::CouldNotDropMeseta.into()) } - character.meseta -= amount; - entity_gateway.save_character(character).await?; + character_meseta.0 -= amount; + entity_gateway.set_character_meseta(&character.id, *character_meseta).await?; let item_id = self.room_item_id_counter.borrow_mut().get_mut(room_id).ok_or(ItemManagerError::NoCharacter(character.id))?(); let floor_item = FloorItem::Meseta(MesetaFloorItem { @@ -1183,16 +1223,16 @@ impl ItemAction for AddStackedFloorItemToInventory { #[derive(Debug)] struct AddMesetaFloorItemToInventory{ - character: CharacterEntity, + character_id: CharacterEntityId, item: MesetaFloorItem, } #[async_trait::async_trait] impl ItemAction for AddMesetaFloorItemToInventory { - async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { - let mut nchar = self.character.clone(); - nchar.meseta = std::cmp::min(self.character.meseta + self.item.meseta.0, 999999); - entity_gateway.save_character(&nchar).await?; + async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { + let character_meseta = item_manager.character_meseta.get_mut(&self.character_id).ok_or(ItemManagerError::NoCharacter(self.character_id))?; + character_meseta.0 = std::cmp::min(character_meseta.0 + self.item.meseta.0, 999999); + entity_gateway.set_character_meseta(&self.character_id, *character_meseta).await?; Ok(()) } } @@ -1254,7 +1294,17 @@ struct TradeMeseta { #[async_trait::async_trait] impl ItemAction for TradeMeseta { - async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { + async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { + { + let src_meseta = item_manager.get_character_meseta_mut(&self.src_character_id)?; + src_meseta.0 -= self.amount as u32; + entity_gateway.set_character_meseta(&self.src_character_id, *src_meseta).await?; + } + { + let dest_meseta = item_manager.get_character_meseta_mut(&self.dest_character_id)?; + dest_meseta.0 += self.amount as u32; + entity_gateway.set_character_meseta(&self.dest_character_id, *dest_meseta).await?; + } Ok(()) } } diff --git a/src/ship/packet/builder/mod.rs b/src/ship/packet/builder/mod.rs index a5c30d7..f5d9f3c 100644 --- a/src/ship/packet/builder/mod.rs +++ b/src/ship/packet/builder/mod.rs @@ -26,10 +26,12 @@ pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) - pub fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, item_manager: &ItemManager, level_table: &CharacterLevelTable) -> PlayerInfo { let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); let inventory = item_manager.get_character_inventory(&client.character).unwrap(); + let meseta = item_manager.get_character_meseta(&client.character.id).unwrap(); let character = CharacterBytesBuilder::default() .character(&client.character) .stats(&stats) .level(level - 1) + .meseta(*meseta) .build(); PlayerInfo { header: player_header(tag, client, area_client), diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index a2f1eda..ea6aff4 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -232,8 +232,8 @@ pub async fn send_bank_list(id: ClientId, { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let bank_items = item_manager.get_character_bank(&client.character)?; - - let bank_items_pkt = builder::message::bank_item_list(bank_items, client.character.bank_meseta); + let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?; + let bank_items_pkt = builder::message::bank_item_list(bank_items, bank_meseta.0); Ok(Box::new(vec![(id, SendShipPacket::BankItemList(bank_items_pkt))].into_iter())) } @@ -252,11 +252,16 @@ where let other_clients_in_area = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?; let bank_action_pkts = match bank_interaction.action { BANK_ACTION_DEPOSIT => { + let character_meseta = item_manager.get_character_meseta(&client.character.id)?; + let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?; if bank_interaction.item_id == 0xFFFFFFFF { - if client.character.meseta >= bank_interaction.meseta_amount && (bank_interaction.meseta_amount + client.character.bank_meseta) <= BANK_MESETA_CAPACITY { - client.character.meseta -= bank_interaction.meseta_amount; - client.character.bank_meseta += bank_interaction.meseta_amount; - entity_gateway.save_character(&client.character).await?; + if character_meseta.0 >= bank_interaction.meseta_amount && (bank_interaction.meseta_amount + bank_meseta.0) <= BANK_MESETA_CAPACITY { + let (character_meseta, bank_meseta) = item_manager.get_character_and_bank_meseta_mut(&client.character.id)?; + character_meseta.0 -= bank_interaction.meseta_amount; + bank_meseta.0 += bank_interaction.meseta_amount; + entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?; + // TODO: BankName + entity_gateway.set_bank_meseta(&client.character.id, item::BankName("".into()), *bank_meseta).await?; } Vec::new() } @@ -267,11 +272,16 @@ where } }, BANK_ACTION_WITHDRAW => { + let character_meseta = item_manager.get_character_meseta(&client.character.id)?; + let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?; if bank_interaction.item_id == 0xFFFFFFFF { - if client.character.meseta + bank_interaction.meseta_amount <= INVENTORY_MESETA_CAPACITY { - client.character.meseta += bank_interaction.meseta_amount; - client.character.bank_meseta -= bank_interaction.meseta_amount; - entity_gateway.save_character(&client.character).await?; + if (bank_meseta.0 >= bank_interaction.meseta_amount) && (character_meseta.0 + bank_interaction.meseta_amount <= INVENTORY_MESETA_CAPACITY) { + let (character_meseta, bank_meseta) = item_manager.get_character_and_bank_meseta_mut(&client.character.id)?; + character_meseta.0 += bank_interaction.meseta_amount; + bank_meseta.0 -= bank_interaction.meseta_amount; + entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?; + // TODO: BankName + entity_gateway.set_bank_meseta(&client.character.id, item::BankName("".into()), *bank_meseta).await?; } Vec::new() } @@ -370,12 +380,13 @@ where } }; - if client.character.meseta < item.price() as u32 { + let character_meseta = item_manager.get_character_meseta_mut(&client.character.id)?; + if character_meseta.0 < item.price() as u32 { return Err(ShipError::ShopError.into()) } - client.character.meseta -= item.price() as u32; - entity_gateway.save_character(&client.character).await?; + character_meseta.0 -= item.price() as u32; + entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?; let inventory_item = item_manager.player_buys_item(entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as usize).await?; let create = builder::message::create_withdrawn_inventory_item(area_client, inventory_item)?; @@ -447,8 +458,9 @@ where grind: grind_mod, }); - client.character.meseta -= 100; - entity_gateway.save_character(&client.character).await?; + let character_meseta = item_manager.get_character_meseta_mut(&client.character.id)?; + character_meseta.0 -= 100; + entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?; let preview_pkt = builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?; diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index a857ce2..b0004fb 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -22,12 +22,14 @@ pub fn block_selected(id: ClientId, let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); let inventory = item_manager.get_character_inventory(&client.character).unwrap(); + let meseta = item_manager.get_character_meseta(&client.character.id).unwrap(); let bank = item_manager.get_character_bank(&client.character).unwrap(); let fc = FullCharacterBytesBuilder::default() .character(&client.character) .stats(&stats) .level(level) + .meseta(*meseta) .inventory(inventory) .bank(bank) .key_config(&client.settings.settings.key_config) diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index 882c3e0..19c8281 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -194,7 +194,7 @@ pub fn update_player_position(id: ClientId, .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))? .as_ref() .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?; - + match &message.msg { GameMessage::PlayerChangedMap(p) => { client.x = p.x; @@ -258,18 +258,22 @@ pub fn update_player_position(id: ClientId, pub async fn charge_attack(id: ClientId, charge: &ChargeAttack, clients: &mut Clients, - entity_gateway: &mut EG) + entity_gateway: &mut EG, + item_manager: &mut ItemManager) -> Result + Send>, anyhow::Error> where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - if client.character.meseta >= charge.meseta { - client.character.meseta -= charge.meseta; - entity_gateway.save_character(&client.character).await?; + let meseta = item_manager.get_character_meseta_mut(&client.character.id)?; + + if meseta.0 >= charge.meseta { + meseta.0 -= charge.meseta; + entity_gateway.set_character_meseta(&client.character.id, *meseta).await?; + // TODO: this should probably echo the packet Ok(Box::new(None.into_iter())) } else { - Err(ShipError::NotEnoughMeseta(id, client.character.meseta).into()) + Err(ShipError::NotEnoughMeseta(id, meseta.0).into()) } } @@ -293,18 +297,21 @@ where pub async fn player_used_medical_center(id: ClientId, _pumc: &PlayerUsedMedicalCenter, // not needed? entity_gateway: &mut EG, - clients: &mut Clients) + clients: &mut Clients, + item_manager: &mut ItemManager) -> Result + Send>, anyhow::Error> where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - if client.character.meseta >= 10 { - client.character.meseta -= 10; - entity_gateway.save_character(&client.character).await?; + let meseta = item_manager.get_character_meseta_mut(&client.character.id)?; + if meseta.0 >= 10 { + meseta.0 -= 10; + entity_gateway.set_character_meseta(&client.character.id, *meseta).await?; + // TODO: this should probably echo the packet Ok(Box::new(None.into_iter())) } else { - Err(ShipError::NotEnoughMeseta(id, client.character.meseta).into()) + Err(ShipError::NotEnoughMeseta(id, meseta.0).into()) } } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 6ee71fe..8b0aa17 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -488,14 +488,14 @@ impl ShipServerState { handler::message::update_player_position(id, msg, &mut self.clients, &block.client_location, &block.rooms)? }, GameMessage::ChargeAttack(charge_attack) => { - handler::message::charge_attack(id, charge_attack, &mut self.clients, &mut self.entity_gateway).await? + handler::message::charge_attack(id, charge_attack, &mut self.clients, &mut self.entity_gateway, &mut self.item_manager).await? }, GameMessage::PlayerUseItem(player_use_item) => { let block = self.blocks.with_client(id, &self.clients)?; handler::message::use_item(id, player_use_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? }, GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => { - handler::message::player_used_medical_center(id, player_used_medical_center, &mut self.entity_gateway, &mut self.clients).await? + handler::message::player_used_medical_center(id, player_used_medical_center, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? }, GameMessage::PlayerFeedMag(player_feed_mag) => { let block = self.blocks.with_client(id, &self.clients)?; diff --git a/tests/common.rs b/tests/common.rs index 6b97b0e..857a80d 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -4,6 +4,7 @@ use elseware::common::serverstate::{ClientId, ServerState}; use elseware::entity::gateway::EntityGateway; use elseware::entity::account::{UserAccountEntity, NewUserAccountEntity, NewUserSettingsEntity}; use elseware::entity::character::{CharacterEntity, NewCharacterEntity}; +use elseware::entity::item::{Meseta, BankName}; use elseware::ship::ship::{ShipServerState, RecvShipPacket}; use elseware::ship::room::Difficulty; @@ -27,6 +28,8 @@ pub async fn new_user_character(entity_gateway: &mut EG, user let _settings = entity_gateway.create_user_settings(new_settings).await.unwrap(); let new_character = NewCharacterEntity::new(user.id); let character = entity_gateway.create_character(new_character).await.unwrap(); + entity_gateway.set_character_meseta(&character.id, Meseta(0)).await.unwrap(); + entity_gateway.set_bank_meseta(&character.id, BankName("".into()), Meseta(0)).await.unwrap(); (user, character) }