diff --git a/rc_chat_room/src/operations/all_joined_channels.rs b/rc_chat_room/src/operations/all_joined_channels.rs index 434cbf9..1b27762 100644 --- a/rc_chat_room/src/operations/all_joined_channels.rs +++ b/rc_chat_room/src/operations/all_joined_channels.rs @@ -12,7 +12,7 @@ async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy, chat_syst let name = user_info.public_id().to_owned(); let channels = user_info.subscribed_channels_strings().await?; let event_tx = user.event_chann(); - chat_system.system_mut().connect_user(name, channels, event_tx); + chat_system.system_mut().await.connect_user(name, channels, event_tx); params.insert(PARAM_KEY, user_info.subscribed_channels().await?); Ok(params.into()) } diff --git a/rc_chat_room/src/operations/join_channel.rs b/rc_chat_room/src/operations/join_channel.rs index 3d9e092..c1b352c 100644 --- a/rc_chat_room/src/operations/join_channel.rs +++ b/rc_chat_room/src/operations/join_channel.rs @@ -1,4 +1,5 @@ -use polariton::operation::{ParameterTable, Typed, OperationResponse}; +use polariton::operation::{ParameterTable, Typed}; +use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl}; const JOIN_CODE: u8 = 1; const LEAVE_CODE: u8 = 5; @@ -12,39 +13,30 @@ pub struct JoinChannelProvider { chat_system: crate::state::ChatImpl, } -pub(super) fn join_channel_provider(chat_system: crate::state::ChatImpl) -> JoinChannelProvider { - JoinChannelProvider { chat_system } -} - -async fn do_join_handling(params: ParameterTable<()>, user: &crate::UserTy, chat_system: &crate::state::ChatImpl) -> Result { - let mut params = params.to_dict(); - if let Some(Typed::Str(chann_name)) = params.remove(&CHANNEL_NAME_PARAM_KEY) { - if let Some(Typed::Int(chann_ty)) = params.remove(&CHANNEL_TYPE_PARAM_KEY) { - if let Some(Typed::Str(channel_pwd)) = params.remove(&CHANNEL_PASSWORD_PARAM_KEY) { - log::warn!("Received channel password {} which is unsupported", channel_pwd.string); - } - let user_info = user.user()?; - //let chat_user = super::get_chat_user(user_info.as_ref().as_ref()); - chat_system.system_mut().join_channel(user_info.public_id().to_owned(), chann_name.string.clone()); - let response = user_info.add_subscribed_channel(chann_name.string, crate::data::channel::ChatChannelType::from_u8(chann_ty as _)?).await?; - params.insert(CHANNEL_INFO_PARAM_KEY, response); - } - } - Ok(params.into()) +pub(super) fn join_channel_provider(chat_system: crate::state::ChatImpl) -> SimpleOpImpl<(), crate::UserTy, JoinChannelProvider> { + SimpleOpImpl::new(JoinChannelProvider { chat_system }) } #[async_trait::async_trait] -impl polariton_server::operations::Operation<()> for JoinChannelProvider { +impl SimpleOperation<()> for JoinChannelProvider { type User = crate::UserTy; + const CODE: u8 = JOIN_CODE; - async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { - polariton_server::operations::result_to_op_resp::(do_join_handling(params, user, &self.chat_system).await) - } -} - -impl polariton_server::operations::OperationCode for JoinChannelProvider { - fn op_code() -> u8 { - JOIN_CODE + async fn handle(&self, params: ParameterTable<()>, user: &Self::User) -> Result, SimpleOpError> { + let mut params = params.to_dict(); + if let Some(Typed::Str(chann_name)) = params.remove(&CHANNEL_NAME_PARAM_KEY) { + if let Some(Typed::Int(chann_ty)) = params.remove(&CHANNEL_TYPE_PARAM_KEY) { + if let Some(Typed::Str(channel_pwd)) = params.remove(&CHANNEL_PASSWORD_PARAM_KEY) { + log::warn!("Received channel password {} which is unsupported", channel_pwd.string); + } + let user_info = user.user()?; + //let chat_user = super::get_chat_user(user_info.as_ref().as_ref()); + self.chat_system.system_mut().await.join_channel(user_info.public_id().to_owned(), chann_name.string.clone()); + let response = user_info.add_subscribed_channel(chann_name.string, crate::data::channel::ChatChannelType::from_u8(chann_ty as _)?).await?; + params.insert(CHANNEL_INFO_PARAM_KEY, response); + } + } + Ok(params.into()) } } @@ -52,34 +44,25 @@ pub struct LeaveChannelProvider { chat_system: crate::state::ChatImpl, } -pub(super) fn leave_channel_provider(chat_system: crate::state::ChatImpl) -> LeaveChannelProvider { - LeaveChannelProvider { chat_system } -} - -async fn do_leave_handling(params: ParameterTable<()>, user: &crate::UserTy, chat_system: &crate::state::ChatImpl) -> Result { - let mut params = params.to_dict(); - if let Some(Typed::Str(chann_name)) = params.remove(&CHANNEL_NAME_PARAM_KEY) { - if let Some(Typed::Int(chann_ty)) = params.remove(&CHANNEL_TYPE_PARAM_KEY) { - let user_info = user.user()?; - //let chat_user = super::get_chat_user(user_info.as_ref().as_ref()); - chat_system.system_mut().leave_channel(user_info.public_id().to_owned(), chann_name.string.clone()); - user_info.remove_subscribed_channel(chann_name.string, crate::data::channel::ChatChannelType::from_u8(chann_ty as _)?).await?; - } - } - Ok(params.into()) +pub(super) fn leave_channel_provider(chat_system: crate::state::ChatImpl) -> SimpleOpImpl<(), crate::UserTy, LeaveChannelProvider> { + SimpleOpImpl::new(LeaveChannelProvider { chat_system }) } #[async_trait::async_trait] -impl polariton_server::operations::Operation<()> for LeaveChannelProvider { +impl SimpleOperation<()> for LeaveChannelProvider { type User = crate::UserTy; + const CODE: u8 = LEAVE_CODE; - async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { - polariton_server::operations::result_to_op_resp::(do_leave_handling(params, user, &self.chat_system).await) - } -} - -impl polariton_server::operations::OperationCode for LeaveChannelProvider { - fn op_code() -> u8 { - LEAVE_CODE + async fn handle(&self, params: ParameterTable<()>, user: &Self::User) -> Result, SimpleOpError> { + let mut params = params.to_dict(); + if let Some(Typed::Str(chann_name)) = params.remove(&CHANNEL_NAME_PARAM_KEY) { + if let Some(Typed::Int(chann_ty)) = params.remove(&CHANNEL_TYPE_PARAM_KEY) { + let user_info = user.user()?; + //let chat_user = super::get_chat_user(user_info.as_ref().as_ref()); + self.chat_system.system_mut().await.leave_channel(user_info.public_id().to_owned(), chann_name.string.clone()); + user_info.remove_subscribed_channel(chann_name.string, crate::data::channel::ChatChannelType::from_u8(chann_ty as _)?).await?; + } + } + Ok(params.into()) } } diff --git a/rc_chat_room/src/operations/send_message.rs b/rc_chat_room/src/operations/send_message.rs index 55c9d49..b0fb514 100644 --- a/rc_chat_room/src/operations/send_message.rs +++ b/rc_chat_room/src/operations/send_message.rs @@ -1,4 +1,5 @@ use polariton::operation::{ParameterTable, Typed}; +use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl}; const MAX_MESSAGE_LEN: usize = 512; @@ -7,8 +8,16 @@ const MESSAGE_TEXT_PARAM_KEY: u8 = 2; // in; str const CHANNEL_NAME_PARAM_KEY: u8 = 3; // in; str const CHAT_LOCATION_PARAM_KEY: u8 = 29; // in; str -pub fn send_public_message_handler(chat_system: crate::state::chat::ChatImpl) -> crate::SimpleChatFunc<2, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy, &crate::state::chat::ChatImpl) -> Result) + Sync + Sync> { - crate::SimpleChatFunc::new(|params, user: &crate::UserTy, chat| { +pub(super) struct PublicMessageSender { + chat: crate::state::chat::ChatImpl +} + +#[async_trait::async_trait] +impl SimpleOperation for PublicMessageSender { + type User = crate::UserTy; + const CODE: u8 = 2; + + async fn handle(&self, params: ParameterTable, user: &Self::User) -> Result, SimpleOpError> { let mut params = params.to_dict(); if let Some(Typed::Int(channel_ty)) = params.remove(&CHANNEL_TYPE_PARAM_KEY) { let channel_enum = crate::data::channel::ChatChannelType::from_u8(channel_ty as u8)?; @@ -17,45 +26,65 @@ pub fn send_public_message_handler(chat_system: crate::state::chat::ChatImpl) -> let user = user.user()?; if message_text.string.bytes().len() > MAX_MESSAGE_LEN { log::warn!("Rejecting too long chat message from {}", user.public_id()); - return Err(oj_rc_core::data::error_codes::ChatErrorCodes::Flood as i16) + return Err((oj_rc_core::data::error_codes::ChatErrorCodes::Flood as i16).into()) } let chat_loc = if let Some(Typed::Str(chat_loc)) = params.remove(&CHAT_LOCATION_PARAM_KEY) { chat_loc.string.clone() } else { "".to_owned() }; - let chat_system = chat.system(); + let chat_system = self.chat.system().await; log::debug!("Got message `{}` from user {} ({} @ {}/{:?})", message_text.string, user.public_id(), chat_loc, channel_name.string, channel_enum); - chat_system.handle_public_message(user.as_ref().as_ref(), message_text.string, channel_name.string, channel_enum); + chat_system.handle_public_message(user.as_ref().as_ref(), message_text.string, channel_name.string, channel_enum).await; } } } Ok(params.into()) - }, chat_system) + } +} + +pub(super) fn send_public_message_handler(chat_system: crate::state::chat::ChatImpl) -> SimpleOpImpl { + SimpleOpImpl::new(PublicMessageSender { + chat: chat_system, + }) } pub const USERNAME_PARAM_KEY: u8 = 7; // in; str -pub fn send_private_message_handler(chat_system: crate::state::chat::ChatImpl) -> crate::SimpleChatFunc<3, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy, &crate::state::chat::ChatImpl) -> Result) + Sync + Sync> { - crate::SimpleChatFunc::new(|params, user: &crate::UserTy, chat| { +pub(super) struct PrivateMessageSender { + chat: crate::state::chat::ChatImpl +} + +#[async_trait::async_trait] +impl SimpleOperation for PrivateMessageSender { + type User = crate::UserTy; + const CODE: u8 = 3; + + async fn handle(&self, params: ParameterTable, user: &Self::User) -> Result, SimpleOpError> { let mut params = params.to_dict(); if let Some(Typed::Str(username)) = params.remove(&USERNAME_PARAM_KEY) { if let Some(Typed::Str(message_text)) = params.remove(&MESSAGE_TEXT_PARAM_KEY) { let user = user.user()?; if message_text.string.bytes().len() > MAX_MESSAGE_LEN { log::warn!("Rejecting too long chat message from {}", user.public_id()); - return Err(oj_rc_core::data::error_codes::ChatErrorCodes::Flood as i16) + return Err((oj_rc_core::data::error_codes::ChatErrorCodes::Flood as i16).into()) } let chat_loc = if let Some(Typed::Str(chat_loc)) = params.remove(&CHAT_LOCATION_PARAM_KEY) { chat_loc.string.clone() } else { "".to_owned() }; - let chat_system = chat.system(); + let chat_system = self.chat.system().await; log::debug!("Got message `{}` from user {} (@ {} to {})", message_text.string, user.public_id(), chat_loc, username.string); chat_system.handle_private_message(user.as_ref().as_ref(), message_text.string, username.string); } } Ok(params.into()) - }, chat_system) + } +} + +pub fn send_private_message_handler(chat_system: crate::state::chat::ChatImpl) -> SimpleOpImpl { + SimpleOpImpl::new(PrivateMessageSender { + chat: chat_system, + }) } diff --git a/rc_chat_room/src/operations/user_online.rs b/rc_chat_room/src/operations/user_online.rs index 12d0551..6f7add1 100644 --- a/rc_chat_room/src/operations/user_online.rs +++ b/rc_chat_room/src/operations/user_online.rs @@ -1,4 +1,4 @@ -use crate::SimpleChatFunc; +use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl}; use polariton::operation::{ParameterTable, Typed}; const USERNAME_PARAM_KEY: u8 = 22; // str; in & out @@ -13,14 +13,29 @@ enum CanSendMessageResult { UserOffline = 2, } +const CODE: u8 = 14; -pub(super) fn is_online_provider(chat_system: crate::state::ChatImpl) -> SimpleChatFunc<14, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy, &crate::state::ChatImpl) -> Result) + Sync + Sync> { - SimpleChatFunc::new(|params, _: &crate::UserTy, chat_system| { +pub(super) struct OnlineChecker { + chat_system: crate::state::chat::ChatImpl +} + +pub(super) fn is_online_provider(chat_system: crate::state::chat::ChatImpl) -> SimpleOpImpl<(), crate::UserTy, OnlineChecker> { + SimpleOpImpl::new(OnlineChecker { + chat_system, + }) +} + +#[async_trait::async_trait] +impl SimpleOperation<()> for OnlineChecker { + type User = crate::UserTy; + const CODE: u8 = CODE; + + async fn handle(&self, params: ParameterTable<()>, _user: &Self::User) -> Result, SimpleOpError> { let mut params = params.to_dict(); if let Some(Typed::Str(user_name)) = params.get(&USERNAME_PARAM_KEY) { let username = user_name.string.clone(); params.insert(DISPLAY_NAME_PARAM_KEY, Typed::Str(username.clone().into())); - if chat_system.system().is_user_online(&username) { + if self.chat_system.system().await.is_user_online(&username) { params.insert(CAN_SEND_DM_PARAM_KEY, Typed::Int(CanSendMessageResult::Ok as _)); Ok(params.into()) } else { @@ -30,7 +45,7 @@ pub(super) fn is_online_provider(chat_system: crate::state::ChatImpl) -> SimpleC Ok(params.into()) } } else { - Err(oj_rc_core::data::error_codes::ChatErrorCodes::UnexpectedError as _) + Err((oj_rc_core::data::error_codes::ChatErrorCodes::UnexpectedError as i16).into()) } - }, chat_system) + } } diff --git a/rc_chat_room/src/state/chat/chat.rs b/rc_chat_room/src/state/chat/chat.rs index 79c2b38..b39a6e2 100644 --- a/rc_chat_room/src/state/chat/chat.rs +++ b/rc_chat_room/src/state/chat/chat.rs @@ -2,28 +2,29 @@ use std::collections::HashMap; #[derive(Clone)] pub struct ChatProvider { - chat_system: std::sync::Arc>, + chat_system: std::sync::Arc>, } impl ChatProvider { pub fn new(conf: oj_rc_core::persist::config::ChatSystemConfig) -> std::io::Result { Ok(Self { - chat_system: std::sync::Arc::new(std::sync::RwLock::new(crate::state::chat::ChatSystem::new(conf)?)), + chat_system: std::sync::Arc::new(tokio::sync::RwLock::new(crate::state::chat::ChatSystem::new(conf)?)), }) } - pub fn system(&self) -> std::sync::RwLockReadGuard<'_, crate::state::chat::ChatSystem> { - self.chat_system.read().unwrap() + pub async fn system(&self) -> tokio::sync::RwLockReadGuard<'_, crate::state::chat::ChatSystem> { + self.chat_system.read().await } - pub fn system_mut(&self) -> std::sync::RwLockWriteGuard<'_, crate::state::chat::ChatSystem> { - self.chat_system.write().unwrap() + pub async fn system_mut(&self) -> tokio::sync::RwLockWriteGuard<'_, crate::state::chat::ChatSystem> { + self.chat_system.write().await } } pub struct ChatSystem { chats: HashMap, online_users: HashMap, + battle_cache: tokio::sync::RwLock>>, config: super::ChatSystemConfig, } @@ -83,7 +84,7 @@ impl ChatSystem { } } - pub fn handle_public_message(&self, user: &dyn oj_rc_core::persist::user::User<()>, text: String, channel: String, channel_ty: crate::data::channel::ChatChannelType) { + pub async fn handle_public_message(&self, user: &(dyn oj_rc_core::persist::user::User<()> + Send + Sync), text: String, channel: String, channel_ty: crate::data::channel::ChatChannelType) { if self.config.is_command_channel(&channel) { if let Some(user_handle) = self.online_users.get(user.public_id()) { self.handle_public_command(user, text, user_handle, channel, channel_ty); @@ -100,6 +101,58 @@ impl ChatSystem { channel_ty, }; room.send_public_message(event_params); + } else { + match channel_ty { + crate::data::channel::ChatChannelType::Battle | crate::data::channel::ChatChannelType::BattleTeam => { + let relevant_players = if let Some(cached) = self.battle_cache.read().await.get(&channel) { + cached.to_owned() + } else if matches!(channel_ty, crate::data::channel::ChatChannelType::BattleTeam) { + if let Ok(players) = user.get_teammates().await { + self.battle_cache.write().await.insert(channel.clone(), players.clone()); + players + } else { + Vec::default() + } + } else { + if let Ok(players) = user.get_gamemates().await { + self.battle_cache.write().await.insert(channel.clone(), players.clone()); + players + } else { + Vec::default() + } + }; + if !relevant_players.is_empty() { + let event_params = crate::events::chat_message::PublicMessage { + sender_name: user.public_id().to_owned(), + sender_display_name: user.display_name().to_owned(), + text, + is_dev: user.is_dev(), + is_mod: user.is_mod(), + is_admin: user.is_admin(), + channel_name: channel, + channel_ty, + }; + let event = polariton::operation::Event { + code: crate::events::chat_message::PublicMessage::CODE, + params: event_params.as_event_params(), + }; + for handle in self.online_users.values() { + if handle.name() == user.public_id() { continue; } + if relevant_players.contains(&handle.name().to_owned()) { + handle.send(polariton_server::ToSend::Data { + data: polariton::packet::Data::Event(event.clone()), + encrypt: true, + channel: 0, + reliable: true, + }); + } + } + } + }, + _ => { + log::warn!("Got message for non-existent chat room {} (variant: {:?})", channel, channel_ty); + } + } } } @@ -165,6 +218,7 @@ impl ChatSystem { Ok(Self { chats: HashMap::new(), online_users: HashMap::new(), + battle_cache: tokio::sync::RwLock::new(HashMap::new()), config: super::ChatSystemConfig::from_persist(config)?, }) } diff --git a/rc_core/src/persist/user/account_json.rs b/rc_core/src/persist/user/account_json.rs index a476d30..268d995 100644 --- a/rc_core/src/persist/user/account_json.rs +++ b/rc_core/src/persist/user/account_json.rs @@ -555,6 +555,54 @@ impl UserData { } Ok(players) } + + async fn get_players_in_current_game(&self) -> Result, polariton_server::operations::SimpleOpError> { + let current_game = self.db.game_by_user_id_and_completion(self.account.id, false).await + .map_err(|e| { + log::error!("Failed to retrieve ongoing game for user {}: {}", self.account.id, e); + polariton_server::operations::SimpleOpError::with_message( + crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16, + format!("Failed to retrieve ongoing game for user {}", self.account.id), + ) + })?; + if let Some(current_game) = current_game { + let guid = current_game.guid; + self.db.players_by_game_guid_and_completion_heavy(current_game.guid, false).await + .map_err(|e| { + log::error!("Failed to retrieve players for game {} for user {}: {}", guid, self.account.id, e); + polariton_server::operations::SimpleOpError::with_message( + crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16, + format!("Failed to retrieve ongoing game {} for user {}", guid, self.account.id), + ) + }) + } else { + Ok(Vec::default()) + } + } + + async fn get_teammates_in_current_game(&self) -> Result, polariton_server::operations::SimpleOpError> { + let current_game_info = self.db.game_and_player_by_user_id_and_completion(self.account.id, false).await + .map_err(|e| { + log::error!("Failed to retrieve ongoing game for user {}: {}", self.account.id, e); + polariton_server::operations::SimpleOpError::with_message( + crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16, + format!("Failed to retrieve ongoing game for user {}", self.account.id), + ) + })?; + if let Some((current_game, current_player)) = current_game_info { + let guid = current_game.guid; + self.db.players_by_game_guid_and_completion_and_team_heavy(current_game.guid, current_player.team, false).await + .map_err(|e| { + log::error!("Failed to retrieve players for game {} for user {}: {}", guid, self.account.id, e); + polariton_server::operations::SimpleOpError::with_message( + crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16, + format!("Failed to retrieve ongoing game {} for user {}", guid, self.account.id), + ) + }) + } else { + Ok(Vec::default()) + } + } } const INVALID_ROBOT_ERR: i16 = crate::data::error_codes::WebServicesError::InvalidRobot as i16; // 140 @@ -1299,6 +1347,24 @@ impl super::ChatUser for UserData { Err(crate::data::error_codes::ChatErrorCodes::DoesNotExist as i16) } } + + async fn get_teammates(&self) -> Result, polariton_server::operations::SimpleOpError> { + Ok( + self.get_teammates_in_current_game().await? + .into_iter() + .map(|player| player.1.public_id) + .collect() + ) + } + + async fn get_gamemates(&self) -> Result, polariton_server::operations::SimpleOpError> { + Ok( + self.get_players_in_current_game().await? + .into_iter() + .map(|player| player.1.public_id) + .collect() + ) + } } #[async_trait::async_trait] diff --git a/rc_core/src/persist/user/traits.rs b/rc_core/src/persist/user/traits.rs index 6b14e1f..68401b8 100644 --- a/rc_core/src/persist/user/traits.rs +++ b/rc_core/src/persist/user/traits.rs @@ -211,6 +211,9 @@ pub trait ChatUser { //async fn has_pending_sanctions(&self) -> Result; async fn get_sanctions(&self, username: String) -> Result, i16>; async fn set_sanction(&self, sanction: SetSanction) -> Result<(), i16>; + // multiplayer-related + async fn get_teammates(&self) -> Result, polariton_server::operations::SimpleOpError>; + async fn get_gamemates(&self) -> Result, polariton_server::operations::SimpleOpError>; } pub struct SetSanction { diff --git a/rc_database/src/wrapper.rs b/rc_database/src/wrapper.rs index c7a46ab..b1e60ba 100644 --- a/rc_database/src/wrapper.rs +++ b/rc_database/src/wrapper.rs @@ -294,6 +294,19 @@ impl Database { .map(|(x, _)| x)) } + pub async fn game_and_player_by_user_id_and_completion(&self, user_id: i32, is_complete: bool) -> Result, sea_orm::DbErr> { + Ok(crate::schema::multiplayer_game::Entity::find() + .find_also_related(crate::schema::multiplayer_game_player::Entity) + //.join(sea_orm::JoinType::InnerJoin, crate::schema::multiplayer_game_player::Relation::Game.def()) + .filter(crate::schema::multiplayer_game::Column::IsComplete.eq(is_complete)) + .filter(crate::schema::multiplayer_game_player::Column::UserId.eq(user_id)) + .order_by_asc(crate::schema::multiplayer_game::Column::CreationTime) + //.into_model() + .one(&self.orm) + .await? + .and_then(|(game, player)| player.map(|player| (game, player)))) + } + pub async fn update_complete_game_by_game_guid(&self, game_guid: i64) -> Result<(), sea_orm::DbErr> { crate::schema::multiplayer_game::Entity::update_many() .col_expr(crate::schema::multiplayer_game::Column::IsComplete, sea_orm::sea_query::Expr::value(true)) @@ -341,6 +354,22 @@ impl Database { .collect()) } + pub async fn players_by_game_guid_and_completion_and_team_heavy(&self, game_guid: i64, team: i32, is_complete: bool) -> Result, sea_orm::DbErr> { + Ok(crate::schema::multiplayer_game_player::Entity::find() + .find_also_related(crate::schema::user::Entity) + .find_also_related(crate::schema::multiplayer_game::Entity) + //.join(sea_orm::JoinType::InnerJoin, crate::schema::multiplayer_game::Relation::Player.def()) + //.join(sea_orm::JoinType::InnerJoin, crate::schema::user::Relation::Player.def()) + .filter(crate::schema::multiplayer_game::Column::Guid.eq(game_guid)) + .filter(crate::schema::multiplayer_game::Column::IsComplete.eq(is_complete)) + .filter(crate::schema::multiplayer_game_player::Column::Team.eq(team)) + .all(&self.orm) + .await? + .into_iter() + .filter_map(|(player, user, _)| user.map(|user| (player, user))) + .collect()) + } + pub async fn players_by_game_id_and_completion(&self, game_id: i32) -> Result, sea_orm::DbErr> { crate::schema::multiplayer_game_player::Entity::find() .join(sea_orm::JoinType::InnerJoin, crate::schema::multiplayer_game::Relation::Player.def())