From fc5d318ac3dedf06a6909c0c3ae17318cc7456d3 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 5 Feb 2023 18:34:04 -0700 Subject: [PATCH] shared banks wow --- Cargo.lock | 4 +- src/entity/gateway/postgres/postgres.rs | 66 +++++++++++++-------- src/ship/chatcommand.rs | 78 +++++++++++++++++++++++-- src/ship/client.rs | 2 - src/ship/items/actions.rs | 6 +- src/ship/ship.rs | 11 +++- 6 files changed, 129 insertions(+), 38 deletions(-) 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/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 735e9e5..9cb8a98 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -418,8 +418,7 @@ 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 { - // this is some degen shit - let conn = Arc::new(Mutex::new(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 **conn.lock().await).await?; @@ -442,8 +441,7 @@ async fn get_character_inventory(conn: &mut sqlx::PgConnection, char_id: &Charac async fn get_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result { - // this is some degen shit - let conn = Arc::new(Mutex::new(conn.begin().await?)); + 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") @@ -451,15 +449,17 @@ async fn get_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEn .fetch_one(&mut **conn.lock().await).await? }, BankIdentifier::Shared(bank_name) => { - todo!(); - /* - sqlx::query_as::<_, PgInventoryEntity>("select shared_bank.* from shared_bank - left join player_character on shared_bank.user = player_character.user - where player_character.id = $1 and bank.name = $2") + 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_one(&mut **conn.lock().await).await? - */ + .fetch_optional(&mut **conn.lock().await) + .await? + .unwrap_or_else(|| PgInventoryEntity { + pchar: char_id.0 as i32, + items: sqlx::types::Json::default(), + }) } }; @@ -519,21 +519,22 @@ async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEn match bank_identifier { BankIdentifier::Character => { - sqlx::query("insert into bank (pchar, items) values ($1, $2) on conflict (pchar, name) do update set items = $2") + 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) => { - // TODO! - /* - sqlx::query("insert into shared_bank (pchar, items) values ($1, $2) on conflict (pchar, name) do update set items = $2") + 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(()) @@ -569,7 +570,7 @@ async fn set_character_equips(conn: &mut sqlx::PgConnection, char_id: &Character async fn set_character_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> { - sqlx::query("insert into character_meseta values ($1, '', $2) on conflict (pchar) do update set meseta = $2") + sqlx::query("insert into character_meseta values ($1, $2) on conflict (pchar) do update set meseta = $2") .bind(char_id.0) .bind(meseta.0 as i32) .execute(conn) @@ -592,14 +593,22 @@ async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntit { match bank_identifier { BankIdentifier::Character => { - sqlx::query("insert into bank_meseta values ($1, '', $2) on conflict pchar do update set meseta = $2") + 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) => { - todo!(); + 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?; } } @@ -611,18 +620,25 @@ async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntit #[derive(sqlx::FromRow)] struct PgMeseta(i32); - match bank_identifier { + let meseta = match bank_identifier { BankIdentifier::Character => { - let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from bank_meseta where pchar = $1"#) + sqlx::query_as::<_, PgMeseta>(r#"select meseta from bank_meseta where pchar = $1"#) .bind(char_id.0) .fetch_one(conn) - .await?; - Ok(Meseta(meseta.0 as u32)) + .await? }, BankIdentifier::Shared(bank_name) => { - todo!(); + 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_else(|| PgMeseta(0)) } - } + }; + Ok(Meseta(meseta.0 as u32)) } async fn create_trade(conn: &mut sqlx::PgConnection, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result diff --git a/src/ship/chatcommand.rs b/src/ship/chatcommand.rs index 0e8bab2..802199d 100644 --- a/src/ship/chatcommand.rs +++ b/src/ship/chatcommand.rs @@ -1,19 +1,89 @@ - 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, T>(id: ClientId, + tokens: T, + entity_gateway: &mut EG, + clients: &Clients, + item_state: &mut ItemState) + -> Result, String> +where + EG: EntityGateway + Clone + 'static, + T: Iterator + 'a, +{ + 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, anyhow::Error>> + -> 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, tokens, &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, + } - None } diff --git a/src/ship/client.rs b/src/ship/client.rs index cebefc9..5495309 100644 --- a/src/ship/client.rs +++ b/src/ship/client.rs @@ -137,7 +137,6 @@ pub struct ClientState { pub tek: Option<(items::ClientItemId, item::weapon::TekSpecialModifier, item::weapon::TekPercentModifier, i32)>, pub character_playtime: chrono::Duration, pub log_on_time: chrono::DateTime, - pub current_bank: item::BankIdentifier, } impl ClientState { @@ -161,7 +160,6 @@ impl ClientState { tek: None, character_playtime, log_on_time: chrono::Utc::now(), - current_bank: item::BankIdentifier::Character, } } diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index ad4f8f9..c807144 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -614,7 +614,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 +632,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/ship.rs b/src/ship/ship.rs index 9464407..0687173 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -291,6 +291,7 @@ pub enum SendShipPacket { CancelTrade(CancelTrade), TradeSuccessful(TradeSuccessful), LobbyEvent(LobbyEvent), + LargeDialog(LargeDialog), } impl SendServerPacket for SendShipPacket { @@ -334,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(), } } } @@ -496,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, @@ -736,7 +738,10 @@ impl ServerState for ShipServerState { RecvShipPacket::PlayerChat(msg) => { match chatcommand::handle_chat_command(id, msg.clone(), &mut self).await { Some(ccmd) => { - 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?;