1
0
mirror of https://git.ngram.ca/OpenJam/rc-servers synced 2026-08-23 23:08:52 +00:00

Add workaround to fix #127

This commit is contained in:
NG (Graham)
2026-08-08 16:22:12 -04:00
parent 5be8610e8a
commit da36c702e1
15 changed files with 315 additions and 43 deletions

View File

@@ -14528,6 +14528,15 @@
},
"permission": "Administrator"
},
{
"regex": "\\?klf",
"op": {
"type": "BuiltIn",
"built_in": "Intercom",
"intercom": "KeybindLockFix"
},
"permission": "Player"
},
{
"regex": "\\?help",
"op": {

View File

@@ -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(),
}
}
}

View File

@@ -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)]

View File

@@ -118,6 +118,18 @@ impl super::IntercomUser for super::account_json::UserData {
}
}
async fn trigger_workaround(&self, msg: IntercomWorkaroundMessage, to: Vec<String>) {
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();

View File

@@ -434,6 +434,7 @@ pub trait IntercomUser: CommonUser {
async fn webservice_listener(&self) -> Result<IntercomListener<super::intercom::IntercomWebServiceUserMessage>, polariton_server::operations::SimpleOpError>;
async fn show_dev_message(&self, msg: super::intercom::IntercomDevMessage, to: Vec<String>);
async fn enter_maintenance(&self, msg: super::intercom::IntercomMaintenanceMessage, to: Vec<String>);
async fn trigger_workaround(&self, msg: super::intercom::IntercomWorkaroundMessage, to: Vec<String>);
async fn update_custom_game(&self, msg: super::intercom::IntercomLobbyCustomGameDataMessage);
async fn update_status(&self, server_name: &str, msg: oj_serdes::ServerStatus);
}

View File

@@ -307,6 +307,14 @@ struct GameConfig {
max_cpu: i32,
}
pub fn game_config_default_map() -> std::collections::HashMap<String, String> {
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 {

View File

@@ -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<IntercomWebServiceUserMessage>,
user: std::sync::Weak<Box<dyn oj_rc_core::persist::user::User<()> + Send + Sync>>,
emitter: polariton_server::events::WeakEventEmitter<()>,
keybind_workaround: std::sync::Arc<crate::workarounds::EditModeInputLockupWorkaround>,
}
impl IntercomHandler {
@@ -12,25 +13,28 @@ impl IntercomHandler {
listener: IntercomListener<IntercomWebServiceUserMessage>,
user: &std::sync::Arc<Box<dyn oj_rc_core::persist::user::User<()> + Send + Sync>>,
emitter: &polariton_server::events::EventEmitter<()>,
keybind_workaround: &std::sync::Arc<crate::workarounds::EditModeInputLockupWorkaround>,
) -> 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<IntercomWebServiceUserMessage>,
user: std::sync::Weak<Box<dyn oj_rc_core::persist::user::User<()> + Send + Sync>>,
emitter: polariton_server::events::WeakEventEmitter<()>
emitter: polariton_server::events::WeakEventEmitter<()>,
keybind_workaround: std::sync::Arc<crate::workarounds::EditModeInputLockupWorkaround>,
) {
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))
}
}

View File

@@ -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<custom_game_tracker::CustomGameMesh>,
pub user_mesh: std::sync::Arc<user_service::UserMesh>,
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()));

View File

@@ -9,6 +9,7 @@ const RESPONSE_CODE_PARAM_KEY: u8 = 168; // int enum; out
pub(super) struct CustomGameInviteResponder {
games: std::sync::Arc<crate::custom_game_tracker::CustomGameMesh>,
mesh: std::sync::Arc<crate::user_service::UserMesh>,
keylock_workaround: std::sync::Arc<crate::workarounds::EditModeInputLockupWorkaround>,
}
#[async_trait::async_trait]
@@ -20,38 +21,59 @@ impl <C: Send + 'static> SimpleOperation<C> 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<C: Send + 'static>(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(),
})
}

View File

