diff --git a/Cargo.lock b/Cargo.lock index ad05a8c..c32fc6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1040,7 +1040,7 @@ checksum = "739e9d7726dc32173fed2d69d17eef3c54682169e4e20ff1d0a45dcd37063cef" [[package]] name = "libpso" version = "0.1.0" -source = "git+http://git.sharnoth.com/jake/libpso#e71b435ea3dde01a44abf0492bfab12236b1ec06" +source = "git+http://git.sharnoth.com/jake/libpso#05222bbf9fe402675447bc163b45e07a327cdb1a" dependencies = [ "chrono", "psopacket", @@ -1399,7 +1399,7 @@ dependencies = [ [[package]] name = "psopacket" version = "1.0.0" -source = "git+http://git.sharnoth.com/jake/libpso#e71b435ea3dde01a44abf0492bfab12236b1ec06" +source = "git+http://git.sharnoth.com/jake/libpso#05222bbf9fe402675447bc163b45e07a327cdb1a" dependencies = [ "proc-macro2", "quote", diff --git a/src/bin/main.rs b/src/bin/main.rs index 9ff056b..0a3ada0 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -68,14 +68,14 @@ fn main() { character.name = format!("Test Char {}", i*2); 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(); + entity_gateway.set_bank_meseta(&character.id, &item::BankIdentifier::Character, item::Meseta(999999)).await.unwrap(); let mut character = NewCharacterEntity::new(fake_user.id); character.slot = 2; character.name = "ItemRefactor".into(); character.exp = 80000000; 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(); + entity_gateway.set_bank_meseta(&character.id, &item::BankIdentifier::Character, item::Meseta(999999)).await.unwrap(); for _ in 0..3 { entity_gateway.create_item( @@ -329,7 +329,7 @@ fn main() { item8_s.into(), item9_u0.into(), item10_u1.into(), item11_u2.into(), item12_u3.into(), item13.into(), item14.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(); + entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), &item::BankIdentifier::Character).await.unwrap(); } info!("[patch] starting server"); diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index db2f5f0..06e4d97 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -104,7 +104,7 @@ pub trait EntityGateway: Send + Sync { unimplemented!(); } - async fn get_character_bank(&mut self, _char_id: &CharacterEntityId, _bank_name: &BankName) -> Result { + async fn get_character_bank(&mut self, _char_id: &CharacterEntityId, _bank_identifier: &BankIdentifier) -> Result { unimplemented!(); } @@ -112,7 +112,7 @@ pub trait EntityGateway: Send + Sync { unimplemented!(); } - async fn set_character_bank(&mut self, _char_id: &CharacterEntityId, _inventory: &BankEntity, _bank_name: &BankName) -> Result<(), GatewayError> { + async fn set_character_bank(&mut self, _char_id: &CharacterEntityId, _bank: &BankEntity, _bank_identifier: &BankIdentifier) -> Result<(), GatewayError> { unimplemented!(); } @@ -132,11 +132,11 @@ pub trait EntityGateway: Send + Sync { unimplemented!(); } - async fn get_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: &BankName) -> Result { + async fn get_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank_identifier: &BankIdentifier) -> Result { unimplemented!(); } - async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: &BankName, _amount: Meseta) -> Result<(), GatewayError> { + async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank_identifier: &BankIdentifier, _amount: Meseta) -> Result<(), GatewayError> { unimplemented!(); } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 13b281c..7752780 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -117,11 +117,11 @@ impl EntityGateway for InMemoryGatewayTransaction { } } - async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: &BankName) -> Result { - match self.working_gateway.get_character_bank(char_id, bank_name).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_name).await + self.original_gateway.get_character_bank(char_id, bank_identifier).await } } } @@ -130,11 +130,10 @@ impl EntityGateway for InMemoryGatewayTransaction { self.working_gateway.set_character_inventory(char_id, inventory).await } - async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: &BankName) -> Result<(), GatewayError> { - self.working_gateway.set_character_bank(char_id, bank, bank_name).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), @@ -161,15 +160,15 @@ impl EntityGateway for InMemoryGatewayTransaction { } } - async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> { - self.working_gateway.set_bank_meseta(char_id, bank, meseta).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: &BankName) -> Result { - match self.working_gateway.get_bank_meseta(char_id, bank).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).await + self.original_gateway.get_bank_meseta(char_id, bank_identifier).await } } } @@ -201,9 +200,11 @@ impl EntityGatewayTransaction for InMemoryGatewayTransaction { 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.banks.lock().await.extend(self.working_gateway.banks.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()); @@ -225,10 +226,12 @@ pub struct InMemoryGateway { user_settings: Arc>>, characters: Arc>>, character_meseta: Arc>>, - bank_meseta: Arc>>, + bank_meseta: Arc>>, + shared_bank_meseta: Arc>>, items: Arc>>, inventories: Arc>>>, - banks: Arc>>, + character_banks: Arc>>, + shared_banks: Arc>>, equips: Arc>>, mag_modifiers: Arc>>>, weapon_modifiers: Arc>>>, @@ -243,9 +246,11 @@ impl Default for InMemoryGateway { 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())), - banks: 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())), @@ -325,9 +330,11 @@ impl EntityGateway for InMemoryGateway { 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 banks = self.banks.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(); @@ -339,9 +346,11 @@ impl EntityGateway for InMemoryGateway { 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)), - banks: Arc::new(Mutex::new(banks)), + 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)), @@ -571,13 +580,33 @@ impl EntityGateway for InMemoryGateway { .unwrap_or_default()) } - async fn get_character_bank(&mut self, char_id: &CharacterEntityId, _bank_name: &BankName) -> Result { - let banks = self.banks.lock().await; - Ok(banks - .iter() - .find(|(id, _)| **id == *char_id) - .map(|(_, b)| b.clone()) - .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> { @@ -604,10 +633,26 @@ impl EntityGateway for InMemoryGateway { 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().await; - banks.insert(*char_id, bank.clone()); + 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(()) } @@ -642,19 +687,58 @@ impl EntityGateway for InMemoryGateway { } } - async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> { - let mut bank_meseta = self.bank_meseta.lock().await; - bank_meseta.insert((*char_id, bank.clone()), meseta); + 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: &BankName) -> Result { - let mut bank_meseta = self.bank_meseta.lock().await; - if let Some(meseta) = bank_meseta.get_mut(&(*char_id, bank.clone())) { - Ok(*meseta) - } - else { - Err(GatewayError::Error) + 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)) + } + } } } diff --git a/src/entity/gateway/postgres/migrations/V0011__shared_bank.sql b/src/entity/gateway/postgres/migrations/V0011__shared_bank.sql new file mode 100644 index 0000000..4bc7ec6 --- /dev/null +++ b/src/entity/gateway/postgres/migrations/V0011__shared_bank.sql @@ -0,0 +1,16 @@ + +create table shared_bank ( + user_account integer references user_accounts (id) not null, + items jsonb not null, + name varchar(128) not null, + unique (user_account, name) +); + + +create table shared_bank_meseta ( + user_account integer references user_accounts (id) not null, + name varchar(128) not null, + meseta integer not null, + unique (user_account, name) +); + diff --git a/src/entity/gateway/postgres/models.rs b/src/entity/gateway/postgres/models.rs index c310dda..b017b3b 100644 --- a/src/entity/gateway/postgres/models.rs +++ b/src/entity/gateway/postgres/models.rs @@ -607,11 +607,11 @@ pub enum PgItemNoteDetail { }, Withdraw { character_id: u32, - bank: String, + bank: BankIdentifier, }, Deposit { character_id: u32, - bank: String, + bank: BankIdentifier, } } @@ -650,13 +650,13 @@ impl From for PgItemNoteDetail { ItemNote::Withdraw{character_id, bank} => { PgItemNoteDetail::Withdraw { character_id: character_id.0, - bank: bank.0, + bank, } }, ItemNote::Deposit{character_id, bank} => { PgItemNoteDetail::Deposit { character_id: character_id.0, - bank: bank.0, + bank, } } } @@ -697,11 +697,11 @@ impl From for ItemNote { }, PgItemNoteDetail::Withdraw{character_id, bank} => ItemNote::Withdraw { character_id: CharacterEntityId(character_id), - bank: BankName(bank), + bank, }, PgItemNoteDetail::Deposit{character_id, bank} => ItemNote::Deposit { character_id: CharacterEntityId(character_id), - bank: BankName(bank), + bank, }, } } diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index bb8c018..4de7413 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -3,7 +3,7 @@ use std::convert::{From, TryFrom, Into}; use futures::Future; -use async_std::stream::StreamExt; +use futures::stream::{StreamExt, FuturesOrdered}; use async_std::sync::{Arc, Mutex}; use libpso::character::guildcard; use crate::entity::account::*; @@ -93,7 +93,7 @@ async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntit .bind(id.0 as i32) .fetch(conn); - weapon_modifiers.for_each(|modifier| { + weapon_modifiers.for_each(|modifier| async move { if let Ok(modifier) = modifier { weapon.apply_modifier(&modifier.modifier); } @@ -101,7 +101,7 @@ async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntit ItemDetail::Weapon(weapon) }, - ItemDetail::Mag(mut mag) => { + ItemDetail::Mag(mag) => { let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell from mag_modifier left join item on item.id = cast (modifier ->> 'FeedMag' as integer) @@ -111,7 +111,7 @@ async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntit .bind(id.0 as i32) .fetch(conn); - mag_modifiers.for_each(|modifier| { + let mag = mag_modifiers.fold(mag, |mut mag, modifier| async { let PgMagModifierWithParameters {modifier, feed, cell, ..} = modifier.unwrap(); let modifier: mag::MagModifier = modifier.0.into(); match modifier { @@ -128,6 +128,8 @@ async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntit mag.change_owner(class, section_id) }, } + + mag }).await; ItemDetail::Mag(mag) @@ -141,6 +143,31 @@ async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntit } } +async fn fetch_item(conn: &mut sqlx::PgConnection, item: PgInventoryItemEntity, individual: fn(ItemEntity) -> T, stacked: fn(Vec) -> T) -> Result +{ + match item { + PgInventoryItemEntity::Individual(item) => { + let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") + .bind(item) + .fetch_one(&mut *conn).await + .map(|item| item.into()) + .map(|item| apply_item_modifications(&mut *conn, item))? + .await; + Ok(individual(entity)) + }, + PgInventoryItemEntity::Stacked(items) => { + let mut stacked_item = Vec::new(); + for s_item in items { + stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") + .bind(s_item) + .fetch_one(&mut *conn).await + .map(|item| item.into())?) + } + Ok(stacked(stacked_item)) + } + } +} + async fn create_user(conn: &mut sqlx::PgConnection, user: NewUserAccountEntity) -> Result { let new_user = sqlx::query_as::<_, PgUserAccount>("insert into user_accounts (email, username, password, activated) values ($1, $2, $3, $4) returning *;") @@ -228,10 +255,10 @@ async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntit 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, + power, mind, def, evade, luck, hp, tp, tech_menu, option_flags, playtime) values - ($1, $2, $3, $4, $5, + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, @@ -280,7 +307,7 @@ async fn get_characters_by_user(conn: &mut sqlx::PgConnection, user: &UserAccoun .bind(user.id.0) .fetch(conn); - Ok(stream.fold(core::array::from_fn(|_| None), |mut acc, char| { + Ok(stream.fold(core::array::from_fn(|_| None), |mut acc, char| async move { if let Ok(char) = char { let slot = char.slot as usize; acc[slot] = Some(char.into()) @@ -391,76 +418,65 @@ async fn add_weapon_modifier(conn: &mut sqlx::PgConnection, item_id: &ItemEntity async fn get_character_inventory(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result { - let mut t = conn.begin().await?; + let conn = Arc::new(Mutex::new(conn.begin().await?)); // this is some degen shit let inventory = sqlx::query_as::<_, PgInventoryEntity>("select * from inventory where pchar = $1") .bind(char_id.0) - .fetch_one(&mut t).await?; - - // TODO: inefficient - let mut real_inventory = Vec::new(); - for inv_item in inventory.items.0.into_iter() { - match inv_item { - PgInventoryItemEntity::Individual(item) => { - let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(item) - .fetch_one(&mut t).await - .map(|item| item.into()) - .map(|item| apply_item_modifications(&mut t, item))? - .await; - real_inventory.push(InventoryItemEntity::Individual(entity)); - }, - PgInventoryItemEntity::Stacked(items) => { - let mut stacked_item = Vec::new(); - for s_item in items { - stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(s_item) - .fetch_one(&mut t).await - .map(|item| item.into())?) + .fetch_one(&mut **conn.lock().await).await?; + + Ok(InventoryEntity::new( + inventory.items.0 + .into_iter() + .map(move |item| { + let conn = conn.clone(); + async move { + fetch_item(&mut **conn.lock().await, item, InventoryItemEntity::Individual, InventoryItemEntity::Stacked).await } - real_inventory.push(InventoryItemEntity::Stacked(stacked_item)); - } - } - } - - Ok(InventoryEntity::new(real_inventory)) + }) + .collect::>() + .collect::>() + .await + .into_iter() + .collect::, _>>()?)) } -async fn get_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_name: &BankName) -> Result +async fn get_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result { - let mut t = conn.begin().await?; - let bank = sqlx::query_as::<_, PgInventoryEntity>("select * from bank where pchar = $1 and name = $2") - .bind(char_id.0) - .bind(bank_name.0.clone()) - .fetch_one(&mut t).await?; - // TODO: inefficient - let mut real_bank = Vec::new(); - for bank_item in bank.items.0.into_iter() { - match bank_item { - PgInventoryItemEntity::Individual(item) => { - let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(item) - .fetch_one(&mut t).await - .map(|item| item.into()) - .map(|item| apply_item_modifications(&mut t, item))? - .await; - real_bank.push(BankItemEntity::Individual(entity)); - }, - PgInventoryItemEntity::Stacked(items) => { - let mut stacked_item = Vec::new(); - for s_item in items { - stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(s_item) - .fetch_one(&mut t).await - .map(|item| item.into()) - .map(|item| apply_item_modifications(&mut t, item))? - .await) - } - real_bank.push(BankItemEntity::Stacked(stacked_item)); - } + let conn = Arc::new(Mutex::new(conn.begin().await?)); // this is some degen shit + let bank = match bank_identifier { + BankIdentifier::Character => { + sqlx::query_as::<_, PgInventoryEntity>("select * from bank where pchar = $1") + .bind(char_id.0) + .fetch_one(&mut **conn.lock().await).await? + }, + BankIdentifier::Shared(bank_name) => { + sqlx::query_as::<_, PgInventoryEntity>("select player_character.id as pchar, shared_bank.items as items from shared_bank + join player_character on shared_bank.user_account = player_character.user_account + where player_character.id = $1 and shared_bank.name = $2") + .bind(char_id.0) + .bind(&bank_name.0) + .fetch_optional(&mut **conn.lock().await) + .await? + .unwrap_or_else(|| PgInventoryEntity { + pchar: char_id.0 as i32, + items: sqlx::types::Json::default(), + }) } - } + }; - Ok(BankEntity::new(real_bank)) + Ok(BankEntity::new( + bank.items.0 + .into_iter() + .map(move |item| { + let conn = conn.clone(); + async move { + fetch_item(&mut **conn.lock().await, item, BankItemEntity::Individual, BankItemEntity::Stacked).await + } + }) + .collect::>() + .collect::>() + .await + .into_iter() + .collect::, _>>()?)) } async fn set_character_inventory(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> @@ -486,8 +502,8 @@ async fn set_character_inventory(conn: &mut sqlx::PgConnection, char_id: &Charac Ok(()) } -async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: &BankName) -> Result<(), GatewayError> -{ + +async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankEntity, bank_identifier: &BankIdentifier) -> Result<(), GatewayError> { let bank = bank.items.iter() .map(|item| { match item { @@ -501,12 +517,26 @@ async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEn }) .collect::>(); - sqlx::query("insert into bank (pchar, items, name) values ($1, $2, $3) on conflict (pchar, name) do update set items = $2") - .bind(char_id.0) - .bind(sqlx::types::Json(bank)) - .bind(bank_name.0.clone()) - .execute(conn) - .await?; + match bank_identifier { + BankIdentifier::Character => { + sqlx::query("insert into bank (pchar, items, name) values ($1, $2, '') on conflict (pchar, name) do update set items = $2") + .bind(char_id.0) + .bind(sqlx::types::Json(bank)) + .execute(conn) + .await?; + }, + BankIdentifier::Shared(bank_name) => { + sqlx::query("insert into shared_bank (user_account, items, name) + select player_character.user_account, $2, $3 from player_character + where player_character.id = $1 + on conflict (user_account, name) do update set items = $2;") + .bind(char_id.0) + .bind(sqlx::types::Json(bank)) + .bind(&bank_name.0) + .execute(conn) + .await?; + } + } Ok(()) } @@ -559,26 +589,55 @@ async fn get_character_meseta(conn: &mut sqlx::PgConnection, char_id: &Character Ok(Meseta(meseta.0 as u32)) } -async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> +async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier, meseta: Meseta) -> Result<(), GatewayError> { - sqlx::query("insert into bank_meseta values ($1, $2, $3) on conflict (pchar, bank) do update set meseta = $3") - .bind(char_id.0) - .bind(bank.0.clone()) - .bind(meseta.0 as i32) - .execute(conn) - .await?; + match bank_identifier { + BankIdentifier::Character => { + sqlx::query("insert into bank_meseta values ($1, '', $2) on conflict (pchar, bank) do update set meseta = $2") + .bind(char_id.0) + .bind(meseta.0 as i32) + .execute(conn) + .await?; + }, + BankIdentifier::Shared(bank_name) => { + sqlx::query("insert into shared_bank_meseta (user_account, name, meseta) + select player_character.user_account, $2, $3 from player_character + where player_character.id = $1 + on conflict (user_account, name) do update set meseta = $3") + .bind(char_id.0) + .bind(&bank_name.0) + .bind(meseta.0 as i32) + .execute(conn) + .await?; + } + } + Ok(()) } -async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankName) -> Result +async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result { #[derive(sqlx::FromRow)] struct PgMeseta(i32); - let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from bank_meseta where pchar = $1 and bank = $2"#) - .bind(char_id.0) - .bind(bank.0.clone()) - .fetch_one(conn) - .await?; + + let meseta = match bank_identifier { + BankIdentifier::Character => { + sqlx::query_as::<_, PgMeseta>(r#"select meseta from bank_meseta where pchar = $1"#) + .bind(char_id.0) + .fetch_one(conn) + .await? + }, + BankIdentifier::Shared(bank_name) => { + sqlx::query_as::<_, PgMeseta>(r#"select shared_bank_meseta.meseta from shared_bank_meseta + join player_character on shared_bank_meseta.user_account = player_character.user_account + where player_character.id = $1 and shared_bank_meseta.name = $2"#) + .bind(char_id.0) + .bind(&bank_name.0) + .fetch_optional(conn) + .await? + .unwrap_or(PgMeseta(0)) + } + }; Ok(Meseta(meseta.0 as u32)) } @@ -696,16 +755,16 @@ impl<'t> EntityGateway for PostgresGateway<'t> { get_character_inventory(&mut *self.pool.acquire().await?, char_id).await } - async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: &BankName) -> Result { - get_character_bank(&mut *self.pool.acquire().await?, char_id, bank_name).await + async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result { + get_character_bank(&mut *self.pool.acquire().await?, char_id, bank_identifier).await } async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> { set_character_inventory(&mut *self.pool.acquire().await?, char_id, inventory).await } - async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: &BankName) -> Result<(), GatewayError> { - set_character_bank(&mut *self.pool.acquire().await?, char_id, bank, bank_name).await + async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_identifier: &BankIdentifier) -> Result<(), GatewayError> { + set_character_bank(&mut *self.pool.acquire().await?, char_id, bank, bank_identifier).await } async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result { @@ -724,12 +783,12 @@ impl<'t> EntityGateway for PostgresGateway<'t> { get_character_meseta(&mut *self.pool.acquire().await?, char_id).await } - async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> { - set_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank, meseta).await + async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier, meseta: Meseta) -> Result<(), GatewayError> { + set_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank_identifier, meseta).await } - async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName) -> Result { - get_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank).await + async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result { + get_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank_identifier).await } async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result { @@ -822,16 +881,16 @@ impl<'c> EntityGateway for PostgresTransaction<'c> { get_character_inventory(&mut *self.pgtransaction.lock().await, char_id).await } - async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: &BankName) -> Result { - get_character_bank(&mut *self.pgtransaction.lock().await, char_id, bank_name).await + async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result { + get_character_bank(&mut *self.pgtransaction.lock().await, char_id, bank_identifier).await } async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> { set_character_inventory(&mut *self.pgtransaction.lock().await, char_id, inventory).await } - async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: &BankName) -> Result<(), GatewayError> { - set_character_bank(&mut *self.pgtransaction.lock().await, char_id, bank, bank_name).await + async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_identifier: &BankIdentifier) -> Result<(), GatewayError> { + set_character_bank(&mut *self.pgtransaction.lock().await, char_id, bank, bank_identifier).await } async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result { @@ -850,12 +909,12 @@ impl<'c> EntityGateway for PostgresTransaction<'c> { get_character_meseta(&mut *self.pgtransaction.lock().await, char_id).await } - async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> { - set_bank_meseta(&mut *self.pgtransaction.lock().await, char_id, bank, meseta).await + async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier, meseta: Meseta) -> Result<(), GatewayError> { + set_bank_meseta(&mut *self.pgtransaction.lock().await, char_id, bank_identifier, meseta).await } - async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName) -> Result { - get_bank_meseta(&mut *self.pgtransaction.lock().await, char_id, bank).await + async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result { + get_bank_meseta(&mut *self.pgtransaction.lock().await, char_id, bank_identifier).await } async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result { diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index eabddb5..6b7bf53 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -22,6 +22,12 @@ pub struct BankName(pub String); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct TradeId(pub u32); +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, derive_more::Display)] +pub enum BankIdentifier { + Character, + Shared(BankName), +} + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum ItemNote { CharacterCreation { @@ -62,11 +68,11 @@ pub enum ItemNote { }, Withdraw { character_id: CharacterEntityId, - bank: BankName, + bank: BankIdentifier, }, Deposit { character_id: CharacterEntityId, - bank: BankName, + bank: BankIdentifier, }, } diff --git a/src/login/character.rs b/src/login/character.rs index 349d1dd..60446fc 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -23,7 +23,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, Meseta}; +use crate::entity::item::{NewItemEntity, ItemDetail, ItemNote, InventoryItemEntity, InventoryEntity, BankEntity, BankIdentifier, EquippedEntity, Meseta}; use crate::entity::item::weapon::Weapon; use crate::entity::item::armor::Armor; use crate::entity::item::tech::Technique; @@ -211,7 +211,7 @@ async fn new_character(entity_gateway: &mut EG, user: let character = entity_gateway.create_character(character).await?; entity_gateway.set_character_meseta(&character.id, Meseta(300)).await?; - entity_gateway.set_bank_meseta(&character.id, &BankName("".into()), Meseta(0)).await?; + entity_gateway.set_bank_meseta(&character.id, &BankIdentifier::Character, Meseta(0)).await?; let new_weapon = match character.char_class { CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber, @@ -305,7 +305,7 @@ async fn new_character(entity_gateway: &mut EG, user: InventoryItemEntity::Stacked(monomates), InventoryItemEntity::Stacked(monofluids)], }; entity_gateway.set_character_inventory(&character.id, &inventory).await?; - entity_gateway.set_character_bank(&character.id, &BankEntity::default(), &BankName("".into())).await?; + entity_gateway.set_character_bank(&character.id, &BankEntity::default(), &BankIdentifier::Character).await?; let equipped = EquippedEntity { weapon: Some(weapon.id), armor: Some(armor.id), diff --git a/src/ship/chatcommand.rs b/src/ship/chatcommand.rs new file mode 100644 index 0000000..97d5054 --- /dev/null +++ b/src/ship/chatcommand.rs @@ -0,0 +1,86 @@ +use libpso::packet::ship::PlayerChat; +use crate::entity::gateway::EntityGateway; +use crate::common::serverstate::ClientId; +use crate::ship::ship::{ShipServerState, SendShipPacket}; +use crate::ship::client::Clients; +use crate::ship::items::state::ItemState; +use crate::entity::item::{BankName, BankIdentifier}; +use crate::ship::packet::builder::message::bank_item_list; + +async fn default_bank<'a, EG>(id: ClientId, + entity_gateway: &mut EG, + clients: &Clients, + item_state: &mut ItemState) + -> Result, String> +where + EG: EntityGateway + Clone + 'static, +{ + let bank = clients + .with_mut(id, |client| { + let mut item_state = item_state.clone(); + let mut entity_gateway = entity_gateway.clone(); + + Box::pin(async move { + item_state.load_character_bank(&mut entity_gateway, &client.character, BankIdentifier::Character).await?; + item_state.get_character_bank(&client.character).await + }) + }) + .await + .map_err(|err| format!("an error occured\n{err:?}"))? + .map_err(|err| format!("an error occured\n{err:?}"))?; + + let bank_items_pkt = bank_item_list(&bank); + Ok(vec![(id, SendShipPacket::BankItemList(bank_items_pkt))]) +} + +async fn switch_bank<'a, EG, T>(id: ClientId, + mut tokens: T, + entity_gateway: &mut EG, + clients: &Clients, + item_state: &mut ItemState) + -> Result, String> +where + EG: EntityGateway + Clone + 'static, + T: Iterator + 'a, +{ + let bank_name = BankName(tokens.next().unwrap_or("").into()); + let bank = clients + .with_mut(id, |client| { + let mut item_state = item_state.clone(); + let mut entity_gateway = entity_gateway.clone(); + + Box::pin(async move { + item_state.load_character_bank(&mut entity_gateway, &client.character, BankIdentifier::Shared(bank_name)).await?; + item_state.get_character_bank(&client.character).await + }) + }) + .await + .map_err(|err| format!("an error occured\n{err:?}"))? + .map_err(|err| format!("an error occured\n{err:?}"))?; + + let bank_items_pkt = bank_item_list(&bank); + Ok(vec![(id, SendShipPacket::BankItemList(bank_items_pkt))]) +} + + +pub async fn handle_chat_command(id: ClientId, + message: PlayerChat, + state: &mut ShipServerState) + -> Option, String>> +where + EG: EntityGateway + Clone + 'static, +{ + let mut tokens = message.message + .trim_start_matches("\tJ") + .trim_start_matches("\tE") + .trim_end_matches('\0') + .split_whitespace(); + let cmd = tokens.next()?; + match cmd { + "/bank" => Some(default_bank(id, &mut state.entity_gateway, &state.clients, &mut state.item_state).await), + "/sbank" => Some(switch_bank(id, tokens, &mut state.entity_gateway, &state.clients, &mut state.item_state).await), + + _ => None, + } + +} diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index f4dcbec..3b74a0a 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -270,7 +270,7 @@ where Box::pin(async move { let mut bank = item_state.bank(&character_id).await?; bank.remove_meseta(amount)?; - transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?; + transaction.gateway().set_bank_meseta(&character_id, &bank.identifier, bank.meseta).await?; item_state.set_bank(bank).await; Ok(((item_state, transaction), ())) @@ -313,7 +313,7 @@ where Box::pin(async move { let mut bank = item_state.bank(&character_id).await?; bank.add_meseta(amount)?; - transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?; + transaction.gateway().set_bank_meseta(&character_id, &bank.identifier, bank.meseta).await?; item_state.set_bank(bank).await; Ok(((item_state, transaction), ())) @@ -338,7 +338,7 @@ where let item = bank.take_item(&item_id, amount) .await .ok_or_else(|| ItemStateError::NoBankItem(item_id))?; - transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?; + transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.identifier).await?; item_state.set_bank(bank).await; Ok(((item_state, transaction), item)) @@ -358,16 +358,16 @@ where move |(mut item_state, transaction), bank_item| { let character = character.clone(); Box::pin(async move { - let bank_name = item_state.bank(&character.id).await?.name; + let bank_identifier = item_state.bank(&character.id).await?.identifier; let mut inventory = item_state.inventory(&character.id).await?; let character_id = character.id; let transaction = bank_item.with_entity_id(transaction, |mut transaction, entity_id| { - let bank_name = bank_name.clone(); + let bank_identifier = bank_identifier.clone(); async move { transaction.gateway().add_item_note(&entity_id, ItemNote::Withdraw { character_id, - bank: bank_name, + bank: bank_identifier, }).await?; Ok(transaction) }}).await?; @@ -408,23 +408,22 @@ where move |(mut item_state, transaction), inventory_item| { Box::pin(async move { let mut bank = item_state.bank(&character_id).await?; - let bank_name = bank.name.clone(); + let bank_identifier = bank.identifier.clone(); let mut transaction = inventory_item.with_entity_id(transaction, move |mut transaction, entity_id| { - let bank_name = bank_name.clone(); + let bank_identifier = bank_identifier.clone(); async move { transaction.gateway().add_item_note(&entity_id, ItemNote::Deposit { character_id, - bank: bank_name, + bank: bank_identifier, }).await?; Ok(transaction) }}).await?; bank.add_inventory_item(inventory_item)?; - transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?; + transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.identifier).await?; item_state.set_bank(bank).await; - Ok(((item_state, transaction), ())) }) } @@ -614,7 +613,8 @@ where tool, }) }; - inventory.add_item(inventory_item)?.1 + inventory.add_item(inventory_item.clone()) + .with_context(|| format!("inventory {inventory:?}\nitem {inventory_item:?}"))?.1 }, item_detail => { let item_entity = transaction.gateway().create_item(NewItemEntity { @@ -631,7 +631,8 @@ where item: item_detail, }) }; - inventory.add_item(inventory_item)?.1 + inventory.add_item(inventory_item.clone()) + .with_context(|| format!("inventory {inventory:?}\nitem {inventory_item:?}"))?.1 }, }; diff --git a/src/ship/items/apply_item.rs b/src/ship/items/apply_item.rs index 6978f2e..58f3b98 100644 --- a/src/ship/items/apply_item.rs +++ b/src/ship/items/apply_item.rs @@ -296,6 +296,7 @@ where ToolType::Antidote => Ok(Vec::new()), ToolType::Antiparalysis => Ok(Vec::new()), ToolType::TrapVision => Ok(Vec::new()), + ToolType::ScapeDoll => Ok(Vec::new()), ToolType::Monogrinder => weapon_grind(item_state, entity_gateway, character, entity_id, 1).await, ToolType::Digrinder => weapon_grind(item_state, entity_gateway, character, entity_id, 2).await, ToolType::Trigrinder => weapon_grind(item_state, entity_gateway, character, entity_id, 3).await, diff --git a/src/ship/items/bank.rs b/src/ship/items/bank.rs index cf88ae3..73f0e10 100644 --- a/src/ship/items/bank.rs +++ b/src/ship/items/bank.rs @@ -1,11 +1,12 @@ use std::cmp::Ordering; use libpso::character::character; use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, BankEntity, BankItemEntity, BankName}; +use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, BankEntity, BankItemEntity}; use std::future::Future; use async_std::sync::{Arc, Mutex}; use crate::entity::character::CharacterEntityId; +use crate::entity::item::BankIdentifier; use crate::ship::items::state::ItemStateError; use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult}; use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; @@ -22,6 +23,7 @@ pub enum BankError { } + #[derive(Clone, Debug)] pub enum BankItemDetail { Individual(IndividualItemDetail), @@ -98,7 +100,7 @@ impl Bank { pub struct BankState { pub character_id: CharacterEntityId, pub item_id_counter: Arc>, - pub name: BankName, + pub identifier: BankIdentifier, pub bank: Bank, pub meseta: Meseta, } @@ -112,12 +114,12 @@ async fn new_item_id(item_id_counter: &Arc>) -> ClientItemId { } impl BankState { - pub fn new(character_id: CharacterEntityId, name: BankName, mut bank: Bank, meseta: Meseta) -> BankState { + pub fn new(character_id: CharacterEntityId, identifier: BankIdentifier, mut bank: Bank, meseta: Meseta) -> BankState { bank.0.sort(); BankState { character_id, item_id_counter: Arc::new(Mutex::new(0)), - name, + identifier, bank, meseta, } @@ -303,12 +305,14 @@ impl BankState { } } -impl std::cmp::PartialEq for BankItem { - fn eq(&self, other: &BankItem) -> bool { + + +impl std::cmp::PartialEq for BankItemDetail { + fn eq(&self, other: &BankItemDetail) -> bool { let mut self_bytes = [0u8; 4]; let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); + self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]); let self_value = u32::from_be_bytes(self_bytes); let other_value = u32::from_be_bytes(other_bytes); @@ -317,15 +321,14 @@ impl std::cmp::PartialEq for BankItem { } } -impl std::cmp::Eq for BankItem {} +impl std::cmp::Eq for BankItemDetail {} -impl std::cmp::PartialOrd for BankItem { - fn partial_cmp(&self, other: &BankItem) -> Option { +impl std::cmp::PartialOrd for BankItemDetail { + fn partial_cmp(&self, other: &BankItemDetail) -> Option { let mut self_bytes = [0u8; 4]; let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); - + self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]); let self_value = u32::from_be_bytes(self_bytes); let other_value = u32::from_be_bytes(other_bytes); @@ -334,13 +337,12 @@ impl std::cmp::PartialOrd for BankItem { } } -impl std::cmp::Ord for BankItem { - fn cmp(&self, other: &BankItem) -> std::cmp::Ordering { +impl std::cmp::Ord for BankItemDetail { + fn cmp(&self, other: &BankItemDetail) -> std::cmp::Ordering { let mut self_bytes = [0u8; 4]; let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); - + self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]); let self_value = u32::from_le_bytes(self_bytes); let other_value = u32::from_le_bytes(other_bytes); @@ -349,3 +351,24 @@ impl std::cmp::Ord for BankItem { } } + +impl std::cmp::PartialEq for BankItem { + fn eq(&self, other: &BankItem) -> bool { + self.item.eq(&other.item) + } +} + +impl std::cmp::Eq for BankItem {} + +impl std::cmp::PartialOrd for BankItem { + fn partial_cmp(&self, other: &BankItem) -> Option { + self.item.partial_cmp(&other.item) + } +} + +impl std::cmp::Ord for BankItem { + fn cmp(&self, other: &BankItem) -> std::cmp::Ordering { + self.item.cmp(&other.item) + } +} + diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index b22a25c..906762a 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -1,11 +1,12 @@ -use std::collections::HashMap; +use std::collections::{HashMap, BinaryHeap}; +use std::cmp::Reverse; use async_std::sync::{Arc, RwLock, Mutex}; -use futures::future::join_all; +use futures::stream::{FuturesOrdered, StreamExt}; use anyhow::Context; use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::character::{CharacterEntity, CharacterEntityId}; -use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankName}; +use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankIdentifier}; use crate::entity::item::tool::Tool; use crate::entity::item::weapon::Weapon; use crate::entity::item::mag::Mag; @@ -218,9 +219,8 @@ impl ItemState { Ok(ClientItemId(*self.room_item_id_counter.read().await)) } - pub async fn load_character(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), anyhow::Error> { + pub async fn load_character_inventory(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), anyhow::Error> { let inventory = entity_gateway.get_character_inventory(&character.id).await?; - let bank = entity_gateway.get_character_bank(&character.id, &BankName("".into())).await?; let equipped = entity_gateway.get_character_equips(&character.id).await?; let inventory_items = inventory.items.into_iter() @@ -262,49 +262,58 @@ impl ItemState { meseta: character_meseta, }; - let bank_items = join_all( - bank.items.into_iter() - .map(|item| { - let mut citem_state = self.clone(); - async move { - Ok(match item { - BankItemEntity::Individual(item) => { - BankItem { - item_id: citem_state.new_item_id().await?, - item: BankItemDetail::Individual(IndividualItemDetail { - entity_id: item.id, - item: item.item, - }) - } - }, - BankItemEntity::Stacked(items) => { - BankItem { - item_id: citem_state.new_item_id().await?, - item: BankItemDetail::Stacked(StackedItemDetail { - entity_ids: items.iter().map(|i| i.id).collect(), - tool: items.get(0) - .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? - .item - .clone() - .as_tool() - .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? - }) - } - }, + self.character_inventory + .write() + .await + .insert(character.id, RwLock::new(inventory_state)); + Ok(()) + } + + pub async fn load_character_bank(&mut self, entity_gateway: &mut EG, character: &CharacterEntity, bank_identifier: BankIdentifier) -> Result<(), anyhow::Error> { + let bank = entity_gateway.get_character_bank(&character.id, &bank_identifier).await?; + let bank_items = bank.items + .into_iter() + .map(|item| { + Ok(Reverse(match item { + BankItemEntity::Individual(item) => { + BankItemDetail::Individual(IndividualItemDetail { + entity_id: item.id, + item: item.item, + }) + }, + BankItemEntity::Stacked(items) => { + BankItemDetail::Stacked(StackedItemDetail { + entity_ids: items.iter().map(|i| i.id).collect(), + tool: items.get(0) + .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? + .item + .clone() + .as_tool() + .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? }) - }}) - .collect::>()) - .await + } + })) + }) + .collect::, anyhow::Error>>()? + .into_iter() + .map(|item| { + let mut citem_state = self.clone(); + async move { + Ok(BankItem { + item_id: citem_state.new_item_id().await?, + item: item.0, + }) + } + }) + .collect::>() + .collect::>() + .await .into_iter() .collect::, anyhow::Error>>()?; - let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &BankName("".into())).await?; - let bank_state = BankState::new(character.id, BankName("".into()), Bank::new(bank_items), bank_meseta); + let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &bank_identifier).await?; + let bank_state = BankState::new(character.id, bank_identifier, Bank::new(bank_items), bank_meseta); - self.character_inventory - .write() - .await - .insert(character.id, RwLock::new(inventory_state)); self.character_bank .write() .await @@ -312,6 +321,13 @@ impl ItemState { Ok(()) } + pub async fn load_character(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), anyhow::Error> { + self.load_character_inventory(entity_gateway, character).await?; + self.load_character_bank(entity_gateway, character, BankIdentifier::Character).await?; + + Ok(()) + } + pub async fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) { let mut base_item_ids = self.room_gem_item_ids .write() @@ -366,7 +382,7 @@ impl ItemState { let removed = { self.character_room.write().await.remove(&character.id) }; - + if let Some(room) = removed.as_ref() { // TODO: this looks wrong, .all(r != room) maybe? if self.character_room.read().await.iter().any(|(_, r)| r == room) { diff --git a/src/ship/mod.rs b/src/ship/mod.rs index 3bc573b..c58edb6 100644 --- a/src/ship/mod.rs +++ b/src/ship/mod.rs @@ -13,3 +13,4 @@ pub mod packet; pub mod quests; pub mod shops; pub mod trade; +pub mod chatcommand; diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 4bbed5e..8a1c6fa 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -28,6 +28,7 @@ use crate::ship::map::{Maps, MapsError, MapAreaError, generate_free_roam_maps}; use crate::ship::packet::handler; use crate::ship::shops::{WeaponShop, ToolShop, ArmorShop}; use crate::ship::trade::TradeState; +use crate::ship::chatcommand; // TODO: remove once stuff settles down pub use crate::ship::client::*; @@ -290,6 +291,7 @@ pub enum SendShipPacket { CancelTrade(CancelTrade), TradeSuccessful(TradeSuccessful), LobbyEvent(LobbyEvent), + LargeDialog(LargeDialog), } impl SendServerPacket for SendShipPacket { @@ -333,6 +335,7 @@ impl SendServerPacket for SendShipPacket { SendShipPacket::CancelTrade(pkt) => pkt.as_bytes(), SendShipPacket::TradeSuccessful(pkt) => pkt.as_bytes(), SendShipPacket::LobbyEvent(pkt) => pkt.as_bytes(), + SendShipPacket::LargeDialog(pkt) => pkt.as_bytes(), } } } @@ -495,10 +498,10 @@ impl Blocks { #[derive(Clone)] pub struct ShipServerState { - entity_gateway: EG, + pub(crate) entity_gateway: EG, pub clients: Clients, name: String, - item_state: items::state::ItemState, + pub(crate) item_state: items::state::ItemState, shops: ItemShops, pub blocks: Blocks, event: ShipEvent, @@ -733,8 +736,18 @@ impl ServerState for ShipServerState { self.direct_message(id, msg).await? }, RecvShipPacket::PlayerChat(msg) => { - let block = self.blocks.get_from_client(id, &self.clients).await?; - handler::communication::player_chat(id, msg, &block.client_location, &self.clients).await? + match chatcommand::handle_chat_command(id, msg.clone(), self).await { + Some(ccmd) => { + match ccmd { + Ok(pkts) => pkts, + Err(msg) => vec![(id, SendShipPacket::LargeDialog(LargeDialog::new(msg)))] + } + }, + None => { + let block = self.blocks.get_from_client(id, &self.clients).await?; + handler::communication::player_chat(id, msg, &block.client_location, &self.clients).await? + } + } }, RecvShipPacket::CreateRoom(create_room) => { let block = self.blocks.get_from_client(id, &self.clients).await?; diff --git a/tests/common.rs b/tests/common.rs index 1a8a6a0..e9dccd0 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -4,10 +4,12 @@ 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::entity::item::{Meseta, BankName, BankIdentifier}; use elseware::ship::ship::{ShipServerState, RecvShipPacket}; use elseware::ship::room::Difficulty; +use elseware::entity::item; + use libpso::packet::ship::*; use libpso::packet::login::{Login, Session}; use libpso::{utf8_to_array, utf8_to_utf16_array}; @@ -30,7 +32,7 @@ pub async fn new_user_character(entity_gateway: &mut 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(); + entity_gateway.set_bank_meseta(&character.id, &BankIdentifier::Character, Meseta(0)).await.unwrap(); (user, character) } @@ -92,3 +94,44 @@ pub async fn join_room(ship: &mut ShipServerState })).await.unwrap(); ship.handle(id, RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap(); } + + + +pub struct WeaponBuilder { + weapon: item::weapon::WeaponType, + grind: u8, +} + +impl WeaponBuilder { + pub fn grind(self, grind: u8) -> WeaponBuilder { + WeaponBuilder { + grind, + ..self + } + } + + pub fn as_new(self) -> item::NewItemEntity { + item::NewItemEntity { + item: item::ItemDetail::Weapon( + item::weapon::Weapon { + weapon: self.weapon, + grind: self.grind, + special: None, + attrs: [None, None, None], + tekked: true, + } + ) + } + } +} + +pub struct ItemBuilder; + +impl ItemBuilder { + pub fn weapon(weapon: item::weapon::WeaponType) -> WeaponBuilder { + WeaponBuilder { + weapon, + grind: 0, + } + } +} diff --git a/tests/test_bank.rs b/tests/test_bank.rs index e2c200b..71fc8f5 100644 --- a/tests/test_bank.rs +++ b/tests/test_bank.rs @@ -31,7 +31,7 @@ async fn test_bank_items_sent_in_character_login() { ), }).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item]), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item]), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -69,7 +69,7 @@ async fn test_request_bank_items() { }).await.unwrap()); } - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -113,7 +113,7 @@ async fn test_request_stacked_bank_items() { }).await.unwrap()); } - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -178,7 +178,7 @@ async fn test_request_bank_items_sorted() { }).await.unwrap(); let bank = vec![item::BankItemEntity::Individual(item1), vec![monomate].into(), item2.into()]; - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -275,7 +275,7 @@ async fn test_deposit_individual_item() { assert_eq!(item.id, item::ItemEntityId(1)); }).unwrap(); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_individual(|item| { assert_eq!(item.id, item::ItemEntityId(2)); @@ -335,7 +335,7 @@ async fn test_deposit_stacked_item() { && player_no_longer_has_item.amount == 3 )); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -397,7 +397,7 @@ async fn test_deposit_partial_stacked_item() { )); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -443,7 +443,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -477,7 +477,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() { && player_no_longer_has_item.amount == 2 )); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -516,7 +516,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -543,7 +543,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() { assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.len(), 10); @@ -594,7 +594,7 @@ async fn test_deposit_individual_item_in_full_bank() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -621,7 +621,7 @@ async fn test_deposit_individual_item_in_full_bank() { assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 200); let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); @@ -666,7 +666,7 @@ async fn test_deposit_stacked_item_in_full_bank() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(full_bank), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(full_bank), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -693,7 +693,7 @@ async fn test_deposit_stacked_item_in_full_bank() { assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 200); let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); @@ -752,7 +752,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() { almost_full_bank.push(bank_monomates.into()); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(almost_full_bank), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(almost_full_bank), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -777,7 +777,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() { unknown: 0, })))).await.unwrap(); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 200); bank_items.items[199].with_stacked(|items| { assert_eq!(items.len(), 4); @@ -818,7 +818,7 @@ async fn test_deposit_meseta() { })))).await.unwrap(); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert!(c1_meseta.0 == 277); assert!(c1_bank_meseta.0 == 23); } @@ -829,7 +829,7 @@ async fn test_deposit_too_much_meseta() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap(); - entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(999980)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(999980)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -857,7 +857,7 @@ async fn test_deposit_too_much_meseta() { assert!(packets.is_err()); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert!(c1_meseta.0 == 300); assert!(c1_bank_meseta.0 == 999980); } @@ -868,7 +868,7 @@ async fn test_deposit_meseta_when_bank_is_maxed() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap(); - entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(999999)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(999999)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -896,7 +896,7 @@ async fn test_deposit_meseta_when_bank_is_maxed() { assert!(packets.is_err()); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert!(c1_meseta.0 == 300); assert!(c1_bank_meseta.0 == 999999); } @@ -923,7 +923,7 @@ async fn test_withdraw_individual_item() { ), }).await.unwrap()); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -983,7 +983,7 @@ async fn test_withdraw_stacked_item() { }).await.unwrap()); } - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1042,7 +1042,7 @@ async fn test_withdraw_partial_stacked_item() { ), }).await.unwrap()); } - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1075,7 +1075,7 @@ async fn test_withdraw_partial_stacked_item() { if create_item.item_id == 0x20001 )); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -1120,7 +1120,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1153,7 +1153,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() { if create_item.item_id == 0x20000 )); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 0); let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); @@ -1195,7 +1195,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1222,7 +1222,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() { assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -1273,7 +1273,7 @@ async fn test_withdraw_individual_item_in_full_inventory() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1299,7 +1299,7 @@ async fn test_withdraw_individual_item_in_full_inventory() { })))).await; assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 1); let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); @@ -1341,7 +1341,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1369,7 +1369,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() { assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -1397,7 +1397,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() { ), }).await.unwrap()); } - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_item]), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_item]), &item::BankIdentifier::Character).await.unwrap(); let mut items = Vec::new(); for _i in 0..29usize { @@ -1453,7 +1453,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() { unknown: 0, })))).await.unwrap(); - let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert!(bank_items.items.len() == 0); let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); @@ -1471,7 +1471,7 @@ async fn test_withdraw_meseta() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; - entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(300)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(300)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1497,7 +1497,7 @@ async fn test_withdraw_meseta() { })))).await.unwrap(); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert!(c1_meseta.0 == 23); assert!(c1_bank_meseta.0 == 277); } @@ -1508,7 +1508,7 @@ async fn test_withdraw_too_much_meseta() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, item::Meseta(999980)).await.unwrap(); - entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(300)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(300)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1536,7 +1536,7 @@ async fn test_withdraw_too_much_meseta() { assert!(packet.is_err()); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert!(c1_meseta.0 == 999980); assert!(c1_bank_meseta.0 == 300); } @@ -1547,7 +1547,7 @@ async fn test_withdraw_meseta_inventory_is_maxed() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); - entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(300)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(300)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1575,7 +1575,7 @@ async fn test_withdraw_meseta_inventory_is_maxed() { assert!(packet.is_err()); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Character).await.unwrap(); assert!(c1_meseta.0 == 999999); assert!(c1_bank_meseta.0 == 300); } @@ -1588,7 +1588,7 @@ async fn test_withdraw_meseta_and_buy_a_few_monomates_with_it() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, item::Meseta(100)).await.unwrap(); - entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(300)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(300)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1629,7 +1629,416 @@ async fn test_withdraw_meseta_and_buy_a_few_monomates_with_it() { })))).await.unwrap(); //let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - //let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); + //let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Character).await.unwrap(); //assert!(c1_meseta.0 == 23); //assert!(c1_bank_meseta.0 == 277); } + +#[async_std::test] +async fn test_deposit_items_into_shared_banks() { + let mut entity_gateway = InMemoryGateway::default(); + + let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; + + let item0 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Saber).as_new()).await.unwrap(); + let item1 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Buster).as_new()).await.unwrap(); + let item2 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Rifle).as_new()).await.unwrap(); + let item3 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Handgun).as_new()).await.unwrap(); + let item4 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Autogun).as_new()).await.unwrap(); + let item5 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Calibur).as_new()).await.unwrap(); + + entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![item0, item1, item2, item3, item4, item5])).await.unwrap(); + + let mut ship = Box::new(ShipServerState::builder() + .gateway(entity_gateway.clone()) + .build()); + log_in_char(&mut ship, ClientId(1), "a1", "a").await; + join_lobby(&mut ship, ClientId(1)).await; + create_room(&mut ship, ClientId(1), "room", "").await; + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest { + client: 0, + target: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0x10000, + action: 0, + item_amount: 0, + meseta_amount: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::PlayerChat(PlayerChat::new(0, "\tE/sbank asdf".into()))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest { + client: 0, + target: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0x10001, + action: 0, + item_amount: 0, + meseta_amount: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0x10002, + action: 0, + item_amount: 0, + meseta_amount: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::PlayerChat(PlayerChat::new(0, "\tE/sbank qwer".into()))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0x10003, + action: 0, + item_amount: 0, + meseta_amount: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::PlayerChat(PlayerChat::new(0, "\tE/bank".into()))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0x10004, + action: 0, + item_amount: 0, + meseta_amount: 0, + unknown: 0, + })))).await.unwrap(); + + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Shared(item::BankName("asdf".into()))).await.unwrap(); + assert_eq!(bank_items.items.len(), 2); + bank_items.items[0].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(2)); + }).unwrap(); + + bank_items.items[1].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(3)); + }).unwrap(); + + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Shared(item::BankName("qwer".into()))).await.unwrap(); + assert_eq!(bank_items.items.len(), 1); + bank_items.items[0].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(4)); + }).unwrap(); + + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); + assert_eq!(bank_items.items.len(), 2); + bank_items.items[0].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(1)); + }).unwrap(); + + bank_items.items[1].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(5)); + }).unwrap(); + + let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); + assert_eq!(inventory_items.items.len(), 1); + inventory_items.items[0].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(6)); + }).unwrap(); +} + + +#[async_std::test] +async fn test_deposit_meseta_into_shared_banks() { + let mut entity_gateway = InMemoryGateway::default(); + + let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; + entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap(); + + let mut ship = Box::new(ShipServerState::builder() + .gateway(entity_gateway.clone()) + .build()); + log_in_char(&mut ship, ClientId(1), "a1", "a").await; + join_lobby(&mut ship, ClientId(1)).await; + create_room(&mut ship, ClientId(1), "room", "").await; + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest { + client: 0, + target: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0xFFFFFFFF, + action: 0, + item_amount: 0, + meseta_amount: 23, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::PlayerChat(PlayerChat::new(0, "\tE/sbank asdf".into()))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest { + client: 0, + target: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0xFFFFFFFF, + action: 0, + item_amount: 0, + meseta_amount: 55, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::PlayerChat(PlayerChat::new(0, "\tE/sbank qwer".into()))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest { + client: 0, + target: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0xFFFFFFFF, + action: 0, + item_amount: 0, + meseta_amount: 32, + unknown: 0, + })))).await.unwrap(); + + let meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); + assert_eq!(meseta.0, 300-23-55-32); + + let bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Character).await.unwrap(); + assert_eq!(bank_meseta.0, 23); + let bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Shared(item::BankName("asdf".into()))).await.unwrap(); + assert_eq!(bank_meseta.0, 55); + let bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Shared(item::BankName("qwer".into()))).await.unwrap(); + assert_eq!(bank_meseta.0, 32); +} + +#[async_std::test] +async fn test_withdraw_items_from_shared_banks() { + let mut entity_gateway = InMemoryGateway::default(); + + let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; + + let item0 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Saber).as_new()).await.unwrap(); + let item1 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Buster).as_new()).await.unwrap(); + let item2 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Rifle).as_new()).await.unwrap(); + let item3 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Handgun).as_new()).await.unwrap(); + let item4 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Autogun).as_new()).await.unwrap(); + let item5 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Calibur).as_new()).await.unwrap(); + + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item0, item1]), &item::BankIdentifier::Character).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item2, item3]), &item::BankIdentifier::Shared(item::BankName("asdf".into()))).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item4, item5]), &item::BankIdentifier::Shared(item::BankName("qwer".into()))).await.unwrap(); + + let mut ship = Box::new(ShipServerState::builder() + .gateway(entity_gateway.clone()) + .build()); + log_in_char(&mut ship, ClientId(1), "a1", "a").await; + join_lobby(&mut ship, ClientId(1)).await; + create_room(&mut ship, ClientId(1), "room", "").await; + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest { + client: 0, + target: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0x20000, // saber (1) + action: 1, + item_amount: 0, + meseta_amount: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::PlayerChat(PlayerChat::new(0, "\tE/sbank asdf".into()))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest { + client: 0, + target: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0x810003, // handgun (4) + action: 1, + item_amount: 0, + meseta_amount: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0x810004, // rifle (3) + action: 1, + item_amount: 0, + meseta_amount: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::PlayerChat(PlayerChat::new(0, "\tE/sbank qwer".into()))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0x810006, // autogun (5) + action: 1, + item_amount: 0, + meseta_amount: 0, + unknown: 0, + })))).await.unwrap(); + + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Shared(item::BankName("asdf".into()))).await.unwrap(); + assert_eq!(bank_items.items.len(), 0); + + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Shared(item::BankName("qwer".into()))).await.unwrap(); + assert_eq!(bank_items.items.len(), 1); + bank_items.items[0].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(6)); + }).unwrap(); + + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankIdentifier::Character).await.unwrap(); + assert_eq!(bank_items.items.len(), 1); + bank_items.items[0].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(2)); + }).unwrap(); + + let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); + assert_eq!(inventory_items.items.len(), 4); + inventory_items.items[0].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(1)); + }).unwrap(); + inventory_items.items[1].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(4)); + }).unwrap(); + inventory_items.items[2].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(3)); + }).unwrap(); + inventory_items.items[3].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(5)); + }).unwrap(); +} + + +#[async_std::test] +async fn test_withdraw_meseta_from_shared_banks() { + let mut entity_gateway = InMemoryGateway::default(); + + let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; + entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(300)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Shared(item::BankName("asdf".into())), item::Meseta(300)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Shared(item::BankName("qwer".into())), item::Meseta(300)).await.unwrap(); + + let mut ship = Box::new(ShipServerState::builder() + .gateway(entity_gateway.clone()) + .build()); + log_in_char(&mut ship, ClientId(1), "a1", "a").await; + join_lobby(&mut ship, ClientId(1)).await; + create_room(&mut ship, ClientId(1), "room", "").await; + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest { + client: 0, + target: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0xFFFFFFFF, + action: 1, + item_amount: 0, + meseta_amount: 23, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::PlayerChat(PlayerChat::new(0, "\tE/sbank asdf".into()))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest { + client: 0, + target: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0xFFFFFFFF, + action: 1, + item_amount: 0, + meseta_amount: 55, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::PlayerChat(PlayerChat::new(0, "\tE/sbank qwer".into()))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest { + client: 0, + target: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0xFFFFFFFF, + action: 1, + item_amount: 0, + meseta_amount: 32, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::PlayerChat(PlayerChat::new(0, "\tE/bank".into()))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest { + client: 0, + target: 0, + unknown: 0, + })))).await.unwrap(); + + ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + client: 0, + target: 0, + item_id: 0xFFFFFFFF, + action: 1, + item_amount: 0, + meseta_amount: 5, + unknown: 0, + })))).await.unwrap(); + + let meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); + let bank_meseta1 = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Character).await.unwrap(); + let bank_meseta2 = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Shared(item::BankName("asdf".into()))).await.unwrap(); + let bank_meseta3 = entity_gateway.get_bank_meseta(&char1.id, &item::BankIdentifier::Shared(item::BankName("qwer".into()))).await.unwrap(); + assert_eq!(meseta.0, 23+55+32+5); + assert_eq!(bank_meseta1.0, 300-23-5); + assert_eq!(bank_meseta2.0, 300-55); + assert_eq!(bank_meseta3.0, 300-32); +} diff --git a/tests/test_item_id.rs b/tests/test_item_id.rs index f8f165f..f8ba644 100644 --- a/tests/test_item_id.rs +++ b/tests/test_item_id.rs @@ -330,7 +330,7 @@ async fn test_depositing_a_full_stack_then_withdrawing_part() { ), }).await.unwrap()); } - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankIdentifier::Character).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone())