From 4caaee880a8ef6f48e28425850e8b328665d13fb Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 6 Oct 2020 23:42:31 -0600 Subject: [PATCH] keep track of item slots without having 74894794793 inventory locations --- .../postgres/migrations/V0001__initial.sql | 5 + src/entity/gateway/postgres/models.rs | 3 +- src/entity/gateway/postgres/postgres.rs | 128 ++++++++++++++---- 3 files changed, 110 insertions(+), 26 deletions(-) diff --git a/src/entity/gateway/postgres/migrations/V0001__initial.sql b/src/entity/gateway/postgres/migrations/V0001__initial.sql index d3e14a0..16ecbb0 100644 --- a/src/entity/gateway/postgres/migrations/V0001__initial.sql +++ b/src/entity/gateway/postgres/migrations/V0001__initial.sql @@ -74,6 +74,11 @@ create table item_location ( created_at timestamptz default current_timestamp not null ); +create table inventory_slots ( + pchar integer references player_character not null, + items integer[30] /* references item (id) */ +); + create table weapon_modifier ( weapon integer references item (id) not null, modifier jsonb not null, diff --git a/src/entity/gateway/postgres/models.rs b/src/entity/gateway/postgres/models.rs index 3ebb29e..eb2554f 100644 --- a/src/entity/gateway/postgres/models.rs +++ b/src/entity/gateway/postgres/models.rs @@ -182,7 +182,7 @@ impl From for PgSectionId { #[derive(Debug, sqlx::FromRow)] pub struct PgCharacter { - id: i32, + pub id: i32, user_account: i32, pub slot: i16, name: String, @@ -569,6 +569,7 @@ pub struct PgItem { pub enum PgItemLocationDetail { Inventory { character_id: u32, + #[serde(skip_serializing)] slot: usize, equipped: bool, }, diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index a348f12..6a0f623 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -204,6 +204,10 @@ impl EntityGateway for PostgresGateway { .bind(char.meseta as i32) .bind(char.bank_meseta as i32) .fetch_one(&self.pool).await.unwrap(); + + sqlx::query("insert into inventory_slots (pchar) values ($1)") + .bind(character.id) + .execute(&self.pool).await.unwrap(); Some(character.into()) } @@ -273,10 +277,35 @@ impl EntityGateway for PostgresGateway { let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;") .bind(sqlx::types::Json(PgItemDetail::from(item.item))) .fetch_one(&self.pool).await.unwrap(); - let location = sqlx::query_as::<_, PgItemLocation>("insert into item_location (item, location) values ($1, $2) returning *;") - .bind(new_item.id) - .bind(sqlx::types::Json(PgItemLocationDetail::from(item.location))) - .fetch_one(&self.pool).await.unwrap(); + let location = if let ItemLocation::Inventory{character_id, slot, ..} = &item.location { + sqlx::query("insert into item_location (item, location) values ($1, $2)") + .bind(new_item.id) + .bind(sqlx::types::Json(PgItemLocationDetail::from(item.location.clone()))) + .execute(&self.pool).await.unwrap(); + sqlx::query("update inventory_slots set items[$2] = $1 where pchar = $3") + .bind(new_item.id) + .bind(*slot as i32) + .bind(character_id.0 as i32) + .execute(&self.pool).await.unwrap(); + sqlx::query_as::<_, PgItemLocation>(r#"select + item_location.item, + jsonb_set(item_location.location, '{Inventory,slot}', (array_position(inventory_slots.items, item.id))::text::jsonb) as location, + item_location.created_at + from item_location + join item on item.id = item_location.item + join inventory_slots on inventory_slots.pchar = cast (item_location.location -> 'Inventory' ->> 'character_id' as integer) + where item.id = $1 + order by item_location.created_at + limit 1"#) + .bind(new_item.id) + .fetch_one(&self.pool).await.unwrap() + } + else { + sqlx::query_as::<_, PgItemLocation>("insert into item_location (item, location) values ($1, $2) returning *") + .bind(new_item.id) + .bind(sqlx::types::Json(PgItemLocationDetail::from(item.location))) + .fetch_one(&self.pool).await.unwrap() + }; Some(ItemEntity { id: ItemEntityId(new_item.id as u32), item: new_item.item.0.into(), @@ -285,10 +314,30 @@ impl EntityGateway for PostgresGateway { } async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) { - sqlx::query("insert into item_location (item, location) values ($1, $2);") - .bind(item_id.0) - .bind(sqlx::types::Json(PgItemLocationDetail::from(item_location))) - .execute(&self.pool).await.unwrap(); + if let ItemLocation::Inventory{character_id, slot, ..} = &item_location { + sqlx::query("update inventory_slots set items[array_position(items, $1)] = null where pchar = $2 and items[array_position(items, $1)] is not null") + .bind(item_id.0 as i32) + .bind(character_id.0 as i32) + .execute(&self.pool).await.unwrap(); + sqlx::query("update inventory_slots set items[$2] = $1 where pchar = $3") + .bind(item_id.0 as i32) + .bind(*slot as i32) + .bind(character_id.0 as i32) + .execute(&self.pool).await.unwrap(); + sqlx::query(r#"insert into item_location (item, location) + select $1, $2 + where (select jsonb_object_keys(location) from item_location where item=$1 + order by created_at desc limit 1) != 'Inventory'"#) + .bind(item_id.0) + .bind(sqlx::types::Json(PgItemLocationDetail::from(item_location))) + .execute(&self.pool).await.unwrap(); + } + else { + sqlx::query("insert into item_location (item, location) values ($1, $2)") + .bind(item_id.0) + .bind(sqlx::types::Json(PgItemLocationDetail::from(item_location))) + .execute(&self.pool).await.unwrap(); + } } async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) { @@ -315,28 +364,57 @@ impl EntityGateway for PostgresGateway { async fn get_items_by_character(&self, char: &CharacterEntity) -> Vec { let q = r#"select * from ( - select distinct on (item_location.item) item.id, item_location.location, item.item - from item_location join item on item.id = item_location.item + select distinct on (item_location.item) + item.id, + case + when item_location.location -> 'Inventory' is not null then + jsonb_set(item_location.location, '{Inventory,slot}', (array_position(inventory_slots.items, item.id))::text::jsonb), + else + item_location.location + end location, + item.item + from item_location + join item on item.id = item_location.item order by item_location.item, item_location.created_at desc ) as i - where cast (location -> 'Inventory' ->> 'character_id' as integer) = 2 - or cast (location -> 'Bank' ->> 'character_id' as integer) = 2"#; + where cast (location -> 'Inventory' ->> 'character_id' as integer) = $1 + or cast (location -> 'Bank' ->> 'character_id' as integer) = $1"#; + let q = r#"select * from ( + select distinct on (item_location.item) + item.id, + case + when item_location.location -> 'Inventory' is not null then + jsonb_set(item_location.location, '{Inventory,slot}', (array_position(inventory_slots.items, item.id))::text::jsonb) + else + item_location.location + end, + item.item + from item_location + join item on item.id = item_location.item + join inventory_slots on inventory_slots.pchar = $1 + order by item_location.item, item_location.created_at desc + ) as i + where cast (location -> 'Inventory' ->> 'character_id' as integer) = $1 + or cast (location -> 'Bank' ->> 'character_id' as integer) = $1 + "#; let items = sqlx::query_as::<_, PgItemWithLocation>(q) .bind(char.id.0) .fetch(&self.pool); join_all(items - .filter_map(|item: Result| { - let item = item.ok()?; - Some(ItemEntity { - id: ItemEntityId(item.id as u32), - item: item.item.0.into(), - location: item.location.0.into() - }) - }) - .map(|item: ItemEntity| { - self.apply_item_modifications(item) - }) - .collect::>() - .await).await + .filter_map(|item: Result| { + let item = item.ok()?; + Some(ItemEntity { + id: ItemEntityId(item.id as u32), + item: item.item.0.into(), + location: item.location.0.into() + }) + }) + .map(|item: ItemEntity| { + self.apply_item_modifications(item) + }) + .collect::>() + .await + ).await } } +