@@ -8,6 +8,7 @@ const RESPONSE_DATA_PARAM_KEY: u8 = 169; // hashtable; out
pub(super) struct CustomGameRetriever {
games: std::sync::Arc<crate::custom_game_tracker::CustomGameMesh>,
keylock_workaround: std::sync::Arc<crate::workarounds::EditModeInputLockupWorkaround>,
}
#[async_trait::async_trait]
@@ -18,7 +19,15 @@ impl <C: Send + 'static> SimpleOperation<C> for CustomGameRetriever {
async fn handle(&self, mut params: ParameterTable<C>, user: &Self::User) -> Result<ParameterTable<C>, 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 <C: Send + 'static> SimpleOperation<C> 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 <C: Send + 'static> SimpleOperation<C> for CustomGameRetriever {
}
}
pub(super) fn custom_session_provider<C: Send + 'static>(games: &std::sync::Arc<crate::custom_game_tracker::CustomGameMesh>) -> SimpleOpImpl<C, crate::UserTy, CustomGameRetriever> {
pub(super) fn custom_session_provider<C: Send + 'static>(
games: &std::sync::Arc<crate::custom_game_tracker::CustomGameMesh>,
keylock_workaround: std::sync::Arc<crate::workarounds::EditModeInputLockupWorkaround>,
) -> SimpleOpImpl<C, crate::UserTy, CustomGameRetriever> {
SimpleOpImpl::new(CustomGameRetriever {
games: games.to_owned(),
keylock_workaround,
})
}

View File

@@ -10,6 +10,7 @@ const INVITE_PARAM_KEY: u8 = 189; // hashtable (refer to C# CheckIfHasBeenInvite
pub(super) struct CustomGamePendingInvites {
games: std::sync::Arc<crate::custom_game_tracker::CustomGameMesh>,
keylock_workaround: std::sync::Arc<crate::workarounds::EditModeInputLockupWorkaround>,
}
#[async_trait::async_trait]
@@ -20,14 +21,33 @@ impl <C: Send + 'static> SimpleOperation<C> for CustomGamePendingInvites {
async fn handle(&self, mut params: ParameterTable<C>, user: &Self::User) -> Result<ParameterTable<C>, 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 <C: Send + 'static> SimpleOperation<C> for CustomGamePendingInvites {
pub(super) fn pending_invite_provider<C: Send + 'static>(init_ctx: &crate::InitConfig) -> SimpleOpImpl<C, crate::UserTy, CustomGamePendingInvites> {
SimpleOpImpl::new(CustomGamePendingInvites {
games: init_ctx.custom_games.clone(),
keylock_workaround: init_ctx.workarounds.edit_mode_input_lockup(),
})
}

View File

@@ -124,7 +124,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
OperationsHandler::new()
.modify(<oj_rc_core::ConfigImpl as oj_rc_core::ConfigProvider<()>>::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<crate::UserTy>
.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))

View File

@@ -3,11 +3,13 @@ use polariton_server::operations::{Operation, OperationCode};
pub struct MoreLobbyAuth {
mesh: std::sync::Arc<crate::user_service::UserMesh>,
keybind_workaround: std::sync::Arc<crate::workarounds::EditModeInputLockupWorkaround>,
}
pub fn more_auth_provider(mesh: &std::sync::Arc<crate::user_service::UserMesh>) -> MoreLobbyAuth {
pub fn more_auth_provider(mesh: &std::sync::Arc<crate::user_service::UserMesh>, keybind_workaround: std::sync::Arc<crate::workarounds::EditModeInputLockupWorkaround>) -> MoreLobbyAuth {
MoreLobbyAuth {
mesh: mesh.to_owned()
mesh: mesh.to_owned(),
keybind_workaround,
}
}
@@ -43,7 +45,12 @@ impl <C: Send + 'static> Operation<C> 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,

View File

@@ -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<std::collections::HashMap<i32, std::sync::atomic::AtomicU8>>,
default_config_map: std::collections::HashMap<String, String>,
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<SessionInfo> {
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);
}
}

View File

@@ -0,0 +1,18 @@
mod input_lockup;
pub use input_lockup::EditModeInputLockupWorkaround;
pub struct Workarounds {
emil: std::sync::Arc<EditModeInputLockupWorkaround>,
}
impl Workarounds {
pub fn new() -> Self {
Self {
emil: std::sync::Arc::new(EditModeInputLockupWorkaround::new())
}
}
pub fn edit_mode_input_lockup(&self) -> std::sync::Arc<EditModeInputLockupWorkaround> {
self.emil.clone()
}
}