1use super::models::{
2 DieselAuthInfo, DieselAuthInfoAnonymous, DieselAuthInfoEmail, DieselAuthInfoGithub, DieselGame,
3 DieselGameBoard, DieselGameDrawWall, DieselGameHand, DieselGamePlayer, DieselGameScore,
4 DieselGameSettings, DieselPlayer,
5};
6use super::schema;
7use crate::auth::{AuthInfo, AuthInfoAnonymous, AuthInfoData, AuthInfoEmail, AuthInfoGithub};
8use diesel::prelude::*;
9use diesel::PgConnection;
10use mahjong_core::deck::DEFAULT_DECK;
11use mahjong_core::hand::KongTile;
12use mahjong_core::{
13 game::GameStyle,
14 round::{Round, RoundTileClaimed},
15};
16use mahjong_core::{
17 Board, BonusTiles, DrawWall, DrawWallPlace, Game, GameId, Hand, HandTile, Hands, PlayerId,
18 Score, ScoreMap, TileId,
19};
20use rustc_hash::FxHashMap;
21use schema::player::dsl as player_dsl;
22use service_contracts::{
23 AuthProvider, GameSettings, ServiceGame, ServicePlayer, ServicePlayerGame,
24};
25use std::str::FromStr;
26use tracing::debug;
27
28pub fn wait_common() {
29 std::thread::sleep(std::time::Duration::from_millis(1));
30}
31
32fn db_request<A, B, C>(mut func: A)
33where
34 A: FnMut() -> Result<B, C>,
35 B: std::fmt::Debug,
36 C: std::fmt::Debug,
37{
38 loop {
39 let result = func();
40
41 if result.is_ok() {
42 break;
43 }
44 debug!("DB request failed: {:?}", result);
45 wait_common();
46 }
47}
48
49impl DieselAuthInfo {
50 pub fn into_raw(self, data: &AuthInfoData) -> AuthInfo {
51 AuthInfo {
52 data: data.clone(),
53 role: serde_json::from_str(&self.role).unwrap(),
54 user_id: self.user_id,
55 }
56 }
57
58 pub fn into_raw_get_data(self, connection: &mut PgConnection) -> AuthInfo {
59 let data = match self.provider.as_str() {
60 val if val == AuthProvider::Email.to_string() => {
61 let data = DieselAuthInfoEmail::get_by_id(connection, &self.user_id)
62 .expect("User not found");
63
64 AuthInfoData::Email(data.into_raw())
65 }
66 val if val == AuthProvider::Github.to_string() => {
67 let data = DieselAuthInfoGithub::get_by_id(connection, &self.user_id)
68 .expect("Github not found");
69
70 AuthInfoData::Github(data.into_raw())
71 }
72 val if val == AuthProvider::Anonymous.to_string() => {
73 let data = DieselAuthInfoAnonymous::get_by_id(connection, &self.user_id)
74 .expect("Anonymous not found");
75
76 AuthInfoData::Anonymous(data.into_raw())
77 }
78 _ => panic!("Unknown provider"),
79 };
80
81 AuthInfo {
82 data,
83 role: serde_json::from_str(&self.role).unwrap(),
84 user_id: self.user_id,
85 }
86 }
87
88 pub fn from_raw(raw: &AuthInfo) -> Self {
89 Self {
90 provider: match raw.data {
91 AuthInfoData::Email(_) => AuthProvider::Email.to_string(),
92 AuthInfoData::Github(_) => AuthProvider::Github.to_string(),
93 AuthInfoData::Anonymous(_) => AuthProvider::Anonymous.to_string(),
94 },
95 role: serde_json::to_string(&raw.role).unwrap(),
96 user_id: raw.user_id.clone(),
97 }
98 }
99
100 pub fn get_info_by_id(
101 connection: &mut PgConnection,
102 id: &String,
103 ) -> Result<Option<AuthInfo>, String> {
104 use schema::auth_info::dsl;
105
106 let auth_info = dsl::auth_info
107 .filter(dsl::user_id.eq(&id))
108 .limit(1)
109 .load::<Self>(connection)
110 .unwrap()
111 .first()
112 .map(|auth_info| auth_info.clone().into_raw_get_data(connection));
113
114 Ok(auth_info)
115 }
116
117 pub fn get_by_id_with_data(
118 connection: &mut PgConnection,
119 id: &String,
120 data: &AuthInfoData,
121 ) -> Option<AuthInfo> {
122 use super::schema::auth_info::dsl;
123
124 let auth_info = loop {
125 if let Ok(db_data) = dsl::auth_info
126 .filter(dsl::user_id.eq(id))
127 .limit(1)
128 .load::<Self>(connection)
129 {
130 break db_data;
131 }
132 wait_common();
133 }
134 .first()
135 .cloned();
136
137 auth_info.map(|auth_info_content| auth_info_content.into_raw(data))
138 }
139}
140
141impl DieselAuthInfoEmail {
142 pub fn into_raw(self) -> AuthInfoEmail {
143 AuthInfoEmail {
144 hashed_pass: self.hashed_pass,
145 id: self.user_id,
146 username: self.username,
147 }
148 }
149
150 pub fn from_raw(raw: &AuthInfoEmail) -> Self {
151 Self {
152 hashed_pass: raw.hashed_pass.clone(),
153 user_id: raw.id.clone(),
154 username: raw.username.clone(),
155 }
156 }
157
158 pub fn get_by_id(connection: &mut PgConnection, id: &String) -> Option<Self> {
159 use super::schema::auth_info_email::dsl;
160
161 loop {
162 if let Ok(data) = dsl::auth_info_email
163 .filter(dsl::user_id.eq(id))
164 .limit(1)
165 .load::<Self>(connection)
166 {
167 break data;
168 }
169 wait_common();
170 }
171 .first()
172 .cloned()
173 }
174
175 pub fn get_info_by_username(
176 connection: &mut PgConnection,
177 username: &String,
178 ) -> Result<Option<AuthInfo>, String> {
179 use schema::auth_info_email::dsl as email_dsl;
180
181 let auth_info_email = email_dsl::auth_info_email
182 .filter(email_dsl::username.eq(&username))
183 .limit(1)
184 .load::<Self>(connection)
185 .unwrap()
186 .first()
187 .map(|auth_info| auth_info.clone().into_raw());
188
189 if auth_info_email.is_none() {
190 return Ok(None);
191 }
192
193 let auth_info_email = auth_info_email.unwrap();
194
195 let auth_info = DieselAuthInfo::get_by_id_with_data(
196 connection,
197 &auth_info_email.id,
198 &AuthInfoData::Email(auth_info_email.clone()),
199 );
200
201 Ok(auth_info)
202 }
203}
204
205impl DieselAuthInfoAnonymous {
206 pub fn into_raw(self) -> AuthInfoAnonymous {
207 AuthInfoAnonymous {
208 hashed_token: self.hashed_token,
209 id: self.user_id,
210 }
211 }
212
213 pub fn from_raw(raw: &AuthInfoAnonymous) -> Self {
214 Self {
215 hashed_token: raw.hashed_token.clone(),
216 user_id: raw.id.clone(),
217 }
218 }
219
220 pub fn get_by_id(connection: &mut PgConnection, id: &String) -> Option<Self> {
221 use super::schema::auth_info_anonymous::dsl;
222
223 loop {
224 if let Ok(data) = dsl::auth_info_anonymous
225 .filter(dsl::user_id.eq(id))
226 .limit(1)
227 .load::<Self>(connection)
228 {
229 break data;
230 }
231 wait_common();
232 }
233 .first()
234 .cloned()
235 }
236
237 pub fn get_info_by_hashed_token(
238 connection: &mut PgConnection,
239 hashed_token: &String,
240 ) -> Result<Option<AuthInfo>, String> {
241 use schema::auth_info_anonymous::dsl as anonymous_dsl;
242
243 let auth_info_email = anonymous_dsl::auth_info_anonymous
244 .filter(anonymous_dsl::hashed_token.eq(&hashed_token))
245 .limit(1)
246 .load::<Self>(connection)
247 .unwrap()
248 .first()
249 .map(|auth_info| auth_info.clone().into_raw());
250
251 if auth_info_email.is_none() {
252 return Ok(None);
253 }
254
255 let auth_info_anonymous = auth_info_email.unwrap();
256
257 let auth_info = DieselAuthInfo::get_by_id_with_data(
258 connection,
259 &auth_info_anonymous.id,
260 &AuthInfoData::Anonymous(auth_info_anonymous.clone()),
261 );
262
263 Ok(auth_info)
264 }
265}
266
267impl DieselAuthInfoGithub {
268 pub fn into_raw(self) -> AuthInfoGithub {
269 AuthInfoGithub {
270 id: self.user_id.clone(),
271 token: self.token.clone(),
272 username: self.username,
273 }
274 }
275
276 pub fn from_raw(raw: &AuthInfoGithub) -> Self {
277 Self {
278 token: raw.token.clone(),
279 user_id: raw.id.clone(),
280 username: raw.username.clone(),
281 }
282 }
283
284 pub fn get_by_id(connection: &mut PgConnection, id: &String) -> Option<Self> {
285 use super::schema::auth_info_github::dsl;
286
287 loop {
288 if let Ok(data) = dsl::auth_info_github
289 .filter(dsl::user_id.eq(id))
290 .limit(1)
291 .load::<Self>(connection)
292 {
293 break data;
294 }
295 wait_common();
296 }
297 .first()
298 .cloned()
299 }
300
301 pub fn get_info_by_username(
302 connection: &mut PgConnection,
303 username: &String,
304 ) -> Result<Option<AuthInfo>, String> {
305 use schema::auth_info_github::dsl as github_dsl;
306
307 let auth_info_github = github_dsl::auth_info_github
308 .filter(github_dsl::username.eq(&username))
309 .limit(1)
310 .load::<Self>(connection)
311 .unwrap()
312 .first()
313 .map(|auth_info| auth_info.clone().into_raw());
314
315 if auth_info_github.is_none() {
316 return Ok(None);
317 }
318
319 let auth_info_github = auth_info_github.unwrap();
320
321 let auth_info = DieselAuthInfo::get_by_id_with_data(
322 connection,
323 &auth_info_github.id,
324 &AuthInfoData::Github(auth_info_github.clone()),
325 );
326
327 Ok(auth_info)
328 }
329}
330
331impl DieselPlayer {
332 pub fn into_raw(self) -> ServicePlayer {
333 ServicePlayer {
334 created_at: self.created_at.and_utc().timestamp_millis().to_string(),
335 id: self.id,
336 is_ai: self.is_ai == 1,
337 name: self.name,
338 }
339 }
340
341 pub fn from_raw(raw: &ServicePlayer) -> Self {
342 Self {
343 created_at: chrono::DateTime::from_timestamp_millis(
344 raw.created_at.parse::<i64>().unwrap(),
345 )
346 .unwrap()
347 .naive_utc(),
348 id: raw.id.clone(),
349 is_ai: if raw.is_ai { 1 } else { 0 },
350 name: raw.name.clone(),
351 }
352 }
353
354 pub fn read_from_ids(
355 connection: &mut PgConnection,
356 ids: &Vec<PlayerId>,
357 ) -> FxHashMap<PlayerId, ServicePlayer> {
358 loop {
359 if let Ok(data) = player_dsl::player
360 .filter(player_dsl::id.eq_any(ids))
361 .load::<Self>(connection)
362 {
363 break data;
364 }
365 wait_common();
366 }
367 .into_iter()
368 .map(|player| (player.id.clone(), player.into_raw()))
369 .collect::<FxHashMap<PlayerId, ServicePlayer>>()
370 }
371
372 pub fn update_from_game(connection: &mut PgConnection, service_game: &ServiceGame) {
373 use super::schema::player::table as player_table;
374
375 let players = service_game
376 .players
377 .values()
378 .map(Self::from_raw)
379 .collect::<Vec<Self>>();
380
381 for player in players {
382 loop {
383 let result = diesel::insert_into(player_table)
384 .values(&player)
385 .on_conflict(player_dsl::id)
386 .do_update()
387 .set(&player)
388 .execute(connection);
389
390 if result.is_ok() {
391 break;
392 }
393 debug!("Error saving player: {:?}", result.err());
394 wait_common();
395 }
396 }
397 }
398
399 pub fn save(connection: &mut PgConnection, player: &ServicePlayer) {
400 use super::schema::player::table as player_table;
401
402 let player = Self::from_raw(player);
403
404 loop {
405 if diesel::insert_into(player_table)
406 .values(&player)
407 .on_conflict(player_dsl::id)
408 .do_update()
409 .set(&player)
410 .execute(connection)
411 .is_ok()
412 {
413 break;
414 }
415 wait_common();
416 }
417 }
418
419 pub fn read_from_id_raw(connection: &mut PgConnection, player_id: &PlayerId) -> Option<Self> {
420 loop {
421 if let Ok(player) = player_dsl::player
422 .filter(player_dsl::id.eq(player_id))
423 .limit(1)
424 .load::<Self>(connection)
425 {
426 break player;
427 }
428 wait_common();
429 }
430 .first()
431 .cloned()
432 }
433
434 pub fn read_from_id(
435 connection: &mut PgConnection,
436 player_id: &PlayerId,
437 ) -> Option<ServicePlayer> {
438 loop {
439 if let Ok(player) = player_dsl::player
440 .filter(player_dsl::id.eq(player_id))
441 .limit(1)
442 .load::<Self>(connection)
443 {
444 break player;
445 }
446 wait_common();
447 }
448 .first()
449 .map(|player| player.clone().into_raw())
450 }
451}
452
453pub struct DieselGameExtra {
454 pub game: Game,
455 pub created_at: chrono::NaiveDateTime,
456 pub updated_at: chrono::NaiveDateTime,
457}
458
459impl DieselGame {
460 pub fn into_raw(self) -> DieselGameExtra {
461 let default_game = Game::new(None);
462 let game_style = GameStyle::from_str(&self.style);
463
464 let round = Round {
465 dealer_player_index: self.round_dealer_index as usize,
466 player_index: self.round_player_index as usize,
467 round_index: self.round_index as u32,
468 tile_claimed: self.round_claimed_id.map(|id| RoundTileClaimed {
469 by: self.round_claimed_by,
470 from: self.round_claimed_from.unwrap(),
471 id: id as TileId,
472 }),
473 wall_tile_drawn: self.round_wall_tile_drawn.map(|tile_id| tile_id as TileId),
474 wind: serde_json::from_str(&self.round_wind).unwrap(),
475 style: game_style.clone().unwrap(),
476 consecutive_same_seats: self.round_consecutive_same_seats as usize,
477 east_player_index: self.round_east_player_index as usize,
478 initial_winds: self.round_initial_winds.map(|w| w as u8),
479 };
480 let game = Game {
481 name: self.name,
482 version: self.version,
483 id: self.id,
484 phase: serde_json::from_str(&self.phase).unwrap(),
485 round,
486 style: game_style.unwrap(),
487 ..default_game
489 };
490
491 DieselGameExtra {
492 game,
493 created_at: self.created_at,
494 updated_at: self.updated_at,
495 }
496 }
497
498 pub fn read_from_id(
499 connection: &mut PgConnection,
500 game_id: &GameId,
501 ) -> Option<DieselGameExtra> {
502 use schema::game::dsl as game_dsl;
503
504 loop {
505 if let Ok(game) = game_dsl::game
506 .filter(game_dsl::id.eq(game_id))
507 .limit(1)
508 .load::<Self>(connection)
509 {
510 break game;
511 }
512 wait_common();
513 }
514 .first()
515 .map(|game| game.clone().into_raw())
516 }
517
518 pub fn read_player_games(connection: &mut PgConnection) -> Vec<ServicePlayerGame> {
519 use schema::game::dsl as game_dsl;
520
521 loop {
522 if let Ok(game) = game_dsl::game.load::<Self>(connection) {
523 break game;
524 }
525 wait_common();
526 }
527 .into_iter()
528 .map(|game| ServicePlayerGame {
529 created_at: game.created_at.and_utc().timestamp_millis().to_string(),
530 id: game.id,
531 updated_at: game.updated_at.and_utc().timestamp_millis().to_string(),
532 })
533 .collect()
534 }
535
536 pub fn update(&self, connection: &mut PgConnection) {
537 use super::schema::game::table as game_table;
538
539 loop {
540 if diesel::insert_into(game_table)
541 .values(self)
542 .on_conflict(super::schema::game::dsl::id)
543 .do_update()
544 .set(self)
545 .execute(connection)
546 .is_ok()
547 {
548 break;
549 }
550 wait_common();
551 }
552 }
553
554 pub fn from_raw(extra: &DieselGameExtra) -> Self {
555 let raw = &extra.game;
556
557 Self {
558 created_at: extra.created_at,
559 id: raw.id.clone(),
560 name: raw.name.clone(),
561 phase: serde_json::to_string(&raw.phase).unwrap(),
562 round_claimed_by: raw.round.tile_claimed.clone().and_then(|t| t.by),
563 round_claimed_from: raw.round.tile_claimed.clone().map(|t| t.from),
564 round_claimed_id: raw.round.tile_claimed.clone().map(|t| t.id as i32),
565 round_dealer_index: raw.round.dealer_player_index as i32,
566 round_index: raw.round.round_index as i32,
567 round_player_index: raw.round.player_index as i32,
568 round_wall_tile_drawn: raw.round.wall_tile_drawn.map(|t| t as i32),
569 round_wind: serde_json::to_string(&raw.round.wind).unwrap(),
570 updated_at: extra.updated_at,
571 version: raw.version.clone(),
572 style: raw.style.to_string(),
573 round_consecutive_same_seats: raw.round.consecutive_same_seats as i32,
574 round_east_player_index: raw.round.east_player_index as i32,
575 round_initial_winds: raw.round.initial_winds.map(|w| w as i32),
576 }
577 }
578
579 pub fn delete_games(connection: &mut PgConnection, game_ids: &[GameId]) {
580 db_request(|| {
581 diesel::delete(schema::game::table)
582 .filter(schema::game::dsl::id.eq_any(game_ids))
583 .execute(connection)
584 });
585 }
586}
587
588impl DieselGamePlayer {
589 pub fn from_game(game: &Game) -> Vec<Self> {
590 game.players
591 .iter()
592 .enumerate()
593 .map(|(index, player_id)| Self {
594 game_id: game.id.clone(),
595 player_id: player_id.clone(),
596 player_index: index as i32,
597 })
598 .collect::<Vec<Self>>()
599 }
600
601 pub fn read_from_game(connection: &mut PgConnection, game_id: &GameId) -> Vec<PlayerId> {
602 use schema::game_player::dsl as game_player_dsl;
603 loop {
604 if let Ok(data) = game_player_dsl::game_player
605 .filter(game_player_dsl::game_id.eq(game_id))
606 .order(game_player_dsl::player_index.asc())
607 .load::<Self>(connection)
608 {
609 break data;
610 }
611 wait_common();
612 }
613 .into_iter()
614 .map(|game| game.player_id)
615 .collect::<Vec<PlayerId>>()
616 }
617
618 pub fn read_from_player(
619 connection: &mut PgConnection,
620 player_id: &PlayerId,
621 ) -> Vec<ServicePlayerGame> {
622 let player = DieselPlayer::read_from_id_raw(connection, player_id);
623
624 if player.is_none() {
625 return vec![];
626 }
627
628 let player = player.unwrap();
629
630 loop {
631 if let Ok(data) = Self::belonging_to(&player)
632 .inner_join(super::schema::game::table)
633 .select(DieselGame::as_select())
634 .order(super::schema::game::dsl::updated_at.desc())
635 .load(connection)
636 {
637 break data;
638 }
639 wait_common();
640 }
641 .into_iter()
642 .map(|game| ServicePlayerGame {
643 created_at: game.created_at.and_utc().timestamp_millis().to_string(),
644 id: game.id,
645 updated_at: game.updated_at.and_utc().timestamp_millis().to_string(),
646 })
647 .collect::<Vec<_>>()
648 }
649
650 pub fn update(connection: &mut PgConnection, diesel_game_players: &Vec<Self>, game: &Game) {
651 use schema::game_player::table as game_player_table;
652
653 db_request(|| {
654 connection.transaction(|t_connection| {
655 diesel::delete(game_player_table)
656 .filter(schema::game_player::dsl::game_id.eq(&game.id))
657 .execute(t_connection)?;
658
659 diesel::insert_into(game_player_table)
660 .values(diesel_game_players)
661 .execute(t_connection)?;
662
663 diesel::result::QueryResult::Ok(())
664 })
665 });
666 }
667
668 pub fn delete_games(connection: &mut PgConnection, game_ids: &[GameId]) {
669 db_request(|| {
670 diesel::delete(schema::game_player::table)
671 .filter(schema::game_player::dsl::game_id.eq_any(game_ids))
672 .execute(connection)
673 });
674 }
675}
676
677impl DieselGameScore {
678 pub fn update_from_game(connection: &mut PgConnection, service_game: &ServiceGame) {
679 use schema::game_score::table as game_score_table;
680
681 db_request(|| {
682 connection.transaction(|t_connection| {
683 loop {
684 if diesel::delete(game_score_table)
685 .filter(schema::game_score::dsl::game_id.eq(&service_game.game.id))
686 .execute(t_connection)
687 .is_ok()
688 {
689 break;
690 }
691 wait_common();
692 }
693
694 let scores = service_game
695 .game
696 .score
697 .iter()
698 .map(|(player_id, score)| Self {
699 game_id: service_game.game.id.clone(),
700 player_id: player_id.clone(),
701 score: *score as i32,
702 })
703 .collect::<Vec<Self>>();
704
705 loop {
706 if diesel::insert_into(game_score_table)
707 .values(&scores)
708 .execute(t_connection)
709 .is_ok()
710 {
711 break;
712 }
713 wait_common();
714 }
715
716 diesel::result::QueryResult::Ok(())
717 })
718 })
719 }
720
721 pub fn read_from_game(connection: &mut PgConnection, game_id: &GameId) -> Score {
722 use schema::game_score::dsl as game_score_dsl;
723
724 let score_map = loop {
725 if let Ok(data) = game_score_dsl::game_score
726 .filter(game_score_dsl::game_id.eq(game_id))
727 .load::<Self>(connection)
728 {
729 break data;
730 }
731 wait_common();
732 }
733 .into_iter()
734 .map(|game_score| {
735 let score = game_score.score as u32;
736
737 (game_score.player_id, score)
738 })
739 .collect::<ScoreMap>();
740
741 Score(score_map)
742 }
743
744 pub fn read_total_from_player(connection: &mut PgConnection, player_id: &PlayerId) -> i32 {
745 use schema::game_score::dsl as game_score_dsl;
746
747 loop {
748 if let Ok(data) = game_score_dsl::game_score
749 .filter(game_score_dsl::player_id.eq(player_id))
750 .load::<Self>(connection)
751 {
752 break data;
753 }
754 wait_common();
755 }
756 .into_iter()
757 .map(|game_score| game_score.score)
758 .sum()
759 }
760
761 pub fn delete_games(connection: &mut PgConnection, game_ids: &[GameId]) {
762 db_request(|| {
763 diesel::delete(schema::game_score::table)
764 .filter(schema::game_score::dsl::game_id.eq_any(game_ids))
765 .execute(connection)
766 });
767 }
768}
769
770impl DieselGameBoard {
771 pub fn update_from_game(connection: &mut PgConnection, service_game: &ServiceGame) {
772 use schema::game_board::table as game_board_table;
773
774 loop {
775 if diesel::delete(game_board_table)
776 .filter(schema::game_board::dsl::game_id.eq(&service_game.game.id))
777 .execute(connection)
778 .is_ok()
779 {
780 break;
781 }
782 wait_common();
783 }
784
785 let board = service_game
786 .game
787 .table
788 .board
789 .0
790 .iter()
791 .enumerate()
792 .map(|(tile_index, tile_id)| Self {
793 game_id: service_game.game.id.clone(),
794 tile_id: *tile_id as i32,
795 tile_index: tile_index as i32,
796 })
797 .collect::<Vec<Self>>();
798
799 loop {
800 if diesel::insert_into(game_board_table)
801 .values(&board)
802 .execute(connection)
803 .is_ok()
804 {
805 break;
806 }
807 wait_common();
808 }
809 }
810
811 pub fn read_from_game(connection: &mut PgConnection, game_id: &GameId) -> Board {
812 use schema::game_board::dsl as game_board_dsl;
813
814 let board_content = loop {
815 if let Ok(data) = game_board_dsl::game_board
816 .filter(game_board_dsl::game_id.eq(game_id))
817 .order(game_board_dsl::tile_index.asc())
818 .load::<Self>(connection)
819 {
820 break data;
821 }
822 wait_common()
823 }
824 .into_iter()
825 .map(|game_board| game_board.tile_id as TileId)
826 .collect::<Vec<TileId>>();
827
828 Board(board_content)
829 }
830
831 pub fn delete_games(connection: &mut PgConnection, game_ids: &[GameId]) {
832 db_request(|| {
833 diesel::delete(schema::game_board::table)
834 .filter(schema::game_board::dsl::game_id.eq_any(game_ids))
835 .execute(connection)
836 });
837 }
838}
839
840impl DieselGameDrawWall {
841 pub fn update_from_game(connection: &mut PgConnection, service_game: &ServiceGame) {
842 use schema::game_draw_wall::table as game_draw_wall_table;
843
844 loop {
845 if diesel::delete(game_draw_wall_table)
846 .filter(schema::game_draw_wall::dsl::game_id.eq(&service_game.game.id))
847 .execute(connection)
848 .is_ok()
849 {
850 break;
851 }
852 wait_common();
853 }
854
855 let mut draw_wall_vec = vec![];
856
857 let draw_wall = service_game
858 .game
859 .table
860 .draw_wall
861 .iter_all(&mut draw_wall_vec)
862 .enumerate()
863 .map(|(tile_index, draw_wall_tile)| Self {
864 game_id: service_game.game.id.clone(),
865 tile_id: draw_wall_tile.0 as i32,
866 tile_index: tile_index as i32,
867 place: draw_wall_tile.1.to_string(),
868 })
869 .collect::<Vec<Self>>();
870
871 loop {
872 if diesel::insert_into(game_draw_wall_table)
873 .values(&draw_wall)
874 .execute(connection)
875 .is_ok()
876 {
877 break;
878 }
879 wait_common();
880 }
881 }
882
883 pub fn read_from_game(connection: &mut PgConnection, game_id: &GameId) -> DrawWall {
884 use schema::game_draw_wall::dsl as game_draw_wall_dsl;
885
886 let draw_wall_content = loop {
887 if let Ok(data) = game_draw_wall_dsl::game_draw_wall
888 .filter(game_draw_wall_dsl::game_id.eq(game_id))
889 .order(game_draw_wall_dsl::tile_index.asc())
890 .load::<Self>(connection)
891 {
892 break data;
893 }
894 wait_common();
895 }
896 .into_iter()
897 .map(|game_draw_wall| {
898 (
899 game_draw_wall.tile_id as usize,
900 DrawWallPlace::from_str(&game_draw_wall.place).unwrap_or_else(|_| {
901 panic!("Unknown draw wall place: {}", game_draw_wall.place)
902 }),
903 )
904 })
905 .collect::<Vec<(TileId, DrawWallPlace)>>();
906
907 DrawWall::new_full(draw_wall_content)
908 }
909
910 pub fn delete_games(connection: &mut PgConnection, game_ids: &[GameId]) {
911 db_request(|| {
912 diesel::delete(schema::game_draw_wall::table)
913 .filter(schema::game_draw_wall::dsl::game_id.eq_any(game_ids))
914 .execute(connection)
915 });
916 }
917}
918
919impl DieselGameHand {
920 pub fn update_from_game(connection: &mut PgConnection, service_game: &ServiceGame) {
921 use schema::game_hand::table as game_hand_table;
922
923 loop {
924 if diesel::delete(game_hand_table)
925 .filter(schema::game_hand::dsl::game_id.eq(&service_game.game.id))
926 .execute(connection)
927 .is_ok()
928 {
929 break;
930 }
931 wait_common();
932 }
933
934 let mut hands: Vec<Self> = vec![];
935
936 service_game
937 .game
938 .table
939 .hands
940 .0
941 .iter()
942 .for_each(|(player_id, hand)| {
943 hand.list.iter().enumerate().for_each(|(tile_index, tile)| {
944 let tile_id = tile.id;
945 let concealed = if tile.concealed { 1 } else { 0 };
946 let set_id = tile.set_id.clone();
947
948 let game_hand = Self {
949 concealed,
950 game_id: service_game.game.id.clone(),
951 is_kong: false,
952 player_id: player_id.clone(),
953 set_id,
954 tile_id: tile_id as i32,
955 tile_index: tile_index as i32,
956 };
957
958 hands.push(game_hand);
959 });
960
961 hand.kong_tiles
962 .iter()
963 .enumerate()
964 .for_each(|(tile_index, tile)| {
965 let tile_id = tile.id;
966 let concealed = if tile.concealed { 1 } else { 0 };
967 let set_id = tile.set_id.clone();
968
969 let game_hand = Self {
970 concealed,
971 game_id: service_game.game.id.clone(),
972 is_kong: true,
973 player_id: player_id.clone(),
974 set_id: Some(set_id),
975 tile_id: tile_id as i32,
976 tile_index: tile_index as i32,
977 };
978
979 hands.push(game_hand);
980 });
981 });
982
983 service_game
984 .game
985 .table
986 .bonus_tiles
987 .0
988 .iter()
989 .for_each(|(player_id, player_bonus_tiles)| {
990 player_bonus_tiles
991 .iter()
992 .enumerate()
993 .for_each(|(tile_index, tile_id)| {
994 let game_hand = Self {
995 concealed: 0,
996 game_id: service_game.game.id.clone(),
997 is_kong: false,
998 player_id: player_id.clone(),
999 set_id: None,
1000 tile_id: *tile_id as i32,
1001 tile_index: tile_index as i32,
1002 };
1003
1004 hands.push(game_hand);
1005 });
1006 });
1007
1008 loop {
1009 if diesel::insert_into(game_hand_table)
1010 .values(&hands)
1011 .execute(connection)
1012 .is_ok()
1013 {
1014 break;
1015 }
1016 wait_common();
1017 }
1018 }
1019
1020 pub fn read_from_game(connection: &mut PgConnection, game_id: &GameId) -> (Hands, BonusTiles) {
1021 use schema::game_hand::dsl as game_hand_dsl;
1022 let mut hands = Hands::default();
1023 let mut bonus_tiles = BonusTiles::default();
1024
1025 loop {
1026 if let Ok(data) = game_hand_dsl::game_hand
1027 .filter(game_hand_dsl::game_id.eq(game_id))
1028 .order(game_hand_dsl::tile_index.asc())
1029 .load::<Self>(connection)
1030 {
1031 break data;
1032 }
1033 wait_common();
1034 }
1035 .into_iter()
1036 .for_each(|game_hand| {
1037 let tile_id = game_hand.tile_id as TileId;
1038 if DEFAULT_DECK.get_sure(tile_id).is_bonus() {
1039 let player_bonus_tiles = bonus_tiles.get_or_create(&game_hand.player_id);
1040 player_bonus_tiles.push(tile_id);
1041 } else {
1042 let player_id = game_hand.player_id;
1043 let concealed = game_hand.concealed == 1;
1044 let set_id = game_hand.set_id;
1045 let mut current_hand = hands
1046 .0
1047 .get(&player_id)
1048 .unwrap_or(&Hand::new(Vec::new()))
1049 .clone();
1050
1051 if game_hand.is_kong {
1052 current_hand.kong_tiles.insert(KongTile {
1053 id: tile_id,
1054 concealed,
1055 set_id: set_id.unwrap(),
1056 });
1057 } else {
1058 current_hand.push(HandTile {
1059 id: tile_id,
1060 concealed,
1061 set_id,
1062 });
1063 }
1064
1065 hands.0.insert(player_id, current_hand);
1066 }
1067 });
1068
1069 (hands, bonus_tiles)
1070 }
1071
1072 pub fn delete_games(connection: &mut PgConnection, game_ids: &[GameId]) {
1073 db_request(|| {
1074 diesel::delete(schema::game_hand::table)
1075 .filter(schema::game_hand::dsl::game_id.eq_any(game_ids))
1076 .execute(connection)
1077 });
1078 }
1079}
1080
1081impl DieselGameSettings {
1082 pub fn update_from_game(connection: &mut PgConnection, service_game: &ServiceGame) {
1083 use schema::game_settings::table as game_settings_table;
1084
1085 let auto_sort_players = service_game
1086 .settings
1087 .auto_sort_players
1088 .clone()
1089 .into_iter()
1090 .collect::<Vec<_>>()
1091 .join(&','.to_string());
1092
1093 let auto_stop_claim_meld = service_game
1094 .settings
1095 .auto_stop_claim_meld
1096 .clone()
1097 .into_iter()
1098 .collect::<Vec<_>>()
1099 .join(&','.to_string());
1100
1101 let settings = Self {
1102 last_discard_time: service_game.settings.last_discard_time as i64,
1103 ai_enabled: service_game.settings.ai_enabled,
1104 discard_wait_ms: service_game.settings.discard_wait_ms,
1105 game_id: service_game.game.id.clone(),
1106 fixed_settings: service_game.settings.fixed_settings,
1107 auto_sort_players,
1108 auto_stop_claim_meld,
1109 dead_wall: service_game.settings.dead_wall,
1110 };
1111
1112 loop {
1113 if diesel::insert_into(game_settings_table)
1114 .values(&settings)
1115 .on_conflict(schema::game_settings::dsl::game_id)
1116 .do_update()
1117 .set(&settings)
1118 .execute(connection)
1119 .is_ok()
1120 {
1121 break;
1122 }
1123 wait_common();
1124 }
1125 }
1126
1127 pub fn read_from_game(connection: &mut PgConnection, game_id: &GameId) -> Option<GameSettings> {
1128 use schema::game_settings::dsl as game_settings_dsl;
1129
1130 loop {
1131 if let Ok(data) = game_settings_dsl::game_settings
1132 .filter(game_settings_dsl::game_id.eq(game_id))
1133 .limit(1)
1134 .load::<Self>(connection)
1135 {
1136 break data;
1137 }
1138 wait_common();
1139 }
1140 .first()
1141 .map(|game_settings| GameSettings {
1142 ai_enabled: game_settings.ai_enabled,
1143 discard_wait_ms: game_settings.discard_wait_ms,
1144 fixed_settings: game_settings.fixed_settings,
1145 last_discard_time: game_settings.last_discard_time as i128,
1146 auto_stop_claim_meld: game_settings
1147 .auto_stop_claim_meld
1148 .split(',')
1149 .map(|s| s.to_string())
1150 .collect(),
1151 auto_sort_players: game_settings
1152 .auto_sort_players
1153 .split(',')
1154 .map(|s| s.to_string())
1155 .collect(),
1156 dead_wall: game_settings.dead_wall,
1157 })
1158 }
1159
1160 pub fn delete_games(connection: &mut PgConnection, game_ids: &[GameId]) {
1161 db_request(|| {
1162 diesel::delete(schema::game_settings::table)
1163 .filter(schema::game_settings::dsl::game_id.eq_any(game_ids))
1164 .execute(connection)
1165 });
1166 }
1167}