diff --git a/assets/robocraft/config.json b/assets/robocraft/config.json index 753ec37..ce871fc 100644 --- a/assets/robocraft/config.json +++ b/assets/robocraft/config.json @@ -14528,6 +14528,15 @@ }, "permission": "Administrator" }, + { + "regex": "\\?klf", + "op": { + "type": "BuiltIn", + "built_in": "Intercom", + "intercom": "KeybindLockFix" + }, + "permission": "Player" + }, { "regex": "\\?help", "op": { diff --git a/rc_chat_room/src/state/chat/config.rs b/rc_chat_room/src/state/chat/config.rs index fb9d956..2e2f196 100644 --- a/rc_chat_room/src/state/chat/config.rs +++ b/rc_chat_room/src/state/chat/config.rs @@ -269,6 +269,7 @@ enum Intercom { DevMessage, DevBroadcast, Maintenance, + KeybindLockFix, // https://git.ngram.ca/OpenJam/rc-servers/issues/127 } impl Intercom { @@ -277,6 +278,7 @@ impl Intercom { oj_rc_core::persist::IntercomChatOperation::DevMessage => Self::DevMessage, oj_rc_core::persist::IntercomChatOperation::DevBroadcast => Self::DevBroadcast, oj_rc_core::persist::IntercomChatOperation::Maintenance => Self::Maintenance, + oj_rc_core::persist::IntercomChatOperation::KeybindLockFix => Self::KeybindLockFix, } } @@ -317,7 +319,13 @@ impl Intercom { } else { "Missing maintenance message, did not send".to_owned() } - + } + Self::KeybindLockFix => { + ctx.user.trigger_workaround( + oj_rc_core::persist::user::intercom::IntercomWorkaroundMessage::KeybindLockout { }, + vec![ctx.user.public_id().to_owned()] + ).await; + "Triggered key lockout workaround".to_owned() } } @@ -328,6 +336,7 @@ impl Intercom { Self::DevMessage => "Show dev message to yourself".to_owned(), Self::DevBroadcast => "Show dev message to everyone".to_owned(), Self::Maintenance => "Broadcast maintenance mode to everyone".to_owned(), + Self::KeybindLockFix => "Trigger a custom game invite to fix the broken client UI; more info https://git.ngram.ca/OpenJam/rc-servers/issues/127".to_owned(), } } } diff --git a/rc_core/src/persist/chat.rs b/rc_core/src/persist/chat.rs index faf19fe..a5238c0 100644 --- a/rc_core/src/persist/chat.rs +++ b/rc_core/src/persist/chat.rs @@ -132,6 +132,7 @@ pub enum IntercomChatOperation { DevMessage, DevBroadcast, Maintenance, + KeybindLockFix, // https://git.ngram.ca/OpenJam/rc-servers/issues/127 } #[derive(Serialize, Deserialize, Clone, Debug)] diff --git a/rc_core/src/persist/user/intercom.rs b/rc_core/src/persist/user/intercom.rs index dd918f9..e649713 100644 --- a/rc_core/src/persist/user/intercom.rs +++ b/rc_core/src/persist/user/intercom.rs @@ -118,6 +118,18 @@ impl super::IntercomUser for super::account_json::UserData { } } + async fn trigger_workaround(&self, msg: IntercomWorkaroundMessage, to: Vec) { + let send_to_everyone = to.is_empty(); + let data = IntercomWebServiceMessage { + public_ids: to, + data: IntercomWebServiceUserMessage::Workaround(msg), + everyone: send_to_everyone, + }; + if let Err(e) = self.post_to_intercom(&data, ".oj_services", "messages").await { + log::error!("Failed to send intercom workaround message: {}", e); + } + } + async fn update_custom_game(&self, msg: IntercomLobbyCustomGameDataMessage) { let data = IntercomLobbyStateMessage::CustomGame(msg); if let Err(e) = self.post_to_intercom(&data, ".oj_lobby", "state").await { @@ -229,6 +241,7 @@ pub struct IntercomWebServiceMessage { pub enum IntercomWebServiceUserMessage { DevMessage(IntercomDevMessage), Maintenance(IntercomMaintenanceMessage), + Workaround(IntercomWorkaroundMessage), } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -242,6 +255,14 @@ pub struct IntercomMaintenanceMessage { pub message: String, } +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "workaround")] +pub enum IntercomWorkaroundMessage { + /// Trigger fix for getting stuck in build mode due to a bad/slow connection + /// more info: https://git.ngram.ca/OpenJam/rc-servers/issues/127 + KeybindLockout { }, +} + pub fn generate_token(salt: &[u8], key: &[u8]) -> String { use sha2::{Digest, Sha512}; let mut hasher = Sha512::new(); diff --git a/rc_core/src/persist/user/traits.rs b/rc_core/src/persist/user/traits.rs index 7b1b56b..f1bb124 100644 --- a/rc_core/src/persist/user/traits.rs +++ b/rc_core/src/persist/user/traits.rs @@ -434,6 +434,7 @@ pub trait IntercomUser: CommonUser { async fn webservice_listener(&self) -> Result, polariton_server::operations::SimpleOpError>; async fn show_dev_message(&self, msg: super::intercom::IntercomDevMessage, to: Vec); async fn enter_maintenance(&self, msg: super::intercom::IntercomMaintenanceMessage, to: Vec); + async fn trigger_workaround(&self, msg: super::intercom::IntercomWorkaroundMessage, to: Vec); async fn update_custom_game(&self, msg: super::intercom::IntercomLobbyCustomGameDataMessage); async fn update_status(&self, server_name: &str, msg: oj_serdes::ServerStatus); } diff --git a/rc_services_room/src/custom_game_tracker.rs b/rc_services_room/src/custom_game_tracker.rs index 35fa874..94642d6 100644 --- a/rc_services_room/src/custom_game_tracker.rs +++ b/rc_services_room/src/custom_game_tracker.rs @@ -307,6 +307,14 @@ struct GameConfig { max_cpu: i32, } +pub fn game_config_default_map() -> std::collections::HashMap { + GameConfig::default().as_map() +} + +pub fn game_config_default_core() -> oj_rc_core::persist::user::intercom::IntercomLobbyCustomGameConfig { + GameConfig::default().as_core() +} + impl core::default::Default for GameConfig { fn default() -> Self { Self { diff --git a/rc_services_room/src/events/handler.rs b/rc_services_room/src/events/handler.rs index 5a2beb0..5045920 100644 --- a/rc_services_room/src/events/handler.rs +++ b/rc_services_room/src/events/handler.rs @@ -1,10 +1,11 @@ use oj_rc_core::persist::user::IntercomListener; -use oj_rc_core::persist::user::intercom::IntercomWebServiceUserMessage; +use oj_rc_core::persist::user::intercom::{IntercomWebServiceUserMessage, IntercomWorkaroundMessage}; pub struct IntercomHandler { listener: IntercomListener, user: std::sync::Weak + Send + Sync>>, emitter: polariton_server::events::WeakEventEmitter<()>, + keybind_workaround: std::sync::Arc, } impl IntercomHandler { @@ -12,25 +13,28 @@ impl IntercomHandler { listener: IntercomListener, user: &std::sync::Arc + Send + Sync>>, emitter: &polariton_server::events::EventEmitter<()>, + keybind_workaround: &std::sync::Arc, ) -> Self { Self { listener, user: std::sync::Arc::downgrade(user), emitter: emitter.to_owned().downgrade(), + keybind_workaround: keybind_workaround.to_owned(), } } async fn run_loop( listener: IntercomListener, user: std::sync::Weak + Send + Sync>>, - emitter: polariton_server::events::WeakEventEmitter<()> + emitter: polariton_server::events::WeakEventEmitter<()>, + keybind_workaround: std::sync::Arc, ) { use futures::StreamExt; let mut listener = listener.listen().await; while let Some(msg) = listener.next().await { match msg { Ok(msg) => { - if let Some(_user) = user.upgrade() { + if let Some(user) = user.upgrade() { match msg { IntercomWebServiceUserMessage::DevMessage(msg) => { let clear_event = super::DevMessage { @@ -50,6 +54,18 @@ impl IntercomHandler { }; emitter.emit(event); } + IntercomWebServiceUserMessage::Workaround(IntercomWorkaroundMessage::KeybindLockout { }) => { + let session = keybind_workaround.add_user(user.account_id(), user.public_id().to_owned()).await; + let non_me = session.users.iter().next().unwrap(); + let event = super::CustomGameInvite { + inviter_public_id: non_me.public_id.clone(), + inviter_display_name: non_me.public_id.clone(), + session: session.session_id, + avatar_id: Some(6), + invited_to_team_a: true, + }; + emitter.emit(event); + } } } else { break; @@ -64,6 +80,6 @@ impl IntercomHandler { } pub fn run(self) -> tokio::task::JoinHandle<()> { - tokio::spawn(Self::run_loop(self.listener, self.user, self.emitter)) + tokio::spawn(Self::run_loop(self.listener, self.user, self.emitter, self.keybind_workaround)) } } diff --git a/rc_services_room/src/main.rs b/rc_services_room/src/main.rs index 79b8d2a..8c4ec02 100644 --- a/rc_services_room/src/main.rs +++ b/rc_services_room/src/main.rs @@ -7,6 +7,7 @@ mod operations; mod vehicle_validators; mod custom_game_tracker; mod user_service; +mod workarounds; use oj_polariton_auth::Handshake; use tokio::net; @@ -29,6 +30,7 @@ pub struct InitConfig { pub vehicle_validators: vehicle_validators::InitedVehicleValidators, pub custom_games: std::sync::Arc, pub user_mesh: std::sync::Arc, + pub workarounds: workarounds::Workarounds, } #[tokio::main] @@ -55,6 +57,7 @@ async fn main() -> std::io::Result<()> { vehicle_validators, custom_games: std::sync::Arc::new(custom_game_tracker::CustomGameMesh::new()), user_mesh: std::sync::Arc::new(user_service::UserMesh::new()), + workarounds: workarounds::Workarounds::new(), }); let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler(&init_ctx), polariton_server::events::EventsHandler::new())); diff --git a/rc_services_room/src/operations/custom_game_invite_respond.rs b/rc_services_room/src/operations/custom_game_invite_respond.rs index 0644373..d84a30a 100644 --- a/rc_services_room/src/operations/custom_game_invite_respond.rs +++ b/rc_services_room/src/operations/custom_game_invite_respond.rs @@ -9,6 +9,7 @@ const RESPONSE_CODE_PARAM_KEY: u8 = 168; // int enum; out pub(super) struct CustomGameInviteResponder { games: std::sync::Arc, mesh: std::sync::Arc, + keylock_workaround: std::sync::Arc, } #[async_trait::async_trait] @@ -20,38 +21,59 @@ impl SimpleOperation for CustomGameInviteResponder { if let Some(Typed::Bool(is_accept)) = params.remove(&ACCEPT_PARAM_KEY) { let user_info = user.user()?; let my_pub_id = user_info.public_id(); - let (resp_code, session_opt) = self.games.update_invite_user(my_pub_id, is_accept).await; - if let Some(session) = session_opt { - if !is_accept { - let event = crate::events::CustomGameInviteDecline { - public_id: my_pub_id.to_owned(), + if let Some(session) = self.keylock_workaround.get_user(user_info.account_id(), my_pub_id).await { + if is_accept { + self.keylock_workaround.accept_invite(user_info.account_id()).await; + let keylock_workaround = self.keylock_workaround.clone(); + let account_id = user_info.account_id(); + let my_owned_pub_id = my_pub_id.to_owned(); + let mesh = self.mesh.clone(); + tokio::task::spawn(async move { + tokio::time::sleep(std::time::Duration::from_millis(5_000)).await; + keylock_workaround.remove_user(account_id).await; + mesh.send_event_to(&my_owned_pub_id, crate::events::CustomGameKick { + session: session.session_id, + was_invited: false, + }).await; + }); + } else { + self.keylock_workaround.remove_user(user_info.account_id()).await; + } + params.insert(RESPONSE_CODE_PARAM_KEY, Typed::Int(crate::data::custom_games::InviteReplyCustomGameResponseCode::Success as _)); + } else { + let (resp_code, session_opt) = self.games.update_invite_user(my_pub_id, is_accept).await; + if let Some(session) = session_opt { + if !is_accept { + let event = crate::events::CustomGameInviteDecline { + public_id: my_pub_id.to_owned(), + }; + let session_members_iter = session.users.iter() + .filter(|mem| !mem.is_invited && mem.public_id != my_pub_id) + .map(|mem| &mem.public_id as &str); + self.mesh.broadcast_event_to(session_members_iter, event).await; + } else { + user_info.update_custom_game(oj_rc_core::persist::user::intercom::IntercomLobbyCustomGameDataMessage { + session_id: session.session_id.clone(), + config: session.config_core, + users: session.users.iter() + .filter(|user| !user.is_invited) + .map(|user| oj_rc_core::persist::user::intercom::IntercomLobbyCustomGameUserData { + public_id: user.public_id.clone(), + team: user.team, + }) + .collect() + }).await; + } + let event = crate::events::CustomGameRefresh { + session: session.session_id, }; - let session_members_iter = session.users.iter() + let other_session_members_iter = session.users.iter() .filter(|mem| !mem.is_invited && mem.public_id != my_pub_id) .map(|mem| &mem.public_id as &str); - self.mesh.broadcast_event_to(session_members_iter, event).await; - } else { - user_info.update_custom_game(oj_rc_core::persist::user::intercom::IntercomLobbyCustomGameDataMessage { - session_id: session.session_id.clone(), - config: session.config_core, - users: session.users.iter() - .filter(|user| !user.is_invited) - .map(|user| oj_rc_core::persist::user::intercom::IntercomLobbyCustomGameUserData { - public_id: user.public_id.clone(), - team: user.team, - }) - .collect() - }).await; + self.mesh.broadcast_event_to(other_session_members_iter, event).await; } - let event = crate::events::CustomGameRefresh { - session: session.session_id, - }; - let other_session_members_iter = session.users.iter() - .filter(|mem| !mem.is_invited && mem.public_id != my_pub_id) - .map(|mem| &mem.public_id as &str); - self.mesh.broadcast_event_to(other_session_members_iter, event).await; - } - params.insert(RESPONSE_CODE_PARAM_KEY, Typed::Int(resp_code as _)); + params.insert(RESPONSE_CODE_PARAM_KEY, Typed::Int(resp_code as _)); + }; } Ok(params) } @@ -61,5 +83,6 @@ pub(super) fn game_invite_respond_provider(init_ctx: &crate:: SimpleOpImpl::new(CustomGameInviteResponder { games: init_ctx.custom_games.clone(), mesh: init_ctx.user_mesh.clone(), + keylock_workaround: init_ctx.workarounds.edit_mode_input_lockup(), }) } diff --git a/rc_services_room/src/operations/custom_game_session.rs b/rc_services_room/src/operations/custom_game_session.rs index bf85ebf..bdec94b 100644 --- a/rc_services_room/src/operations/custom_game_session.rs +++ b/rc_services_room/src/operations/custom_game_session.rs @@ -8,6 +8,7 @@ const RESPONSE_DATA_PARAM_KEY: u8 = 169; // hashtable; out pub(super) struct CustomGameRetriever { games: std::sync::Arc, + keylock_workaround: std::sync::Arc, } #[async_trait::async_trait] @@ -18,7 +19,15 @@ impl SimpleOperation for CustomGameRetriever { async fn handle(&self, mut params: ParameterTable, user: &Self::User) -> Result, SimpleOpError> { let user_info = user.user()?; let my_pub_id = user_info.public_id(); - let game_opt = self.games.get_user_game(my_pub_id).await; + let mut is_workaround = false; + let game_opt = if let Some(session) = self.keylock_workaround.get_user(user_info.account_id(), my_pub_id).await { + is_workaround = true; + Some(session) + } else if let Some(session) = self.games.get_user_game(my_pub_id).await { + Some(session) + } else { + None + }; if let Some(game) = game_opt { log::debug!("User {} retrieved their custom game session {} info", my_pub_id, game.session_id); params.insert(RESPONSE_CODE_PARAM_KEY, Typed::Int(crate::data::custom_games::SessionRetrieveResponse::SessionRetrieved as _)); @@ -26,8 +35,17 @@ impl SimpleOperation for CustomGameRetriever { .map(|member| member.public_id.clone()) .collect(); let avatars = user_info.list_avatar_info(&pub_ids).await?; - let avatar_map: std::collections::HashMap<_, _> = avatars.into_iter() + let mut avatar_map: std::collections::HashMap<_, _> = avatars.into_iter() .map(|avatar| (avatar.public_id.clone(), avatar)).collect(); + if is_workaround { + let leader = game.users.first().unwrap(); + avatar_map.insert(leader.public_id.clone(), oj_rc_core::persist::user::SocialInfo { + public_id: leader.public_id.clone(), + display_name: leader.public_id.clone(), + avatar_id: Some(6), + }); + } + let avatar_map = avatar_map; let resp = crate::data::custom_games::Session { leader: game.users.first().map(|leader| leader.public_id.clone()).unwrap_or_default(), session: game.session_id, @@ -63,9 +81,13 @@ impl SimpleOperation for CustomGameRetriever { } } -pub(super) fn custom_session_provider(games: &std::sync::Arc) -> SimpleOpImpl { +pub(super) fn custom_session_provider( + games: &std::sync::Arc, + keylock_workaround: std::sync::Arc, +) -> SimpleOpImpl { SimpleOpImpl::new(CustomGameRetriever { games: games.to_owned(), + keylock_workaround, }) } diff --git a/rc_services_room/src/operations/custom_games_invite.rs b/rc_services_room/src/operations/custom_games_invite.rs index 3db05dd..7fb7c65 100644 --- a/rc_services_room/src/operations/custom_games_invite.rs +++ b/rc_services_room/src/operations/custom_games_invite.rs @@ -10,6 +10,7 @@ const INVITE_PARAM_KEY: u8 = 189; // hashtable (refer to C# CheckIfHasBeenInvite pub(super) struct CustomGamePendingInvites { games: std::sync::Arc, + keylock_workaround: std::sync::Arc, } #[async_trait::async_trait] @@ -20,14 +21,33 @@ impl SimpleOperation for CustomGamePendingInvites { async fn handle(&self, mut params: ParameterTable, user: &Self::User) -> Result, SimpleOpError> { let user_info = user.user()?; let my_pub_id = user_info.public_id(); - if let Some(session) = self.games.get_user_game(my_pub_id).await { + let mut is_workaround = false; + let session = if let Some(session) = self.keylock_workaround.get_user(user_info.account_id(), my_pub_id).await { + log::debug!("User {} is in keylock workaround mode ({})", my_pub_id, session.session_id); + is_workaround = true; + Some(session) + } else if let Some(session) = self.games.get_user_game(my_pub_id).await { + Some(session) + } else { + None + }; + if let Some(session) = session { let myself = session.users.iter().find(|u| u.public_id == my_pub_id).unwrap(); if myself.is_invited { log::debug!("User {} has checked and is invited to custom game {}", my_pub_id, session.session_id); params.insert(RESULT_CODE_PARAM_KEY, Typed::Int(CustomGameInviteCode::PendingInvite as _)); // build invite data let leader = session.users.first().unwrap(); - let leader_avatar = user_info.list_avatar_info(std::slice::from_ref(&leader.public_id)).await?; + let leader_avatar = if is_workaround { + vec![oj_rc_core::persist::user::SocialInfo { + public_id: leader.public_id.clone(), + display_name: leader.public_id.clone(), + avatar_id: Some(6), + }] + } else { + user_info.list_avatar_info(std::slice::from_ref(&leader.public_id)).await? + }; + let resp = CustomGameInvite { inviter_public_id: leader_avatar[0].public_id.clone(), inviter_display_name: leader_avatar[0].display_name.clone(), @@ -51,6 +71,7 @@ impl SimpleOperation for CustomGamePendingInvites { pub(super) fn pending_invite_provider(init_ctx: &crate::InitConfig) -> SimpleOpImpl { SimpleOpImpl::new(CustomGamePendingInvites { games: init_ctx.custom_games.clone(), + keylock_workaround: init_ctx.workarounds.edit_mode_input_lockup(), }) } diff --git a/rc_services_room/src/operations/mod.rs b/rc_services_room/src/operations/mod.rs index 172016d..8de70ff 100644 --- a/rc_services_room/src/operations/mod.rs +++ b/rc_services_room/src/operations/mod.rs @@ -124,7 +124,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler OperationsHandler::new() .modify(>::polariton_operation_modifier(&init_ctx.cubes)) .add(eac::EacChallengeIgnorer) - .add(more_auth::more_auth_provider(&init_ctx.user_mesh)) + .add(more_auth::more_auth_provider(&init_ctx.user_mesh, init_ctx.workarounds.edit_mode_input_lockup())) .add(versioner::version_teller(&init_ctx.cubes)) .add(maintenancer::maintenace_teller(&init_ctx.cubes)) .add(game_quality::QualityConfigTeller) @@ -163,7 +163,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler .add(dev_message::dev_message_provider(&init_ctx.cubes)) .add(custom_games_maps::allowed_maps_provider()) .add(avatar_info::avatar_get_provider()) - .add(custom_game_session::custom_session_provider(&init_ctx.custom_games)) + .add(custom_game_session::custom_session_provider(&init_ctx.custom_games, init_ctx.workarounds.edit_mode_input_lockup())) .add(user_xp::get_user_xp_provider()) .add(garage_upgrades::garage_upgrades_provider(&init_ctx.cubes)) .add(game_event_params::event_system_params_provider(&init_ctx.cubes)) diff --git a/rc_services_room/src/operations/more_auth.rs b/rc_services_room/src/operations/more_auth.rs index 82247f5..623f0e7 100644 --- a/rc_services_room/src/operations/more_auth.rs +++ b/rc_services_room/src/operations/more_auth.rs @@ -3,11 +3,13 @@ use polariton_server::operations::{Operation, OperationCode}; pub struct MoreLobbyAuth { mesh: std::sync::Arc, + keybind_workaround: std::sync::Arc, } -pub fn more_auth_provider(mesh: &std::sync::Arc) -> MoreLobbyAuth { +pub fn more_auth_provider(mesh: &std::sync::Arc, keybind_workaround: std::sync::Arc) -> MoreLobbyAuth { MoreLobbyAuth { - mesh: mesh.to_owned() + mesh: mesh.to_owned(), + keybind_workaround, } } @@ -43,7 +45,12 @@ impl Operation for MoreLobbyAuth { crate::update_status(user_info.as_ref().as_ref()).await; let mut resp_params = std::collections::HashMap::with_capacity(1); resp_params.insert(Self::AUTH_PAYLOAD_KEY, polariton::operation::Typed::Byte(0)); - crate::events::IntercomHandler::new(listener, &user_info, user.event_sender()).run(); + crate::events::IntercomHandler::new( + listener, + &user_info, + user.event_sender(), + &self.keybind_workaround, + ).run(); return polariton::operation::OperationResponse { code: Self::op_code(), return_code: 0, diff --git a/rc_services_room/src/workarounds/input_lockup.rs b/rc_services_room/src/workarounds/input_lockup.rs new file mode 100644 index 0000000..358120f --- /dev/null +++ b/rc_services_room/src/workarounds/input_lockup.rs @@ -0,0 +1,113 @@ +// If you lose connection in edit mode and then open chat, you will lose all keyboard input +// until you receive a party or game invite +// https://git.ngram.ca/OpenJam/rc-servers/issues/127 +// +// This implementation sends a game invite since it's on the same server + +use crate::custom_game_tracker::{SessionInfo, UserInfo}; + +const WORKAROUND_SESSION_ID: &str = "sys_0_cg|workaround"; +const WORKAROUND_PUBLIC_ID: &str = "MrWorkaround66 sys"; + +#[repr(u8)] +#[derive(Clone, Copy)] +enum InviteState { + Invited, + Accepted, +} + +impl InviteState { + #[inline] + fn from_u8(num: u8) -> Self { + match num { + 0 => Self::Invited, + 1 => Self::Accepted, + _ => panic!("Invalid InviteState {}", num), + } + } + + #[inline] + fn to_u8(self) -> u8 { + self as u8 + } +} + +pub struct EditModeInputLockupWorkaround { + active_users: tokio::sync::RwLock>, + default_config_map: std::collections::HashMap, + default_config_core: oj_rc_core::persist::user::intercom::IntercomLobbyCustomGameConfig, +} + +impl EditModeInputLockupWorkaround { + pub fn new() -> Self { + Self { + active_users: tokio::sync::RwLock::new(std::collections::HashMap::new()), + default_config_map: crate::custom_game_tracker::game_config_default_map(), + default_config_core: crate::custom_game_tracker::game_config_default_core(), + } + } + + pub async fn add_user(&self, id: i32, public_id: String) -> SessionInfo { + self.active_users.write().await.insert(id, std::sync::atomic::AtomicU8::new(InviteState::Invited.to_u8())); + SessionInfo { + session_id: WORKAROUND_SESSION_ID.to_owned(), + config: self.default_config_map.clone(), + config_core: self.default_config_core.clone(), + users: vec![ + UserInfo { + public_id: WORKAROUND_PUBLIC_ID.to_owned(), + is_invited: false, + team: 1, + state: crate::data::custom_games::PlayerSessionStatus::Ready, + }, + UserInfo { + public_id, + is_invited: true, + team: 0, + state: crate::data::custom_games::PlayerSessionStatus::Ready, + } + ], + } + } + + pub async fn get_user(&self, id: i32, public_id: &str) -> Option { + if let Some(state) = self.active_users.read().await.get(&id) { + let state = InviteState::from_u8(state.load(std::sync::atomic::Ordering::Relaxed)); + let is_invited = matches!(state, InviteState::Invited); + Some(SessionInfo { + session_id: WORKAROUND_SESSION_ID.to_owned(), + config: self.default_config_map.clone(), + config_core: self.default_config_core.clone(), + users: vec![ + UserInfo { + public_id: WORKAROUND_PUBLIC_ID.to_owned(), + is_invited: false, + team: 1, + state: crate::data::custom_games::PlayerSessionStatus::Ready, + }, + UserInfo { + public_id: public_id.to_owned(), + is_invited, + team: 0, + state: crate::data::custom_games::PlayerSessionStatus::Ready, + } + ], + }) + } else { + None + } + } + + pub async fn accept_invite(&self, id: i32) { + let lock = self.active_users.read().await; + if let Some(state) = lock.get(&id) { + state.store(InviteState::Accepted.to_u8(), std::sync::atomic::Ordering::Relaxed); + } else { + log::warn!("Tried to use EditModeInputLockupWorkaround for non-added user {}", id); + } + } + + pub async fn remove_user(&self, id: i32) { + self.active_users.write().await.remove(&id); + } +} diff --git a/rc_services_room/src/workarounds/mod.rs b/rc_services_room/src/workarounds/mod.rs new file mode 100644 index 0000000..c84df28 --- /dev/null +++ b/rc_services_room/src/workarounds/mod.rs @@ -0,0 +1,18 @@ +mod input_lockup; +pub use input_lockup::EditModeInputLockupWorkaround; + +pub struct Workarounds { + emil: std::sync::Arc, +} + +impl Workarounds { + pub fn new() -> Self { + Self { + emil: std::sync::Arc::new(EditModeInputLockupWorkaround::new()) + } + } + + pub fn edit_mode_input_lockup(&self) -> std::sync::Arc { + self.emil.clone() + } +}