928 lines
39 KiB
Rust
928 lines
39 KiB
Rust
// this lint is currently bugged and suggests incorrect code https://github.com/rust-lang/rust-clippy/issues/9123
|
|
#![allow(clippy::explicit_auto_deref)]
|
|
|
|
use std::convert::{From, TryFrom, Into};
|
|
use futures::future::{Future, BoxFuture};
|
|
use futures::stream::{StreamExt, FuturesOrdered};
|
|
use async_std::sync::{Arc, Mutex};
|
|
use libpso::character::guildcard;
|
|
use crate::entity::account::*;
|
|
use crate::entity::character::*;
|
|
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError};
|
|
use crate::entity::item::*;
|
|
use super::models::*;
|
|
|
|
use sqlx::postgres::PgPoolOptions;
|
|
use sqlx::Connection;
|
|
|
|
|
|
mod embedded {
|
|
use refinery::embed_migrations;
|
|
embed_migrations!("src/entity/gateway/postgres/migrations");
|
|
}
|
|
|
|
|
|
#[derive(Clone)]
|
|
pub struct PostgresTransaction<'t> {
|
|
pgtransaction: Arc<Mutex<sqlx::Transaction<'t, sqlx::Postgres>>>,
|
|
}
|
|
|
|
|
|
#[async_trait::async_trait]
|
|
impl<'t> EntityGatewayTransaction for PostgresTransaction<'t> {
|
|
type ParentGateway = PostgresTransaction<'t>;
|
|
|
|
fn gateway(&mut self) -> &mut Self::ParentGateway {
|
|
self
|
|
}
|
|
|
|
async fn commit(self) -> Result<(), GatewayError> {
|
|
Arc::try_unwrap(self.pgtransaction)
|
|
.unwrap()
|
|
.into_inner()
|
|
.commit()
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Clone)]
|
|
pub struct PostgresGateway {
|
|
pool: sqlx::Pool<sqlx::Postgres>,
|
|
}
|
|
|
|
impl PostgresGateway {
|
|
pub fn new(host: &str, dbname: &str, username: &str, password: &str) -> PostgresGateway {
|
|
let mut conn = refinery::config::Config::new(refinery::config::ConfigDbType::Postgres)
|
|
.set_db_host(host)
|
|
.set_db_user(username)
|
|
.set_db_pass(password)
|
|
.set_db_name(dbname);
|
|
embedded::migrations::runner().run(&mut conn).unwrap();
|
|
|
|
let pool = async_std::task::block_on(async move {
|
|
PgPoolOptions::new()
|
|
.max_connections(5)
|
|
.connect(&format!("postgresql://{username}:{password}@{host}:5432/{dbname}")).await.unwrap()
|
|
});
|
|
|
|
PostgresGateway {
|
|
pool,
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// TODO: remove unwraps, return Result
|
|
async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntity) -> ItemEntity
|
|
{
|
|
let ItemEntity {id, item} = item;
|
|
|
|
let item = match item {
|
|
ItemDetail::Weapon(mut weapon) => {
|
|
let q = r#"select weapon, modifier
|
|
from weapon_modifier
|
|
where weapon = $1
|
|
order by created_at"#;
|
|
let weapon_modifiers = sqlx::query_as::<_, PgWeaponModifier>(q)
|
|
.bind(id.0 as i32)
|
|
.fetch(conn);
|
|
|
|
weapon_modifiers.for_each(|modifier| async move {
|
|
if let Ok(modifier) = modifier {
|
|
weapon.apply_modifier(&modifier.modifier);
|
|
}
|
|
}).await;
|
|
|
|
ItemDetail::Weapon(weapon)
|
|
},
|
|
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)
|
|
left join item as item2 on item2.id = cast (modifier ->> 'MagCell' as integer)
|
|
where mag = $1 order by created_at"#;
|
|
let mag_modifiers = sqlx::query_as::<_, PgMagModifierWithParameters>(q)
|
|
.bind(id.0 as i32)
|
|
.fetch(conn);
|
|
|
|
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 {
|
|
mag::MagModifier::FeedMag{..} => {
|
|
mag.feed(feed.unwrap().tool)
|
|
},
|
|
mag::MagModifier::BankMag => {
|
|
mag.bank()
|
|
},
|
|
mag::MagModifier::MagCell(_) => {
|
|
mag.apply_mag_cell(mag::MagCell::try_from(Into::<tool::Tool>::into(cell.unwrap().0).tool).unwrap()).unwrap()
|
|
},
|
|
mag::MagModifier::OwnerChange(class, section_id) => {
|
|
mag.change_owner(class, section_id)
|
|
},
|
|
}
|
|
|
|
mag
|
|
}).await;
|
|
|
|
ItemDetail::Mag(mag)
|
|
},
|
|
item => item
|
|
};
|
|
|
|
ItemEntity {
|
|
id,
|
|
item,
|
|
}
|
|
}
|
|
|
|
async fn fetch_item<T>(conn: &mut sqlx::PgConnection, item: PgInventoryItemEntity, individual: fn(ItemEntity) -> T, stacked: fn(Vec<ItemEntity>) -> T) -> Result<T, GatewayError>
|
|
{
|
|
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<UserAccountEntity, GatewayError>
|
|
{
|
|
let new_user = sqlx::query_as::<_, PgUserAccount>("insert into user_accounts (email, username, password, activated) values ($1, $2, $3, $4) returning *;")
|
|
.bind(user.email)
|
|
.bind(user.username)
|
|
.bind(user.password)
|
|
.bind(user.activated)
|
|
.fetch_one(conn).await?;
|
|
Ok(new_user.into())
|
|
}
|
|
|
|
async fn get_user_by_id(conn: &mut sqlx::PgConnection, id: UserAccountId) -> Result<UserAccountEntity, GatewayError>
|
|
{
|
|
let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where id = $1")
|
|
.bind(id.0)
|
|
.fetch_one(conn).await?;
|
|
Ok(user.into())
|
|
}
|
|
|
|
async fn get_user_by_name(conn: &mut sqlx::PgConnection, username: String) -> Result<UserAccountEntity, GatewayError>
|
|
{
|
|
let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where username = $1")
|
|
.bind(username)
|
|
.fetch_one(conn).await?;
|
|
Ok(user.into())
|
|
}
|
|
|
|
|
|
async fn save_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<(), GatewayError>
|
|
{
|
|
sqlx::query("UPDATE user_accounts set username=$1, password=$2, banned=$3, muted=$4, flags=$5 where id=$6")
|
|
.bind(&user.username)
|
|
.bind(&user.password)
|
|
.bind(user.banned_until)
|
|
.bind(user.muted_until)
|
|
.bind(user.flags)
|
|
.bind(user.id.0)
|
|
.execute(conn).await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn create_user_settings(conn: &mut sqlx::PgConnection, settings: NewUserSettingsEntity) -> Result<UserSettingsEntity, GatewayError>
|
|
{
|
|
let new_settings = sqlx::query_as::<_, PgUserSettings>("insert into user_settings (user_account, blocked_users, key_config, joystick_config, option_flags, shortcuts, symbol_chats, team_name)
|
|
values ($1, $2, $3, $4, $5, $6, $7, $8) returning *;")
|
|
.bind(settings.user_id.0)
|
|
.bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
|
|
.bind(settings.settings.keyboard_config.to_vec())
|
|
.bind(settings.settings.gamepad_config.to_vec())
|
|
.bind(settings.settings.option_flags as i32)
|
|
.bind(settings.settings.shortcuts.to_vec())
|
|
.bind(settings.settings.symbol_chats.to_vec())
|
|
.bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
|
|
.fetch_one(conn).await?;
|
|
Ok(new_settings.into())
|
|
}
|
|
|
|
async fn get_user_settings_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError>
|
|
{
|
|
let settings = sqlx::query_as::<_, PgUserSettings>("select * from user_settings where user_account = $1")
|
|
.bind(user.id.0)
|
|
.fetch_one(conn).await?;
|
|
Ok(settings.into())
|
|
}
|
|
|
|
async fn save_user_settings(conn: &mut sqlx::PgConnection, settings: &UserSettingsEntity) -> Result<(), GatewayError>
|
|
{
|
|
sqlx::query("update user_settings set blocked_users=$1, key_config=$2, joystick_config=$3, option_flags=$4, shortcuts=$5, symbol_chats=$6, team_name=$7 where id=$8")
|
|
.bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
|
|
.bind(&settings.settings.keyboard_config.to_vec())
|
|
.bind(&settings.settings.gamepad_config.to_vec())
|
|
.bind(settings.settings.option_flags)
|
|
.bind(&settings.settings.shortcuts.to_vec())
|
|
.bind(&settings.settings.symbol_chats.to_vec())
|
|
.bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
|
|
.bind(settings.id.0)
|
|
.execute(conn).await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError>
|
|
{
|
|
let q = r#"insert into player_character
|
|
(user_account, slot, name, exp, class,
|
|
section_id, costume, skin, face, head,
|
|
hair, hair_r, hair_g, hair_b, prop_x,
|
|
prop_y, techs, config, infoboard, guildcard,
|
|
power, mind, def, evade, luck,
|
|
hp, tp, tech_menu, option_flags, playtime)
|
|
values
|
|
($1, $2, $3, $4, $5,
|
|
$6, $7, $8, $9, $10,
|
|
$11, $12, $13, $14, $15,
|
|
$16, $17, $18, $19, $20,
|
|
$21, $22, $23, $24, $25,
|
|
$26, $27, $28, $29, $30)
|
|
returning *;"#;
|
|
let character = sqlx::query_as::<_, PgCharacter>(q)
|
|
.bind(char.user_id.0)
|
|
.bind(char.slot as i16)
|
|
.bind(char.name)
|
|
.bind(char.exp as i32)
|
|
.bind(char.char_class.to_string())
|
|
.bind(char.section_id.to_string())
|
|
.bind(char.appearance.costume as i16)
|
|
.bind(char.appearance.skin as i16)
|
|
.bind(char.appearance.face as i16)
|
|
.bind(char.appearance.head as i16)
|
|
.bind(char.appearance.hair as i16)
|
|
.bind(char.appearance.hair_r as i16)
|
|
.bind(char.appearance.hair_g as i16)
|
|
.bind(char.appearance.hair_b as i16)
|
|
.bind(char.appearance.prop_x)
|
|
.bind(char.appearance.prop_y)
|
|
.bind(&char.techs.as_bytes().to_vec())
|
|
.bind(&char.config.as_bytes().to_vec())
|
|
.bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0)))
|
|
.bind(char.guildcard.description)
|
|
.bind(char.materials.power as i16)
|
|
.bind(char.materials.mind as i16)
|
|
.bind(char.materials.def as i16)
|
|
.bind(char.materials.evade as i16)
|
|
.bind(char.materials.luck as i16)
|
|
.bind(char.materials.hp as i16)
|
|
.bind(char.materials.tp as i16)
|
|
.bind(char.tech_menu.tech_menu.to_vec())
|
|
.bind(char.option_flags as i32)
|
|
.bind(0)
|
|
.fetch_one(conn).await?;
|
|
|
|
Ok(character.into())
|
|
}
|
|
|
|
async fn get_characters_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError>
|
|
{
|
|
let stream = sqlx::query_as::<_, PgCharacter>("select * from player_character where user_account = $1 and slot < 4 order by created_at;")
|
|
.bind(user.id.0)
|
|
.fetch(conn);
|
|
|
|
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())
|
|
}
|
|
acc
|
|
}).await)
|
|
}
|
|
|
|
async fn save_character(conn: &mut sqlx::PgConnection, char: &CharacterEntity) -> Result<(), GatewayError>
|
|
{
|
|
let q = r#"update player_character set
|
|
user_account=$1, slot=$2, name=$3, exp=$4, class=$5, section_id=$6, costume=$7, skin=$8, face=$9, head=$10, hair=$11, hair_r=$12,
|
|
hair_g=$13, hair_b=$14, prop_x=$15, prop_y=$16, techs=$17, config=$18, infoboard=$19, guildcard=$20, power=$21, mind=$22, def=$23,
|
|
evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29, playtime=$30
|
|
where id=$31;"#;
|
|
sqlx::query(q)
|
|
.bind(char.user_id.0) // $1
|
|
.bind(char.slot as i16) // $2
|
|
.bind(&char.name) // $3
|
|
.bind(char.exp as i32) // $4
|
|
.bind(char.char_class.to_string()) // $5
|
|
.bind(char.section_id.to_string()) // $6
|
|
.bind(char.appearance.costume as i16) // $7
|
|
.bind(char.appearance.skin as i16) // $8
|
|
.bind(char.appearance.face as i16) // $9
|
|
.bind(char.appearance.head as i16) // $10
|
|
.bind(char.appearance.hair as i16) // $11
|
|
.bind(char.appearance.hair_r as i16) // $12
|
|
.bind(char.appearance.hair_g as i16) // $13
|
|
.bind(char.appearance.hair_b as i16) // $14
|
|
.bind(char.appearance.prop_x) // $15
|
|
.bind(char.appearance.prop_y) // $16
|
|
.bind(&char.techs.as_bytes().to_vec()) // $17
|
|
.bind(&char.config.as_bytes().to_vec()) // $18
|
|
.bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) // $19
|
|
.bind(&char.guildcard.description) // $20
|
|
.bind(char.materials.power as i16) // $21
|
|
.bind(char.materials.mind as i16) // $22
|
|
.bind(char.materials.def as i16) // $23
|
|
.bind(char.materials.evade as i16) // $24
|
|
.bind(char.materials.luck as i16) // $25
|
|
.bind(char.materials.hp as i16) // $26
|
|
.bind(char.materials.tp as i16) // $27
|
|
.bind(char.tech_menu.tech_menu.to_vec()) // $28
|
|
.bind(char.option_flags as i32) // $29
|
|
.bind(char.playtime as i32) // $30
|
|
.bind(char.id.0 as i32) // $31
|
|
.execute(conn).await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn create_item(conn: &mut sqlx::PgConnection, item: NewItemEntity) -> Result<ItemEntity, GatewayError>
|
|
{
|
|
let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;")
|
|
.bind(sqlx::types::Json(PgItemDetail::from(item.item)))
|
|
.fetch_one(conn).await?;
|
|
|
|
Ok(ItemEntity {
|
|
id: ItemEntityId(new_item.id as u32),
|
|
item: new_item.item.0.into(),
|
|
})
|
|
}
|
|
|
|
async fn add_item_note(conn: &mut sqlx::PgConnection, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError>
|
|
{
|
|
sqlx::query("insert into item_note(item, note) values ($1, $2)")
|
|
.bind(item_id.0)
|
|
.bind(sqlx::types::Json(PgItemNoteDetail::from(item_note)))
|
|
.execute(conn).await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn feed_mag(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError>
|
|
{
|
|
sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);")
|
|
.bind(mag_item_id.0)
|
|
.bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::FeedMag{food: *tool_item_id})))
|
|
.execute(conn).await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn change_mag_owner(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError>
|
|
{
|
|
sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);")
|
|
.bind(mag_item_id.0)
|
|
.bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::OwnerChange(character.char_class, character.section_id))))
|
|
.execute(conn).await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn use_mag_cell(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError>
|
|
{
|
|
sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);")
|
|
.bind(mag_item_id.0)
|
|
.bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::MagCell(*mag_cell_id))))
|
|
.execute(conn).await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn add_weapon_modifier(conn: &mut sqlx::PgConnection, item_id: &ItemEntityId, modifier: &weapon::WeaponModifier) -> Result<(), GatewayError>
|
|
{
|
|
sqlx::query("insert into weapon_modifier (weapon, modifier) values ($1, $2);")
|
|
.bind(item_id.0)
|
|
.bind(sqlx::types::Json(modifier))
|
|
.execute(conn).await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn get_character_inventory(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError>
|
|
{
|
|
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?;
|
|
|
|
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
|
|
}
|
|
})
|
|
.collect::<FuturesOrdered<_>>()
|
|
.collect::<Vec<_>>()
|
|
.await
|
|
.into_iter()
|
|
.collect::<Result<Vec<_>, _>>()?))
|
|
}
|
|
|
|
async fn get_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result<BankEntity, GatewayError>
|
|
{
|
|
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(
|
|
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::<FuturesOrdered<_>>()
|
|
.collect::<Vec<_>>()
|
|
.await
|
|
.into_iter()
|
|
.collect::<Result<Vec<_>, _>>()?))
|
|
}
|
|
|
|
async fn set_character_inventory(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError>
|
|
{
|
|
let inventory = inventory.items.iter()
|
|
.map(|item| {
|
|
match item {
|
|
InventoryItemEntity::Individual(item) => {
|
|
PgInventoryItemEntity::Individual(item.id.0 as i32)
|
|
},
|
|
InventoryItemEntity::Stacked(items) => {
|
|
PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect())
|
|
},
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2")
|
|
.bind(char_id.0)
|
|
.bind(sqlx::types::Json(inventory))
|
|
.execute(conn)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
|
|
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 {
|
|
BankItemEntity::Individual(item) => {
|
|
PgInventoryItemEntity::Individual(item.id.0 as i32)
|
|
},
|
|
BankItemEntity::Stacked(items) => {
|
|
PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect())
|
|
},
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
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(())
|
|
}
|
|
|
|
async fn get_character_equips(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result<EquippedEntity, GatewayError>
|
|
{
|
|
let equips = sqlx::query_as::<_, PgEquipped>("select * from equipped where pchar = $1")
|
|
.bind(char_id.0)
|
|
.fetch_one(conn)
|
|
.await?;
|
|
|
|
Ok(equips.into())
|
|
}
|
|
|
|
async fn set_character_equips(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError>
|
|
{
|
|
sqlx::query(r#"insert into equipped (pchar, weapon, armor, shield, unit0, unit1, unit2, unit3, mag) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#)
|
|
.bind(char_id.0)
|
|
.bind(equips.weapon.map(|i| i.0 as i32))
|
|
.bind(equips.armor.map(|i| i.0 as i32))
|
|
.bind(equips.shield.map(|i| i.0 as i32))
|
|
.bind(equips.unit[0].map(|i| i.0 as i32))
|
|
.bind(equips.unit[1].map(|i| i.0 as i32))
|
|
.bind(equips.unit[2].map(|i| i.0 as i32))
|
|
.bind(equips.unit[3].map(|i| i.0 as i32))
|
|
.bind(equips.mag.map(|i| i.0 as i32))
|
|
.execute(conn)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
|
|
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")
|
|
.bind(char_id.0)
|
|
.bind(meseta.0 as i32)
|
|
.execute(conn)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn get_character_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result<Meseta, GatewayError>
|
|
{
|
|
#[derive(sqlx::FromRow)]
|
|
struct PgMeseta(i32);
|
|
let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where pchar = $1"#)
|
|
.bind(char_id.0)
|
|
.fetch_one(conn)
|
|
.await?;
|
|
Ok(Meseta(meseta.0 as u32))
|
|
}
|
|
|
|
async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier, meseta: Meseta) -> Result<(), GatewayError>
|
|
{
|
|
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_identifier: &BankIdentifier) -> Result<Meseta, GatewayError>
|
|
{
|
|
#[derive(sqlx::FromRow)]
|
|
struct PgMeseta(i32);
|
|
|
|
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))
|
|
}
|
|
|
|
async fn create_trade(conn: &mut sqlx::PgConnection, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result<TradeEntity, GatewayError>
|
|
{
|
|
let trade = sqlx::query_as::<_, PgTradeEntity>(r#"insert into trades (character1, character2) values ($1, $2) returning *;"#)
|
|
.bind(char_id1.0)
|
|
.bind(char_id2.0)
|
|
.fetch_one(conn)
|
|
.await?;
|
|
Ok(trade.into())
|
|
}
|
|
|
|
async fn set_character_playtime(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError>
|
|
{
|
|
sqlx::query(r#"update player_character set playtime=$2 where id=$1;"#)
|
|
.bind(char_id.0)
|
|
.bind(playtime)
|
|
.execute(conn)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl EntityGateway for PostgresGateway {
|
|
type Transaction<'t> = PostgresTransaction<'t> where Self: 't;
|
|
|
|
fn with_transaction<'a, F, Fut, R>(&'a mut self, func: F) -> BoxFuture<'a, Result<R, anyhow::Error>>
|
|
where
|
|
Fut: Future<Output = Result<(Self::Transaction<'a>, R), anyhow::Error>> + Send + 'a,
|
|
F: FnOnce(Self::Transaction<'a>) -> Fut + Send + 'a,
|
|
R: Send,
|
|
{
|
|
Box::pin(async move {
|
|
let transaction = PostgresTransaction {
|
|
pgtransaction: Arc::new(Mutex::new(self.pool.begin().await?))
|
|
};
|
|
let (transaction, result) = func(transaction).await?;
|
|
transaction.commit().await?;
|
|
Ok(result)
|
|
})
|
|
}
|
|
|
|
async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
|
|
create_user(&mut *self.pool.acquire().await?, user).await
|
|
}
|
|
|
|
async fn get_user_by_id(&mut self, id: UserAccountId) -> Result<UserAccountEntity, GatewayError> {
|
|
get_user_by_id(&mut *self.pool.acquire().await?, id).await
|
|
}
|
|
|
|
async fn get_user_by_name(&mut self, username: String) -> Result<UserAccountEntity, GatewayError> {
|
|
get_user_by_name(&mut *self.pool.acquire().await?, username).await
|
|
}
|
|
|
|
async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> {
|
|
save_user(&mut *self.pool.acquire().await?, user).await
|
|
}
|
|
|
|
async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result<UserSettingsEntity, GatewayError> {
|
|
create_user_settings(&mut *self.pool.acquire().await?, settings).await
|
|
}
|
|
|
|
async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> {
|
|
get_user_settings_by_user(&mut *self.pool.acquire().await?, user).await
|
|
}
|
|
|
|
async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> {
|
|
save_user_settings(&mut *self.pool.acquire().await?, settings).await
|
|
}
|
|
|
|
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
|
create_character(&mut *self.pool.acquire().await?, char).await
|
|
}
|
|
|
|
async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError> {
|
|
get_characters_by_user(&mut *self.pool.acquire().await?, user).await
|
|
}
|
|
|
|
async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> {
|
|
save_character(&mut *self.pool.acquire().await?, char).await
|
|
}
|
|
|
|
async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> {
|
|
Ok(GuildCardDataEntity {
|
|
id: GuildCardDataId(0),
|
|
user_id: user.id,
|
|
guildcard: guildcard::GuildCardData::default(),
|
|
})
|
|
}
|
|
|
|
async fn create_item(&mut self, item: NewItemEntity) -> Result<ItemEntity, GatewayError> {
|
|
create_item(&mut *self.pool.acquire().await?, item).await
|
|
}
|
|
|
|
async fn add_item_note(&mut self, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> {
|
|
add_item_note(&mut *self.pool.acquire().await?, item_id, item_note).await
|
|
}
|
|
|
|
async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> {
|
|
feed_mag(&mut *self.pool.acquire().await?, mag_item_id, tool_item_id).await
|
|
}
|
|
|
|
async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> {
|
|
change_mag_owner(&mut *self.pool.acquire().await?, mag_item_id, character).await
|
|
}
|
|
|
|
async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> {
|
|
use_mag_cell(&mut *self.pool.acquire().await?, mag_item_id, mag_cell_id).await
|
|
}
|
|
|
|
async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: &weapon::WeaponModifier) -> Result<(), GatewayError> {
|
|
add_weapon_modifier(&mut *self.pool.acquire().await?, item_id, modifier).await
|
|
}
|
|
|
|
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
|
|
get_character_inventory(&mut *self.pool.acquire().await?, char_id).await
|
|
}
|
|
|
|
async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result<BankEntity, GatewayError> {
|
|
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_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<EquippedEntity, GatewayError> {
|
|
get_character_equips(&mut *self.pool.acquire().await?, char_id).await
|
|
}
|
|
|
|
async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> {
|
|
set_character_equips(&mut *self.pool.acquire().await?, char_id, equips).await
|
|
}
|
|
|
|
async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> {
|
|
set_character_meseta(&mut *self.pool.acquire().await?, char_id, meseta).await
|
|
}
|
|
|
|
async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result<Meseta, GatewayError> {
|
|
get_character_meseta(&mut *self.pool.acquire().await?, char_id).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_identifier: &BankIdentifier) -> Result<Meseta, GatewayError> {
|
|
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<TradeEntity, GatewayError> {
|
|
create_trade(&mut *self.pool.acquire().await?, char_id1, char_id2).await
|
|
}
|
|
|
|
async fn set_character_playtime(&mut self, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError> {
|
|
set_character_playtime(&mut *self.pool.acquire().await?, char_id, playtime).await
|
|
}
|
|
}
|
|
|
|
|
|
#[async_trait::async_trait]
|
|
impl<'c> EntityGateway for PostgresTransaction<'c> {
|
|
type Transaction<'t> = PostgresTransaction<'c> where Self: 't;
|
|
|
|
async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
|
|
create_user(&mut *self.pgtransaction.lock().await, user).await
|
|
}
|
|
|
|
async fn get_user_by_id(&mut self, id: UserAccountId) -> Result<UserAccountEntity, GatewayError> {
|
|
get_user_by_id(&mut *self.pgtransaction.lock().await, id).await
|
|
}
|
|
|
|
async fn get_user_by_name(&mut self, username: String) -> Result<UserAccountEntity, GatewayError> {
|
|
get_user_by_name(&mut *self.pgtransaction.lock().await, username).await
|
|
}
|
|
|
|
async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> {
|
|
save_user(&mut *self.pgtransaction.lock().await, user).await
|
|
}
|
|
|
|
async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result<UserSettingsEntity, GatewayError> {
|
|
create_user_settings(&mut *self.pgtransaction.lock().await, settings).await
|
|
}
|
|
|
|
async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> {
|
|
get_user_settings_by_user(&mut *self.pgtransaction.lock().await, user).await
|
|
}
|
|
|
|
async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> {
|
|
save_user_settings(&mut *self.pgtransaction.lock().await, settings).await
|
|
}
|
|
|
|
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
|
create_character(&mut *self.pgtransaction.lock().await, char).await
|
|
}
|
|
|
|
async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError> {
|
|
get_characters_by_user(&mut *self.pgtransaction.lock().await, user).await
|
|
}
|
|
|
|
async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> {
|
|
save_character(&mut *self.pgtransaction.lock().await, char).await
|
|
}
|
|
|
|
async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> {
|
|
Ok(GuildCardDataEntity {
|
|
id: GuildCardDataId(0),
|
|
user_id: user.id,
|
|
guildcard: guildcard::GuildCardData::default(),
|
|
})
|
|
}
|
|
|
|
async fn create_item(&mut self, item: NewItemEntity) -> Result<ItemEntity, GatewayError> {
|
|
create_item(&mut *self.pgtransaction.lock().await, item).await
|
|
}
|
|
|
|
async fn add_item_note(&mut self, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> {
|
|
add_item_note(&mut *self.pgtransaction.lock().await, item_id, item_note).await
|
|
}
|
|
|
|
async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> {
|
|
feed_mag(&mut *self.pgtransaction.lock().await, mag_item_id, tool_item_id).await
|
|
}
|
|
|
|
async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> {
|
|
change_mag_owner(&mut *self.pgtransaction.lock().await, mag_item_id, character).await
|
|
}
|
|
|
|
async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> {
|
|
use_mag_cell(&mut *self.pgtransaction.lock().await, mag_item_id, mag_cell_id).await
|
|
}
|
|
|
|
async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: &weapon::WeaponModifier) -> Result<(), GatewayError> {
|
|
add_weapon_modifier(&mut *self.pgtransaction.lock().await, item_id, modifier).await
|
|
}
|
|
|
|
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
|
|
get_character_inventory(&mut *self.pgtransaction.lock().await, char_id).await
|
|
}
|
|
|
|
async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result<BankEntity, GatewayError> {
|
|
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_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<EquippedEntity, GatewayError> {
|
|
get_character_equips(&mut *self.pgtransaction.lock().await, char_id).await
|
|
}
|
|
|
|
async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> {
|
|
set_character_equips(&mut *self.pgtransaction.lock().await, char_id, equips).await
|
|
}
|
|
|
|
async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> {
|
|
set_character_meseta(&mut *self.pgtransaction.lock().await, char_id, meseta).await
|
|
}
|
|
|
|
async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result<Meseta, GatewayError> {
|
|
get_character_meseta(&mut *self.pgtransaction.lock().await, char_id).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_identifier: &BankIdentifier) -> Result<Meseta, GatewayError> {
|
|
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<TradeEntity, GatewayError> {
|
|
create_trade(&mut *self.pgtransaction.lock().await, char_id1, char_id2).await
|
|
}
|
|
|
|
async fn set_character_playtime(&mut self, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError> {
|
|
set_character_playtime(&mut *self.pgtransaction.lock().await, char_id, playtime).await
|
|
}
|
|
}
|
|
|