You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

966 lines
41 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
1 year ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
2 years ago
4 years ago
4 years ago
4 years ago
2 years ago
4 years ago
4 years ago
1 year ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
4 years ago
  1. // this lint is currently bugged and suggests incorrect code https://github.com/rust-lang/rust-clippy/issues/9123
  2. #![allow(clippy::explicit_auto_deref)]
  3. use std::convert::{From, TryFrom, Into};
  4. use futures::future::{Future, BoxFuture};
  5. use futures::stream::{StreamExt, FuturesOrdered};
  6. use async_std::sync::{Arc, Mutex};
  7. use libpso::character::guildcard;
  8. use crate::entity::account::*;
  9. use crate::entity::character::*;
  10. use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError};
  11. use crate::entity::item::*;
  12. use crate::entity::room::*;
  13. use super::models::*;
  14. use sqlx::postgres::PgPoolOptions;
  15. use sqlx::Connection;
  16. mod embedded {
  17. use refinery::embed_migrations;
  18. embed_migrations!("src/entity/gateway/postgres/migrations");
  19. }
  20. #[derive(Clone)]
  21. pub struct PostgresTransaction<'t> {
  22. pgtransaction: Arc<Mutex<sqlx::Transaction<'t, sqlx::Postgres>>>,
  23. }
  24. #[async_trait::async_trait]
  25. impl<'t> EntityGatewayTransaction for PostgresTransaction<'t> {
  26. type ParentGateway = PostgresTransaction<'t>;
  27. fn gateway(&mut self) -> &mut Self::ParentGateway {
  28. self
  29. }
  30. async fn commit(self) -> Result<(), GatewayError> {
  31. Arc::try_unwrap(self.pgtransaction)
  32. .unwrap()
  33. .into_inner()
  34. .commit()
  35. .await?;
  36. Ok(())
  37. }
  38. }
  39. #[derive(Clone)]
  40. pub struct PostgresGateway {
  41. pool: sqlx::Pool<sqlx::Postgres>,
  42. }
  43. impl PostgresGateway {
  44. pub fn new(host: &str, dbname: &str, username: &str, password: &str) -> PostgresGateway {
  45. let mut conn = refinery::config::Config::new(refinery::config::ConfigDbType::Postgres)
  46. .set_db_host(host)
  47. .set_db_user(username)
  48. .set_db_pass(password)
  49. .set_db_name(dbname);
  50. embedded::migrations::runner().run(&mut conn).unwrap();
  51. let pool = async_std::task::block_on(async move {
  52. PgPoolOptions::new()
  53. .max_connections(5)
  54. .connect(&format!("postgresql://{username}:{password}@{host}:5432/{dbname}")).await.unwrap()
  55. });
  56. PostgresGateway {
  57. pool,
  58. }
  59. }
  60. }
  61. // TODO: remove unwraps, return Result
  62. async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntity) -> ItemEntity
  63. {
  64. let ItemEntity {id, item} = item;
  65. let item = match item {
  66. ItemDetail::Weapon(mut weapon) => {
  67. let q = r#"select weapon, modifier
  68. from weapon_modifier
  69. where weapon = $1
  70. order by created_at"#;
  71. let weapon_modifiers = sqlx::query_as::<_, PgWeaponModifier>(q)
  72. .bind(id.0 as i32)
  73. .fetch(conn);
  74. weapon_modifiers.for_each(|modifier| async move {
  75. if let Ok(modifier) = modifier {
  76. weapon.apply_modifier(&modifier.modifier);
  77. }
  78. }).await;
  79. ItemDetail::Weapon(weapon)
  80. },
  81. ItemDetail::Mag(mag) => {
  82. let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell
  83. from mag_modifier
  84. left join item on item.id = cast (modifier ->> 'FeedMag' as integer)
  85. left join item as item2 on item2.id = cast (modifier ->> 'MagCell' as integer)
  86. where mag = $1 order by created_at"#;
  87. let mag_modifiers = sqlx::query_as::<_, PgMagModifierWithParameters>(q)
  88. .bind(id.0 as i32)
  89. .fetch(conn);
  90. let mag = mag_modifiers.fold(mag, |mut mag, modifier| async {
  91. let PgMagModifierWithParameters {modifier, feed, cell, ..} = modifier.unwrap();
  92. let modifier: mag::MagModifier = modifier.0.into();
  93. match modifier {
  94. mag::MagModifier::FeedMag{..} => {
  95. mag.feed(feed.unwrap().tool)
  96. },
  97. mag::MagModifier::BankMag => {
  98. mag.bank()
  99. },
  100. mag::MagModifier::MagCell(_) => {
  101. mag.apply_mag_cell(mag::MagCell::try_from(Into::<tool::Tool>::into(cell.unwrap().0).tool).unwrap()).unwrap()
  102. },
  103. mag::MagModifier::OwnerChange(class, section_id) => {
  104. mag.change_owner(class, section_id)
  105. },
  106. }
  107. mag
  108. }).await;
  109. ItemDetail::Mag(mag)
  110. },
  111. item => item
  112. };
  113. ItemEntity {
  114. id,
  115. item,
  116. }
  117. }
  118. async fn fetch_item<T>(conn: &mut sqlx::PgConnection, item: PgInventoryItemEntity, individual: fn(ItemEntity) -> T, stacked: fn(Vec<ItemEntity>) -> T) -> Result<T, GatewayError>
  119. {
  120. match item {
  121. PgInventoryItemEntity::Individual(item) => {
  122. let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1")
  123. .bind(item)
  124. .fetch_one(&mut *conn).await
  125. .map(|item| item.into())
  126. .map(|item| apply_item_modifications(&mut *conn, item))?
  127. .await;
  128. Ok(individual(entity))
  129. },
  130. PgInventoryItemEntity::Stacked(items) => {
  131. let mut stacked_item = Vec::new();
  132. for s_item in items {
  133. stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1")
  134. .bind(s_item)
  135. .fetch_one(&mut *conn).await
  136. .map(|item| item.into())?)
  137. }
  138. Ok(stacked(stacked_item))
  139. }
  140. }
  141. }
  142. async fn create_user(conn: &mut sqlx::PgConnection, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError>
  143. {
  144. let new_user = sqlx::query_as::<_, PgUserAccount>("insert into user_accounts (email, username, password, activated) values ($1, $2, $3, $4) returning *;")
  145. .bind(user.email)
  146. .bind(user.username)
  147. .bind(user.password)
  148. .bind(user.activated)
  149. .fetch_one(conn).await?;
  150. Ok(new_user.into())
  151. }
  152. async fn get_user_by_id(conn: &mut sqlx::PgConnection, id: UserAccountId) -> Result<UserAccountEntity, GatewayError>
  153. {
  154. let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where id = $1")
  155. .bind(id.0 as i32)
  156. .fetch_one(conn).await?;
  157. Ok(user.into())
  158. }
  159. async fn get_user_by_name(conn: &mut sqlx::PgConnection, username: String) -> Result<UserAccountEntity, GatewayError>
  160. {
  161. let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where username = $1")
  162. .bind(username)
  163. .fetch_one(conn).await?;
  164. Ok(user.into())
  165. }
  166. async fn save_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<(), GatewayError>
  167. {
  168. sqlx::query("UPDATE user_accounts set username=$1, password=$2, banned=$3, muted=$4, flags=$5 where id=$6")
  169. .bind(&user.username)
  170. .bind(&user.password)
  171. .bind(user.banned_until)
  172. .bind(user.muted_until)
  173. .bind(user.flags as i32)
  174. .bind(user.id.0 as i32)
  175. .execute(conn).await?;
  176. Ok(())
  177. }
  178. async fn create_user_settings(conn: &mut sqlx::PgConnection, settings: NewUserSettingsEntity) -> Result<UserSettingsEntity, GatewayError>
  179. {
  180. 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)
  181. values ($1, $2, $3, $4, $5, $6, $7, $8) returning *;")
  182. .bind(settings.user_id.0 as i32)
  183. .bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
  184. .bind(settings.settings.keyboard_config.to_vec())
  185. .bind(settings.settings.gamepad_config.to_vec())
  186. .bind(settings.settings.option_flags as i32)
  187. .bind(settings.settings.shortcuts.to_vec())
  188. .bind(settings.settings.symbol_chats.to_vec())
  189. .bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
  190. .fetch_one(conn).await?;
  191. Ok(new_settings.into())
  192. }
  193. async fn get_user_settings_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError>
  194. {
  195. let settings = sqlx::query_as::<_, PgUserSettings>("select * from user_settings where user_account = $1")
  196. .bind(user.id.0 as i32)
  197. .fetch_one(conn).await?;
  198. Ok(settings.into())
  199. }
  200. async fn save_user_settings(conn: &mut sqlx::PgConnection, settings: &UserSettingsEntity) -> Result<(), GatewayError>
  201. {
  202. 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")
  203. .bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
  204. .bind(&settings.settings.keyboard_config.to_vec())
  205. .bind(&settings.settings.gamepad_config.to_vec())
  206. .bind(settings.settings.option_flags as i32)
  207. .bind(&settings.settings.shortcuts.to_vec())
  208. .bind(&settings.settings.symbol_chats.to_vec())
  209. .bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
  210. .bind(settings.id.0 as i32)
  211. .execute(conn).await?;
  212. Ok(())
  213. }
  214. async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError>
  215. {
  216. let q = r#"insert into player_character
  217. (user_account, slot, name, exp, class,
  218. section_id, costume, skin, face, head,
  219. hair, hair_r, hair_g, hair_b, prop_x,
  220. prop_y, techs, config, infoboard, guildcard,
  221. power, mind, def, evade, luck,
  222. hp, tp, tech_menu, option_flags, playtime)
  223. values
  224. ($1, $2, $3, $4, $5,
  225. $6, $7, $8, $9, $10,
  226. $11, $12, $13, $14, $15,
  227. $16, $17, $18, $19, $20,
  228. $21, $22, $23, $24, $25,
  229. $26, $27, $28, $29, $30)
  230. returning *;"#;
  231. let character = sqlx::query_as::<_, PgCharacter>(q)
  232. .bind(char.user_id.0 as i32)
  233. .bind(char.slot as i16)
  234. .bind(char.name)
  235. .bind(char.exp as i32)
  236. .bind(char.char_class.to_string())
  237. .bind(char.section_id.to_string())
  238. .bind(char.appearance.costume as i16)
  239. .bind(char.appearance.skin as i16)
  240. .bind(char.appearance.face as i16)
  241. .bind(char.appearance.head as i16)
  242. .bind(char.appearance.hair as i16)
  243. .bind(char.appearance.hair_r as i16)
  244. .bind(char.appearance.hair_g as i16)
  245. .bind(char.appearance.hair_b as i16)
  246. .bind(char.appearance.prop_x)
  247. .bind(char.appearance.prop_y)
  248. .bind(&char.techs.as_bytes().to_vec())
  249. .bind(&char.config.as_bytes().to_vec())
  250. .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0)))
  251. .bind(char.guildcard.description)
  252. .bind(char.materials.power as i16)
  253. .bind(char.materials.mind as i16)
  254. .bind(char.materials.def as i16)
  255. .bind(char.materials.evade as i16)
  256. .bind(char.materials.luck as i16)
  257. .bind(char.materials.hp as i16)
  258. .bind(char.materials.tp as i16)
  259. .bind(char.tech_menu.tech_menu.to_vec())
  260. .bind(char.option_flags as i32)
  261. .bind(0)
  262. .fetch_one(conn).await?;
  263. Ok(character.into())
  264. }
  265. async fn get_characters_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError>
  266. {
  267. let stream = sqlx::query_as::<_, PgCharacter>("select * from player_character where user_account = $1 and slot < 4 order by created_at;")
  268. .bind(user.id.0 as i32)
  269. .fetch(conn);
  270. Ok(stream.fold(core::array::from_fn(|_| None), |mut acc, char| async move {
  271. if let Ok(char) = char {
  272. let slot = char.slot as usize;
  273. acc[slot] = Some(char.into())
  274. }
  275. acc
  276. }).await)
  277. }
  278. async fn save_character(conn: &mut sqlx::PgConnection, char: &CharacterEntity) -> Result<(), GatewayError>
  279. {
  280. let q = r#"update player_character set
  281. 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,
  282. 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,
  283. evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29, playtime=$30
  284. where id=$31;"#;
  285. sqlx::query(q)
  286. .bind(char.user_id.0 as i32) // $1
  287. .bind(char.slot as i16) // $2
  288. .bind(&char.name) // $3
  289. .bind(char.exp as i32) // $4
  290. .bind(char.char_class.to_string()) // $5
  291. .bind(char.section_id.to_string()) // $6
  292. .bind(char.appearance.costume as i16) // $7
  293. .bind(char.appearance.skin as i16) // $8
  294. .bind(char.appearance.face as i16) // $9
  295. .bind(char.appearance.head as i16) // $10
  296. .bind(char.appearance.hair as i16) // $11
  297. .bind(char.appearance.hair_r as i16) // $12
  298. .bind(char.appearance.hair_g as i16) // $13
  299. .bind(char.appearance.hair_b as i16) // $14
  300. .bind(char.appearance.prop_x) // $15
  301. .bind(char.appearance.prop_y) // $16
  302. .bind(&char.techs.as_bytes().to_vec()) // $17
  303. .bind(&char.config.as_bytes().to_vec()) // $18
  304. .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) // $19
  305. .bind(&char.guildcard.description) // $20
  306. .bind(char.materials.power as i16) // $21
  307. .bind(char.materials.mind as i16) // $22
  308. .bind(char.materials.def as i16) // $23
  309. .bind(char.materials.evade as i16) // $24
  310. .bind(char.materials.luck as i16) // $25
  311. .bind(char.materials.hp as i16) // $26
  312. .bind(char.materials.tp as i16) // $27
  313. .bind(char.tech_menu.tech_menu.to_vec()) // $28
  314. .bind(char.option_flags as i32) // $29
  315. .bind(char.playtime as i32) // $30
  316. .bind(char.id.0 as i32) // $31
  317. .execute(conn).await?;
  318. Ok(())
  319. }
  320. async fn create_item(conn: &mut sqlx::PgConnection, item: NewItemEntity) -> Result<ItemEntity, GatewayError>
  321. {
  322. let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;")
  323. .bind(sqlx::types::Json(PgItemDetail::from(item.item)))
  324. .fetch_one(conn).await?;
  325. Ok(ItemEntity {
  326. id: ItemEntityId(new_item.id as u32),
  327. item: new_item.item.0.into(),
  328. })
  329. }
  330. async fn add_item_note(conn: &mut sqlx::PgConnection, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError>
  331. {
  332. sqlx::query("insert into item_note(item, note) values ($1, $2)")
  333. .bind(item_id.0 as i32)
  334. .bind(sqlx::types::Json(PgItemNoteDetail::from(item_note)))
  335. .execute(conn).await?;
  336. Ok(())
  337. }
  338. async fn feed_mag(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError>
  339. {
  340. sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);")
  341. .bind(mag_item_id.0 as i32)
  342. .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::FeedMag{food: *tool_item_id})))
  343. .execute(conn).await?;
  344. Ok(())
  345. }
  346. async fn change_mag_owner(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError>
  347. {
  348. sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);")
  349. .bind(mag_item_id.0 as i32)
  350. .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::OwnerChange(character.char_class, character.section_id))))
  351. .execute(conn).await?;
  352. Ok(())
  353. }
  354. async fn use_mag_cell(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError>
  355. {
  356. sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);")
  357. .bind(mag_item_id.0 as i32)
  358. .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::MagCell(*mag_cell_id))))
  359. .execute(conn).await?;
  360. Ok(())
  361. }
  362. async fn add_weapon_modifier(conn: &mut sqlx::PgConnection, item_id: &ItemEntityId, modifier: &weapon::WeaponModifier) -> Result<(), GatewayError>
  363. {
  364. sqlx::query("insert into weapon_modifier (weapon, modifier) values ($1, $2);")
  365. .bind(item_id.0 as i32)
  366. .bind(sqlx::types::Json(modifier))
  367. .execute(conn).await?;
  368. Ok(())
  369. }
  370. async fn get_character_inventory(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError>
  371. {
  372. let conn = Arc::new(Mutex::new(conn.begin().await?)); // this is some degen shit
  373. let inventory = sqlx::query_as::<_, PgInventoryEntity>("select * from inventory where pchar = $1")
  374. .bind(char_id.0 as i32)
  375. .fetch_one(&mut **conn.lock().await).await?;
  376. Ok(InventoryEntity::new(
  377. inventory.items.0
  378. .into_iter()
  379. .map(move |item| {
  380. let conn = conn.clone();
  381. async move {
  382. fetch_item(&mut **conn.lock().await, item, InventoryItemEntity::Individual, InventoryItemEntity::Stacked).await
  383. }
  384. })
  385. .collect::<FuturesOrdered<_>>()
  386. .collect::<Vec<_>>()
  387. .await
  388. .into_iter()
  389. .collect::<Result<Vec<_>, _>>()?))
  390. }
  391. async fn get_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result<BankEntity, GatewayError>
  392. {
  393. let conn = Arc::new(Mutex::new(conn.begin().await?)); // this is some degen shit
  394. let bank = match bank_identifier {
  395. BankIdentifier::Character => {
  396. sqlx::query_as::<_, PgInventoryEntity>("select * from bank where pchar = $1")
  397. .bind(char_id.0 as i32)
  398. .fetch_one(&mut **conn.lock().await).await?
  399. },
  400. BankIdentifier::Shared(bank_name) => {
  401. sqlx::query_as::<_, PgInventoryEntity>("select player_character.id as pchar, shared_bank.items as items from shared_bank
  402. join player_character on shared_bank.user_account = player_character.user_account
  403. where player_character.id = $1 and shared_bank.name = $2")
  404. .bind(char_id.0 as i32)
  405. .bind(&bank_name.0)
  406. .fetch_optional(&mut **conn.lock().await)
  407. .await?
  408. .unwrap_or_else(|| PgInventoryEntity {
  409. pchar: char_id.0 as i32,
  410. items: sqlx::types::Json::default(),
  411. })
  412. }
  413. };
  414. Ok(BankEntity::new(
  415. bank.items.0
  416. .into_iter()
  417. .map(move |item| {
  418. let conn = conn.clone();
  419. async move {
  420. fetch_item(&mut **conn.lock().await, item, BankItemEntity::Individual, BankItemEntity::Stacked).await
  421. }
  422. })
  423. .collect::<FuturesOrdered<_>>()
  424. .collect::<Vec<_>>()
  425. .await
  426. .into_iter()
  427. .collect::<Result<Vec<_>, _>>()?))
  428. }
  429. async fn set_character_inventory(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError>
  430. {
  431. let inventory = inventory.items.iter()
  432. .map(|item| {
  433. match item {
  434. InventoryItemEntity::Individual(item) => {
  435. PgInventoryItemEntity::Individual(item.id.0 as i32)
  436. },
  437. InventoryItemEntity::Stacked(items) => {
  438. PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect())
  439. },
  440. }
  441. })
  442. .collect::<Vec<_>>();
  443. sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2")
  444. .bind(char_id.0 as i32)
  445. .bind(sqlx::types::Json(inventory))
  446. .execute(conn)
  447. .await?;
  448. Ok(())
  449. }
  450. async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankEntity, bank_identifier: &BankIdentifier) -> Result<(), GatewayError> {
  451. let bank = bank.items.iter()
  452. .map(|item| {
  453. match item {
  454. BankItemEntity::Individual(item) => {
  455. PgInventoryItemEntity::Individual(item.id.0 as i32)
  456. },
  457. BankItemEntity::Stacked(items) => {
  458. PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect())
  459. },
  460. }
  461. })
  462. .collect::<Vec<_>>();
  463. match bank_identifier {
  464. BankIdentifier::Character => {
  465. sqlx::query("insert into bank (pchar, items, name) values ($1, $2, '') on conflict (pchar, name) do update set items = $2")
  466. .bind(char_id.0 as i32)
  467. .bind(sqlx::types::Json(bank))
  468. .execute(conn)
  469. .await?;
  470. },
  471. BankIdentifier::Shared(bank_name) => {
  472. sqlx::query("insert into shared_bank (user_account, items, name)
  473. select player_character.user_account, $2, $3 from player_character
  474. where player_character.id = $1
  475. on conflict (user_account, name) do update set items = $2;")
  476. .bind(char_id.0 as i32)
  477. .bind(sqlx::types::Json(bank))
  478. .bind(&bank_name.0)
  479. .execute(conn)
  480. .await?;
  481. }
  482. }
  483. Ok(())
  484. }
  485. async fn get_character_equips(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result<EquippedEntity, GatewayError>
  486. {
  487. let equips = sqlx::query_as::<_, PgEquipped>("select * from equipped where pchar = $1")
  488. .bind(char_id.0 as i32)
  489. .fetch_one(conn)
  490. .await?;
  491. Ok(equips.into())
  492. }
  493. async fn set_character_equips(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError>
  494. {
  495. 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)
  496. on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#)
  497. .bind(char_id.0 as i32)
  498. .bind(equips.weapon.map(|i| i.0 as i32))
  499. .bind(equips.armor.map(|i| i.0 as i32))
  500. .bind(equips.shield.map(|i| i.0 as i32))
  501. .bind(equips.unit[0].map(|i| i.0 as i32))
  502. .bind(equips.unit[1].map(|i| i.0 as i32))
  503. .bind(equips.unit[2].map(|i| i.0 as i32))
  504. .bind(equips.unit[3].map(|i| i.0 as i32))
  505. .bind(equips.mag.map(|i| i.0 as i32))
  506. .execute(conn)
  507. .await?;
  508. Ok(())
  509. }
  510. async fn set_character_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError>
  511. {
  512. sqlx::query("insert into character_meseta values ($1, $2) on conflict (pchar) do update set meseta = $2")
  513. .bind(char_id.0 as i32)
  514. .bind(meseta.0 as i32)
  515. .execute(conn)
  516. .await?;
  517. Ok(())
  518. }
  519. async fn get_character_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result<Meseta, GatewayError>
  520. {
  521. #[derive(sqlx::FromRow)]
  522. struct PgMeseta(i32);
  523. let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where pchar = $1"#)
  524. .bind(char_id.0 as i32)
  525. .fetch_one(conn)
  526. .await?;
  527. Ok(Meseta(meseta.0 as u32))
  528. }
  529. async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier, meseta: Meseta) -> Result<(), GatewayError>
  530. {
  531. match bank_identifier {
  532. BankIdentifier::Character => {
  533. sqlx::query("insert into bank_meseta values ($1, '', $2) on conflict (pchar, bank) do update set meseta = $2")
  534. .bind(char_id.0 as i32)
  535. .bind(meseta.0 as i32)
  536. .execute(conn)
  537. .await?;
  538. },
  539. BankIdentifier::Shared(bank_name) => {
  540. sqlx::query("insert into shared_bank_meseta (user_account, name, meseta)
  541. select player_character.user_account, $2, $3 from player_character
  542. where player_character.id = $1
  543. on conflict (user_account, name) do update set meseta = $3")
  544. .bind(char_id.0 as i32)
  545. .bind(&bank_name.0)
  546. .bind(meseta.0 as i32)
  547. .execute(conn)
  548. .await?;
  549. }
  550. }
  551. Ok(())
  552. }
  553. async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result<Meseta, GatewayError>
  554. {
  555. #[derive(sqlx::FromRow)]
  556. struct PgMeseta(i32);
  557. let meseta = match bank_identifier {
  558. BankIdentifier::Character => {
  559. sqlx::query_as::<_, PgMeseta>(r#"select meseta from bank_meseta where pchar = $1"#)
  560. .bind(char_id.0 as i32)
  561. .fetch_one(conn)
  562. .await?
  563. },
  564. BankIdentifier::Shared(bank_name) => {
  565. sqlx::query_as::<_, PgMeseta>(r#"select shared_bank_meseta.meseta from shared_bank_meseta
  566. join player_character on shared_bank_meseta.user_account = player_character.user_account
  567. where player_character.id = $1 and shared_bank_meseta.name = $2"#)
  568. .bind(char_id.0 as i32)
  569. .bind(&bank_name.0)
  570. .fetch_optional(conn)
  571. .await?
  572. .unwrap_or(PgMeseta(0))
  573. }
  574. };
  575. Ok(Meseta(meseta.0 as u32))
  576. }
  577. async fn create_trade(conn: &mut sqlx::PgConnection, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result<TradeEntity, GatewayError>
  578. {
  579. let trade = sqlx::query_as::<_, PgTradeEntity>(r#"insert into trades (character1, character2) values ($1, $2) returning *;"#)
  580. .bind(char_id1.0 as i32)
  581. .bind(char_id2.0 as i32)
  582. .fetch_one(conn)
  583. .await?;
  584. Ok(trade.into())
  585. }
  586. async fn set_character_playtime(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError>
  587. {
  588. sqlx::query(r#"update player_character set playtime=$2 where id=$1;"#)
  589. .bind(char_id.0 as i32)
  590. .bind(playtime as i32)
  591. .execute(conn)
  592. .await?;
  593. Ok(())
  594. }
  595. async fn create_room(conn: &mut sqlx::PgConnection, room: NewRoomEntity) -> Result<RoomEntity, GatewayError> {
  596. sqlx::query_as::<_, PgRoomEntity>("insert into room (name, section_id, mode, episode, difficulty) values ($1, $2, $3, $4, $5) returning *")
  597. .bind(room.name)
  598. .bind(u8::from(room.section_id) as i8)
  599. .bind(u8::from(room.mode) as i8)
  600. .bind(u8::from(room.episode) as i8)
  601. .bind(u8::from(room.difficulty) as i8)
  602. .fetch_one(conn)
  603. .await
  604. .map(|room| room.into())
  605. .map_err(|err| err.into())
  606. }
  607. async fn add_room_note(conn: &mut sqlx::PgConnection, room_id: RoomEntityId, note: RoomNote) -> Result<(), GatewayError> {
  608. sqlx::query("insert into room_note (room, note) values ($1, $2)")
  609. .bind(room_id.0 as i32)
  610. .bind(sqlx::types::Json(note))
  611. .execute(conn)
  612. .await?;
  613. Ok(())
  614. }
  615. #[async_trait::async_trait]
  616. impl EntityGateway for PostgresGateway {
  617. type Transaction<'t> = PostgresTransaction<'t> where Self: 't;
  618. fn with_transaction<'a, F, Fut, R>(&'a mut self, func: F) -> BoxFuture<'a, Result<R, anyhow::Error>>
  619. where
  620. Fut: Future<Output = Result<(Self::Transaction<'a>, R), anyhow::Error>> + Send + 'a,
  621. F: FnOnce(Self::Transaction<'a>) -> Fut + Send + 'a,
  622. R: Send,
  623. {
  624. Box::pin(async move {
  625. let transaction = PostgresTransaction {
  626. pgtransaction: Arc::new(Mutex::new(self.pool.begin().await?))
  627. };
  628. let (transaction, result) = func(transaction).await?;
  629. transaction.commit().await?;
  630. Ok(result)
  631. })
  632. }
  633. async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
  634. create_user(&mut *self.pool.acquire().await?, user).await
  635. }
  636. async fn get_user_by_id(&mut self, id: UserAccountId) -> Result<UserAccountEntity, GatewayError> {
  637. get_user_by_id(&mut *self.pool.acquire().await?, id).await
  638. }
  639. async fn get_user_by_name(&mut self, username: String) -> Result<UserAccountEntity, GatewayError> {
  640. get_user_by_name(&mut *self.pool.acquire().await?, username).await
  641. }
  642. async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> {
  643. save_user(&mut *self.pool.acquire().await?, user).await
  644. }
  645. async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result<UserSettingsEntity, GatewayError> {
  646. create_user_settings(&mut *self.pool.acquire().await?, settings).await
  647. }
  648. async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> {
  649. get_user_settings_by_user(&mut *self.pool.acquire().await?, user).await
  650. }
  651. async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> {
  652. save_user_settings(&mut *self.pool.acquire().await?, settings).await
  653. }
  654. async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
  655. create_character(&mut *self.pool.acquire().await?, char).await
  656. }
  657. async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError> {
  658. get_characters_by_user(&mut *self.pool.acquire().await?, user).await
  659. }
  660. async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> {
  661. save_character(&mut *self.pool.acquire().await?, char).await
  662. }
  663. async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> {
  664. Ok(GuildCardDataEntity {
  665. id: GuildCardDataId(0),
  666. user_id: user.id,
  667. guildcard: guildcard::GuildCardData::default(),
  668. })
  669. }
  670. async fn create_item(&mut self, item: NewItemEntity) -> Result<ItemEntity, GatewayError> {
  671. create_item(&mut *self.pool.acquire().await?, item).await
  672. }
  673. async fn add_item_note(&mut self, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> {
  674. add_item_note(&mut *self.pool.acquire().await?, item_id, item_note).await
  675. }
  676. async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> {
  677. feed_mag(&mut *self.pool.acquire().await?, mag_item_id, tool_item_id).await
  678. }
  679. async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> {
  680. change_mag_owner(&mut *self.pool.acquire().await?, mag_item_id, character).await
  681. }
  682. async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> {
  683. use_mag_cell(&mut *self.pool.acquire().await?, mag_item_id, mag_cell_id).await
  684. }
  685. async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: &weapon::WeaponModifier) -> Result<(), GatewayError> {
  686. add_weapon_modifier(&mut *self.pool.acquire().await?, item_id, modifier).await
  687. }
  688. async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
  689. get_character_inventory(&mut *self.pool.acquire().await?, char_id).await
  690. }
  691. async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result<BankEntity, GatewayError> {
  692. get_character_bank(&mut *self.pool.acquire().await?, char_id, bank_identifier).await
  693. }
  694. async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> {
  695. set_character_inventory(&mut *self.pool.acquire().await?, char_id, inventory).await
  696. }
  697. async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_identifier: &BankIdentifier) -> Result<(), GatewayError> {
  698. set_character_bank(&mut *self.pool.acquire().await?, char_id, bank, bank_identifier).await
  699. }
  700. async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result<EquippedEntity, GatewayError> {
  701. get_character_equips(&mut *self.pool.acquire().await?, char_id).await
  702. }
  703. async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> {
  704. set_character_equips(&mut *self.pool.acquire().await?, char_id, equips).await
  705. }
  706. async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> {
  707. set_character_meseta(&mut *self.pool.acquire().await?, char_id, meseta).await
  708. }
  709. async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result<Meseta, GatewayError> {
  710. get_character_meseta(&mut *self.pool.acquire().await?, char_id).await
  711. }
  712. async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier, meseta: Meseta) -> Result<(), GatewayError> {
  713. set_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank_identifier, meseta).await
  714. }
  715. async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result<Meseta, GatewayError> {
  716. get_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank_identifier).await
  717. }
  718. async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result<TradeEntity, GatewayError> {
  719. create_trade(&mut *self.pool.acquire().await?, char_id1, char_id2).await
  720. }
  721. async fn set_character_playtime(&mut self, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError> {
  722. set_character_playtime(&mut *self.pool.acquire().await?, char_id, playtime).await
  723. }
  724. async fn create_room(&mut self, room: NewRoomEntity) -> Result<RoomEntity, GatewayError> {
  725. create_room(&mut *self.pool.acquire().await?, room).await
  726. }
  727. async fn add_room_note(&mut self, room_id: RoomEntityId, note: RoomNote) -> Result<(), GatewayError> {
  728. add_room_note(&mut *self.pool.acquire().await?, room_id, note).await
  729. }
  730. }
  731. #[async_trait::async_trait]
  732. impl<'c> EntityGateway for PostgresTransaction<'c> {
  733. type Transaction<'t> = PostgresTransaction<'c> where Self: 't;
  734. async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
  735. create_user(&mut *self.pgtransaction.lock().await, user).await
  736. }
  737. async fn get_user_by_id(&mut self, id: UserAccountId) -> Result<UserAccountEntity, GatewayError> {
  738. get_user_by_id(&mut *self.pgtransaction.lock().await, id).await
  739. }
  740. async fn get_user_by_name(&mut self, username: String) -> Result<UserAccountEntity, GatewayError> {
  741. get_user_by_name(&mut *self.pgtransaction.lock().await, username).await
  742. }
  743. async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> {
  744. save_user(&mut *self.pgtransaction.lock().await, user).await
  745. }
  746. async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result<UserSettingsEntity, GatewayError> {
  747. create_user_settings(&mut *self.pgtransaction.lock().await, settings).await
  748. }
  749. async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> {
  750. get_user_settings_by_user(&mut *self.pgtransaction.lock().await, user).await
  751. }
  752. async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> {
  753. save_user_settings(&mut *self.pgtransaction.lock().await, settings).await
  754. }
  755. async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
  756. create_character(&mut *self.pgtransaction.lock().await, char).await
  757. }
  758. async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError> {
  759. get_characters_by_user(&mut *self.pgtransaction.lock().await, user).await
  760. }
  761. async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> {
  762. save_character(&mut *self.pgtransaction.lock().await, char).await
  763. }
  764. async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> {
  765. Ok(GuildCardDataEntity {
  766. id: GuildCardDataId(0),
  767. user_id: user.id,
  768. guildcard: guildcard::GuildCardData::default(),
  769. })
  770. }
  771. async fn create_item(&mut self, item: NewItemEntity) -> Result<ItemEntity, GatewayError> {
  772. create_item(&mut *self.pgtransaction.lock().await, item).await
  773. }
  774. async fn add_item_note(&mut self, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> {
  775. add_item_note(&mut *self.pgtransaction.lock().await, item_id, item_note).await
  776. }
  777. async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> {
  778. feed_mag(&mut *self.pgtransaction.lock().await, mag_item_id, tool_item_id).await
  779. }
  780. async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> {
  781. change_mag_owner(&mut *self.pgtransaction.lock().await, mag_item_id, character).await
  782. }
  783. async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> {
  784. use_mag_cell(&mut *self.pgtransaction.lock().await, mag_item_id, mag_cell_id).await
  785. }
  786. async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: &weapon::WeaponModifier) -> Result<(), GatewayError> {
  787. add_weapon_modifier(&mut *self.pgtransaction.lock().await, item_id, modifier).await
  788. }
  789. async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
  790. get_character_inventory(&mut *self.pgtransaction.lock().await, char_id).await
  791. }
  792. async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result<BankEntity, GatewayError> {
  793. get_character_bank(&mut *self.pgtransaction.lock().await, char_id, bank_identifier).await
  794. }
  795. async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> {
  796. set_character_inventory(&mut *self.pgtransaction.lock().await, char_id, inventory).await
  797. }
  798. async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_identifier: &BankIdentifier) -> Result<(), GatewayError> {
  799. set_character_bank(&mut *self.pgtransaction.lock().await, char_id, bank, bank_identifier).await
  800. }
  801. async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result<EquippedEntity, GatewayError> {
  802. get_character_equips(&mut *self.pgtransaction.lock().await, char_id).await
  803. }
  804. async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> {
  805. set_character_equips(&mut *self.pgtransaction.lock().await, char_id, equips).await
  806. }
  807. async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> {
  808. set_character_meseta(&mut *self.pgtransaction.lock().await, char_id, meseta).await
  809. }
  810. async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result<Meseta, GatewayError> {
  811. get_character_meseta(&mut *self.pgtransaction.lock().await, char_id).await
  812. }
  813. async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier, meseta: Meseta) -> Result<(), GatewayError> {
  814. set_bank_meseta(&mut *self.pgtransaction.lock().await, char_id, bank_identifier, meseta).await
  815. }
  816. async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank_identifier: &BankIdentifier) -> Result<Meseta, GatewayError> {
  817. get_bank_meseta(&mut *self.pgtransaction.lock().await, char_id, bank_identifier).await
  818. }
  819. async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result<TradeEntity, GatewayError> {
  820. create_trade(&mut *self.pgtransaction.lock().await, char_id1, char_id2).await
  821. }
  822. async fn set_character_playtime(&mut self, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError> {
  823. set_character_playtime(&mut *self.pgtransaction.lock().await, char_id, playtime).await
  824. }
  825. async fn create_room(&mut self, room: NewRoomEntity) -> Result<RoomEntity, GatewayError> {
  826. create_room(&mut *self.pgtransaction.lock().await, room).await
  827. }
  828. async fn add_room_note(&mut self, room_id: RoomEntityId, note: RoomNote) -> Result<(), GatewayError> {
  829. add_room_note(&mut *self.pgtransaction.lock().await, room_id, note).await
  830. }
  831. }