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:
@@ -14528,6 +14528,15 @@
|
||||
},
|
||||
"permission": "Administrator"
|
||||
},
|
||||
{
|
||||
"regex": "\\?klf",
|
||||
"op": {
|
||||
"type": "BuiltIn",
|
||||
"built_in": "Intercom",
|
||||
"intercom": "KeybindLockFix"
|
||||
},
|
||||
"permission": "Player"
|
||||
},
|
||||
{
|
||||
"regex": "\\?help",
|
||||
"op": {
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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,
|
||||
|
||||
113
rc_services_room/src/workarounds/input_lockup.rs
Normal file
113
rc_services_room/src/workarounds/input_lockup.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
18
rc_services_room/src/workarounds/mod.rs
Normal file
18
rc_services_room/src/workarounds/mod.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user