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

Add game event refresh freezing when user enters lobby; fix #84

This commit is contained in:
NG (Graham)
2026-08-21 21:04:37 -04:00
parent 1f0aa22f68
commit b717ca1ed3
14 changed files with 347 additions and 86 deletions

View File

@@ -322,7 +322,7 @@ impl Intercom {
}
Self::KeybindLockFix => {
ctx.user.trigger_workaround(
oj_rc_core::persist::user::intercom::IntercomWorkaroundMessage::KeybindLockout { },
oj_rc_core::persist::user::intercom::IntercomWorkaroundMessage::WebService( oj_rc_core::persist::user::intercom::IntercomWebServiceWorkaroundMessage::KeybindLockout { }),
vec![ctx.user.public_id().to_owned()]
).await;
"Triggered key lockout workaround".to_owned()

View File

@@ -79,6 +79,21 @@ impl GameMap {
crate::persist::config::GameMap::Earth2 => Self::Earth2,
}
}
#[inline]
pub fn from_str(s: &str) -> Option<Self> {
match s {
"RC_Planet_Mars_01_CTF" => Some(Self::Mars1), // og flat mars
"RC_Planet_Mars_02_BA" => Some(Self::Mars2), // the one with the bridge in the middle
"RC_Planet_Mars_03_BA" => Some(Self::Mars3), // tharsis rift without the rift
"RC_Planet_Neptune_01_CTF" => Some(Self::Neptune1), // og flat GJ1214b gliese lake without the lake
"RC_Planet_Neptune_02_BA" => Some(Self::Neptune2), // the one with the cave
"RC_Planet_Neptune_03_BA" => Some(Self::Neptune3), // spitzer dam
"RC_Planet_Earth_01_BA" => Some(Self::Earth1), // birmingham power station
"RC_Planet_Earth_02_BA" => Some(Self::Earth2), // vanguard
_ => None,
}
}
}
#[repr(u8)]

View File

@@ -379,6 +379,7 @@ impl <C: Clone + Send> super::ConfigProvider<C> for CubeConfig {
index: first,
started: chrono::Utc::now().timestamp(),
needs_to_be_saved: true,
lockouts: std::collections::HashMap::new(),
}
}

View File

@@ -164,17 +164,18 @@ pub struct ChatSystemConfig {
pub can_create_channels: bool,
}
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct GameEventSequence {
pub strategy: GameRotationStrategy,
pub modes: Vec<GameEvents>,
pub index: usize,
pub started: i64,
pub(crate) needs_to_be_saved: bool,
pub(crate) lockouts: std::collections::HashMap<i32, GameEvent>,
}
impl GameEventSequence {
pub fn now(&mut self, updater: Box<dyn crate::persist::user::GameEventSetter>) -> GameEventTransmissible {
pub fn now(&mut self, updater: Box<dyn crate::persist::user::GameEventSetter>, user: i32) -> GameEventTransmissible {
let time_now = chrono::Utc::now().timestamp();
let mut item_now = &self.modes[self.index];
let needs_refresh = time_now >= (item_now.duration.as_secs() as i64) + self.started;
@@ -207,43 +208,91 @@ impl GameEventSequence {
updater.set_singleplayer(sp).await;
});
}
let remaining_ticks = ((item_now.duration.as_secs() as i64) - (time_now - self.started)) * 10_000_000;
GameEventTransmissible {
maps: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Str,
custom_ty: None,
items: vec![
Typed::Str(crate::data::game_mode::GameMap::from_persist(item_now.singleplayer.map).as_str().into()),
Typed::Str(crate::data::game_mode::GameMap::from_persist(item_now.multiplayer.map).as_str().into()),
],
}),
visibilities: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Int,
custom_ty: None,
items: vec![
Typed::Int(crate::data::game_mode::MapVisibility::from_persist(item_now.singleplayer.visibility) as _),
Typed::Int(crate::data::game_mode::MapVisibility::from_persist(item_now.multiplayer.visibility) as _),
],
}),
modes: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Int,
custom_ty: None,
items: vec![
Typed::Int(crate::data::game_mode::GameMode::from_persist(item_now.singleplayer.mode) as _),
Typed::Int(crate::data::game_mode::GameMode::from_persist(item_now.multiplayer.mode) as _),
],
}),
auto_heals: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Bool,
custom_ty: None,
items: vec![
Typed::Bool(item_now.singleplayer.auto_heal),
Typed::Bool(item_now.multiplayer.auto_heal),
],
}),
remaining_ticks: Typed::Long(remaining_ticks),
if let Some(multiplayer_override) = self.lockouts.get(&user) {
GameEventTransmissible {
maps: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Str,
custom_ty: None,
items: vec![
Typed::Str(crate::data::game_mode::GameMap::from_persist(item_now.singleplayer.map).as_str().into()),
Typed::Str(crate::data::game_mode::GameMap::from_persist(multiplayer_override.map).as_str().into()),
],
}),
visibilities: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Int,
custom_ty: None,
items: vec![
Typed::Int(crate::data::game_mode::MapVisibility::from_persist(item_now.singleplayer.visibility) as _),
Typed::Int(crate::data::game_mode::MapVisibility::from_persist(multiplayer_override.visibility) as _),
],
}),
modes: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Int,
custom_ty: None,
items: vec![
Typed::Int(crate::data::game_mode::GameMode::from_persist(item_now.singleplayer.mode) as _),
Typed::Int(crate::data::game_mode::GameMode::from_persist(multiplayer_override.mode) as _),
],
}),
auto_heals: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Bool,
custom_ty: None,
items: vec![
Typed::Bool(item_now.singleplayer.auto_heal),
Typed::Bool(multiplayer_override.auto_heal),
],
}),
remaining_ticks: Typed::Long(10_000_000),
}
} else {
let remaining_ticks = ((item_now.duration.as_secs() as i64) - (time_now - self.started)) * 10_000_000;
GameEventTransmissible {
maps: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Str,
custom_ty: None,
items: vec![
Typed::Str(crate::data::game_mode::GameMap::from_persist(item_now.singleplayer.map).as_str().into()),
Typed::Str(crate::data::game_mode::GameMap::from_persist(item_now.multiplayer.map).as_str().into()),
],
}),
visibilities: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Int,
custom_ty: None,
items: vec![
Typed::Int(crate::data::game_mode::MapVisibility::from_persist(item_now.singleplayer.visibility) as _),
Typed::Int(crate::data::game_mode::MapVisibility::from_persist(item_now.multiplayer.visibility) as _),
],
}),
modes: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Int,
custom_ty: None,
items: vec![
Typed::Int(crate::data::game_mode::GameMode::from_persist(item_now.singleplayer.mode) as _),
Typed::Int(crate::data::game_mode::GameMode::from_persist(item_now.multiplayer.mode) as _),
],
}),
auto_heals: Typed::Arr(polariton::operation::Arr {
ty: polariton::serdes::TypePrefix::Bool,
custom_ty: None,
items: vec![
Typed::Bool(item_now.singleplayer.auto_heal),
Typed::Bool(item_now.multiplayer.auto_heal),
],
}),
remaining_ticks: Typed::Long(remaining_ticks),
}
}
}
/// Returns true if replacing existing entry for user
pub fn add_lockout(&mut self, user: i32, event: GameEvent) -> bool {
self.lockouts.insert(user, event).is_some()
}
/// Returns true if entry for user existed
pub fn remove_lockout(&mut self, user: i32) -> bool {
self.lockouts.remove(&user).is_some()
}
}
pub struct GameEventTransmissible {

View File

@@ -229,14 +229,20 @@ 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);
match msg {
IntercomWorkaroundMessage::Lobby(_lobby) => todo!(),
IntercomWorkaroundMessage::WebService(ws) => {
let data = IntercomWebServiceMessage {
public_ids: to,
data: IntercomWebServiceUserMessage::Workaround(ws),
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) {
@@ -266,26 +272,127 @@ pub struct IntercomLobbyCustomGameDataMessage {
pub users: Vec<IntercomLobbyCustomGameUserData>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum CustomGameMode {
#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
pub enum IntercomGameMap {
Mars1,
Mars2,
Mars3,
Neptune1,
Neptune2,
Neptune3,
Earth1,
Earth2,
}
impl IntercomGameMap {
pub fn into_conf(self) -> crate::persist::config::GameMap {
match self {
Self::Mars1 => crate::persist::config::GameMap::Mars1,
Self::Mars2 => crate::persist::config::GameMap::Mars2,
Self::Mars3 => crate::persist::config::GameMap::Mars3,
Self::Neptune1 => crate::persist::config::GameMap::Neptune1,
Self::Neptune2 => crate::persist::config::GameMap::Neptune2,
Self::Neptune3 => crate::persist::config::GameMap::Neptune3,
Self::Earth1 => crate::persist::config::GameMap::Earth1,
Self::Earth2 => crate::persist::config::GameMap::Earth2,
}
}
pub fn from_data(map: crate::data::game_mode::GameMap) -> Self {
match map {
crate::data::game_mode::GameMap::Mars1 => Self::Mars1,
crate::data::game_mode::GameMap::Mars2 => Self::Mars2,
crate::data::game_mode::GameMap::Mars3 => Self::Mars3,
crate::data::game_mode::GameMap::Neptune1 => Self::Neptune1,
crate::data::game_mode::GameMap::Neptune2 => Self::Neptune2,
crate::data::game_mode::GameMap::Neptune3 => Self::Neptune3,
crate::data::game_mode::GameMap::Earth1 => Self::Earth1,
crate::data::game_mode::GameMap::Earth2 => Self::Earth2,
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"RC_Planet_Mars_01_CTF" => Some(Self::Mars1),
"RC_Planet_Mars_02_BA" => Some(Self::Mars2),
"RC_Planet_Mars_03_BA" => Some(Self::Mars3),
"RC_Planet_Neptune_01_CTF" => Some(Self::Neptune1),
"RC_Planet_Neptune_02_BA" => Some(Self::Neptune2),
"RC_Planet_Neptune_03_BA" => Some(Self::Neptune3),
"RC_Planet_Earth_01_BA" => Some(Self::Earth1),
"RC_Planet_Earth_02_BA" => Some(Self::Earth2),
_ => None,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
pub enum IntercomGameMode {
BattleArena,
TeamDeathmatch,
Pit,
SuddenDeath,
}
impl IntercomGameMode {
pub fn into_conf(self) -> crate::persist::config::GameType {
match self {
Self::BattleArena => crate::persist::config::GameType::BattleArena,
Self::SuddenDeath => crate::persist::config::GameType::SuddenDeath,
Self::Pit => crate::persist::config::GameType::Pit,
Self::TeamDeathmatch => crate::persist::config::GameType::TeamDeathmatch,
}
}
pub fn from_data(mode: crate::data::game_mode::GameMode) -> Self {
match mode {
crate::data::game_mode::GameMode::BattleArena => Self::BattleArena,
crate::data::game_mode::GameMode::SuddenDeath => Self::SuddenDeath,
crate::data::game_mode::GameMode::Pit => Self::Pit,
crate::data::game_mode::GameMode::TeamDeathmatch => Self::TeamDeathmatch,
_ => Self::Pit,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
pub enum CustomGameVisibility {
pub enum IntercomGameVisibility {
Good,
Poor,
Bad,
}
impl IntercomGameVisibility {
pub fn into_conf(self) -> crate::persist::config::GameVisibility {
match self {
Self::Good => crate::persist::config::GameVisibility::Good,
Self::Poor => crate::persist::config::GameVisibility::Poor,
Self::Bad => crate::persist::config::GameVisibility::Bad,
}
}
pub fn from_data(vis: crate::data::game_mode::MapVisibility) -> Self {
match vis {
crate::data::game_mode::MapVisibility::Good => Self::Good,
crate::data::game_mode::MapVisibility::Poor => Self::Poor,
crate::data::game_mode::MapVisibility::Bad => Self::Bad,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
pub struct IntercomGameEvent {
pub map: IntercomGameMap,
pub visibility: IntercomGameVisibility,
pub mode: IntercomGameMode,
pub auto_heal: bool,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct IntercomLobbyCustomGameConfig {
pub game_mode: CustomGameMode,
pub game_mode: IntercomGameMode,
pub map: String,
pub map_visibility: CustomGameVisibility,
pub map_visibility: IntercomGameVisibility,
pub health_regen: bool,
pub capture_segment_memory: bool,
pub base_shields_go_down: bool,
@@ -350,7 +457,7 @@ pub struct IntercomWebServiceMessage {
pub enum IntercomWebServiceUserMessage {
DevMessage(IntercomDevMessage),
Maintenance(IntercomMaintenanceMessage),
Workaround(IntercomWorkaroundMessage),
Workaround(IntercomWebServiceWorkaroundMessage),
}
#[derive(Serialize, Deserialize, Clone, Debug)]
@@ -366,10 +473,31 @@ pub struct IntercomMaintenanceMessage {
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(tag = "workaround")]
pub enum IntercomWorkaroundMessage {
pub enum IntercomWebServiceWorkaroundMessage {
/// 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 { },
/// Lock game event expiry to currently-selected game mode for user
/// more info: https://git.ngram.ca/OpenJam/rc-servers/issues/84
GameModeEventLock {
event: IntercomGameEvent,
},
/// Return to normal game event expiry behaviour for user
/// more info: https://git.ngram.ca/OpenJam/rc-servers/issues/84
GameModeEventUnlock { },
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(tag = "workaround")]
pub enum IntercomLobbyWorkaroundMessage {
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(tag = "workaround")]
pub enum IntercomWorkaroundMessage {
Lobby(IntercomLobbyWorkaroundMessage),
WebService(IntercomWebServiceWorkaroundMessage),
}
pub fn generate_token(salt: &[u8], key: &[u8]) -> String {

View File

@@ -11,10 +11,6 @@ fn fake_impl_to_db(client_emu: &crate::persist::config::ClientEmulator) -> oj_rc
#[async_trait::async_trait]
impl super::LobbyUser for UserData {
fn user_id(&self) -> i32 {
self.account.id
}
async fn player_data(&self, cpu_counter: &crate::cubes::CpuListParser) -> Result<crate::data::player_data::PlayerData, polariton_server::operations::SimpleOpError> {
self.user_player_data(cpu_counter).await.map_err(|e| {
if let Some(msg) = e.error_msg() {

View File

@@ -297,7 +297,6 @@ pub enum UserRole {
#[async_trait::async_trait]
pub trait LobbyUser {
fn user_id(&self) -> i32;
async fn player_data(&self, cpu_counter: &crate::cubes::CpuListParser) -> Result<crate::data::player_data::PlayerData, polariton_server::operations::SimpleOpError>;
#[allow(clippy::too_many_arguments)]
async fn start_game(&self, game: GameDescriptor, players: Vec<PlayerLobbyDescriptor>, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser, team_chooser: &dyn super::TeamChooser, missing_players: usize) -> Result<FakePlayers, polariton_server::operations::SimpleOpError>;

View File

@@ -385,6 +385,18 @@ impl QueueHandler {
}
}
let to_unlock: Vec<String> = q_entry.users.iter().map(|user| user.user.public_id().to_owned()).collect();
if let Some(first) = q_entry.users.first() {
let first_user = first.user.clone();
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_millis(10_000)).await;
log::info!("Unlocking {} users' game mode events after entering match", to_unlock.len());
first_user.trigger_workaround(
oj_rc_core::persist::user::intercom::IntercomWorkaroundMessage::WebService(oj_rc_core::persist::user::intercom::IntercomWebServiceWorkaroundMessage::GameModeEventUnlock { }),
to_unlock,
).await;
});
}
}
async fn enter_custom_match(
@@ -394,15 +406,15 @@ impl QueueHandler {
user: &(dyn oj_rc_core::persist::user::LobbyUser + Send + Sync),
) {
let mode = match session.config.game_mode {
oj_rc_core::persist::user::intercom::CustomGameMode::BattleArena => oj_rc_core::data::game_mode::GameMode::BattleArena,
oj_rc_core::persist::user::intercom::CustomGameMode::TeamDeathmatch => oj_rc_core::data::game_mode::GameMode::TeamDeathmatch,
oj_rc_core::persist::user::intercom::CustomGameMode::Pit => oj_rc_core::data::game_mode::GameMode::Pit,
oj_rc_core::persist::user::intercom::CustomGameMode::SuddenDeath => oj_rc_core::data::game_mode::GameMode::SuddenDeath,
oj_rc_core::persist::user::intercom::IntercomGameMode::BattleArena => oj_rc_core::data::game_mode::GameMode::BattleArena,
oj_rc_core::persist::user::intercom::IntercomGameMode::TeamDeathmatch => oj_rc_core::data::game_mode::GameMode::TeamDeathmatch,
oj_rc_core::persist::user::intercom::IntercomGameMode::Pit => oj_rc_core::data::game_mode::GameMode::Pit,
oj_rc_core::persist::user::intercom::IntercomGameMode::SuddenDeath => oj_rc_core::data::game_mode::GameMode::SuddenDeath,
};
let visibility = match session.config.map_visibility {
oj_rc_core::persist::user::intercom::CustomGameVisibility::Good => oj_rc_core::data::game_mode::MapVisibility::Good,
oj_rc_core::persist::user::intercom::CustomGameVisibility::Poor => oj_rc_core::data::game_mode::MapVisibility::Poor,
oj_rc_core::persist::user::intercom::CustomGameVisibility::Bad => oj_rc_core::data::game_mode::MapVisibility::Bad,
oj_rc_core::persist::user::intercom::IntercomGameVisibility::Good => oj_rc_core::data::game_mode::MapVisibility::Good,
oj_rc_core::persist::user::intercom::IntercomGameVisibility::Poor => oj_rc_core::data::game_mode::MapVisibility::Poor,
oj_rc_core::persist::user::intercom::IntercomGameVisibility::Bad => oj_rc_core::data::game_mode::MapVisibility::Bad,
};
let key = QueueKey {
map: session.config.map.clone(),
@@ -499,7 +511,7 @@ impl QueueHandler {
let new_player = QueueUser {
emitter: event_emitter,
player: player_data,
user_id: oj_rc_core::persist::user::LobbyUser::user_id(lobby_user),
user_id: oj_rc_core::persist::user::CommonUser::account_id(lobby_user),
enqueued_at: chrono::Utc::now(),
user: user.clone(),
};
@@ -657,6 +669,17 @@ impl QueueHandler {
self.ensure_autostart_task_running();
log::info!("Locking user {} game move event as they enter queue", user.public_id());
let game_event = oj_rc_core::persist::user::intercom::IntercomGameEvent {
map: oj_rc_core::persist::user::intercom::IntercomGameMap::from_str(&map).unwrap_or(oj_rc_core::persist::user::intercom::IntercomGameMap::Mars1),
visibility: oj_rc_core::persist::user::intercom::IntercomGameVisibility::from_data(visibility),
mode: oj_rc_core::persist::user::intercom::IntercomGameMode::from_data(mode),
auto_heal,
};
user.trigger_workaround(oj_rc_core::persist::user::intercom::IntercomWorkaroundMessage::WebService(oj_rc_core::persist::user::intercom::IntercomWebServiceWorkaroundMessage::GameModeEventLock {
event: game_event.clone(),
}), vec![user.public_id().to_owned()]).await;
let key = QueueKey {
map, mode, visibility, auto_heal,
};
@@ -669,7 +692,7 @@ impl QueueHandler {
let new_player = QueueUser {
emitter: event_emitter,
player: player_data,
user_id: oj_rc_core::persist::user::LobbyUser::user_id(lobby_user),
user_id: oj_rc_core::persist::user::CommonUser::account_id(lobby_user),
enqueued_at: chrono::Utc::now(),
user: user.clone(),
};
@@ -684,9 +707,13 @@ impl QueueHandler {
match self.change_strategy {
GamemodeChangeStrategy::Upgrade => {
let mut new_queue_map = std::collections::HashMap::<QueueKey, Queue>::with_capacity(lock.len());
let mut to_update_lockout = Vec::new();
let mut count = 0;
for (_key, mut q_entry) in lock.drain() {
count += q_entry.users.len();
for user in q_entry.users.iter() {
to_update_lockout.push(user.user.public_id().to_owned());
}
if let Some(values) = new_queue_map.get_mut(&key) {
values.users.append(&mut q_entry.users);
values.platoons.extend(q_entry.platoons);
@@ -702,9 +729,17 @@ impl QueueHandler {
if count != 0 {
log::info!("Upgraded {} users in queue to new gamemode {}", count, key.short());
}
if !to_update_lockout.is_empty() {
log::info!("Updating game event locks for {} users due to lobby upgrade", to_update_lockout.len());
user.trigger_workaround(
oj_rc_core::persist::user::intercom::IntercomWorkaroundMessage::WebService(oj_rc_core::persist::user::intercom::IntercomWebServiceWorkaroundMessage::GameModeEventLock { event: game_event }),
to_update_lockout,
).await;
}
},
GamemodeChangeStrategy::Notify => {
let mut seen = std::collections::HashSet::new();
let mut to_update_lockout = Vec::new();
for (_key, q_entry) in lock.drain() {
for player in q_entry.users {
if seen.contains(&player.user_id) { continue; }
@@ -714,6 +749,7 @@ impl QueueHandler {
text: "Please requeue".to_owned(),
});
seen.insert(player.user_id);
to_update_lockout.push(player.user.public_id().to_owned());
}
for platoon in q_entry.platoons.into_values() {
for player in platoon.members {
@@ -730,6 +766,13 @@ impl QueueHandler {
if !seen.is_empty() {
log::info!("Notified {} users in queue of new gamemode {}", seen.len(), key.short());
}
if !to_update_lockout.is_empty() {
log::info!("Removing game event locks for {} users due to lobby notify", to_update_lockout.len());
user.trigger_workaround(
oj_rc_core::persist::user::intercom::IntercomWorkaroundMessage::WebService(oj_rc_core::persist::user::intercom::IntercomWebServiceWorkaroundMessage::GameModeEventUnlock { }),
to_update_lockout,
).await;
}
},
GamemodeChangeStrategy::Ignore => {
log::debug!("Gamemode appears to have changed to {}, ignoring already-queued players", key.short());
@@ -786,7 +829,12 @@ impl QueueHandler {
pub async fn leave_queue(&self, user: std::sync::Arc<Box<dyn oj_rc_core::persist::user::User<()> + Send + Sync>>) {
let public_id = user.public_id();
let user_id = oj_rc_core::persist::user::LobbyUser::user_id(user.as_ref().as_ref());
let user_id = oj_rc_core::persist::user::CommonUser::account_id(user.as_ref().as_ref());
log::info!("Unlocking user {} game mode events after leaving queue", public_id);
user.trigger_workaround(
oj_rc_core::persist::user::intercom::IntercomWorkaroundMessage::WebService(oj_rc_core::persist::user::intercom::IntercomWebServiceWorkaroundMessage::GameModeEventUnlock { }),
vec![public_id.to_owned()],
).await;
if let Some(session_id) = self.custom_game_for_user.read().await.get(public_id) {
// player is in custom game session
let mut lock = self.users_in_custom_games_queue.lock().await;

View File

@@ -651,20 +651,20 @@ impl GameConfig {
fn as_core(&self) -> oj_rc_core::persist::user::intercom::IntercomLobbyCustomGameConfig {
oj_rc_core::persist::user::intercom::IntercomLobbyCustomGameConfig {
game_mode: match self.game_mode {
oj_rc_core::data::game_mode::GameMode::BattleArena => oj_rc_core::persist::user::intercom::CustomGameMode::BattleArena,
oj_rc_core::data::game_mode::GameMode::TeamDeathmatch => oj_rc_core::persist::user::intercom::CustomGameMode::TeamDeathmatch,
oj_rc_core::data::game_mode::GameMode::Pit => oj_rc_core::persist::user::intercom::CustomGameMode::Pit,
oj_rc_core::data::game_mode::GameMode::SuddenDeath => oj_rc_core::persist::user::intercom::CustomGameMode::SuddenDeath,
oj_rc_core::data::game_mode::GameMode::BattleArena => oj_rc_core::persist::user::intercom::IntercomGameMode::BattleArena,
oj_rc_core::data::game_mode::GameMode::TeamDeathmatch => oj_rc_core::persist::user::intercom::IntercomGameMode::TeamDeathmatch,
oj_rc_core::data::game_mode::GameMode::Pit => oj_rc_core::persist::user::intercom::IntercomGameMode::Pit,
oj_rc_core::data::game_mode::GameMode::SuddenDeath => oj_rc_core::persist::user::intercom::IntercomGameMode::SuddenDeath,
invalid => {
log::warn!("Custom game set to invalid mode {:?} (using sudden death as fallback)", invalid);
oj_rc_core::persist::user::intercom::CustomGameMode::SuddenDeath
oj_rc_core::persist::user::intercom::IntercomGameMode::SuddenDeath
},
},
map: self.map.clone(),
map_visibility: match self.map_visibility {
oj_rc_core::data::game_mode::MapVisibility::Good => oj_rc_core::persist::user::intercom::CustomGameVisibility::Good,
oj_rc_core::data::game_mode::MapVisibility::Poor => oj_rc_core::persist::user::intercom::CustomGameVisibility::Poor,
oj_rc_core::data::game_mode::MapVisibility::Bad => oj_rc_core::persist::user::intercom::CustomGameVisibility::Bad,
oj_rc_core::data::game_mode::MapVisibility::Good => oj_rc_core::persist::user::intercom::IntercomGameVisibility::Good,
oj_rc_core::data::game_mode::MapVisibility::Poor => oj_rc_core::persist::user::intercom::IntercomGameVisibility::Poor,
oj_rc_core::data::game_mode::MapVisibility::Bad => oj_rc_core::persist::user::intercom::IntercomGameVisibility::Bad,
},
health_regen: self.health_regen,
capture_segment_memory: self.capture_segment_memory,

View File

@@ -1,11 +1,12 @@
use oj_rc_core::persist::user::IntercomListener;
use oj_rc_core::persist::user::intercom::{IntercomWebServiceUserMessage, IntercomWorkaroundMessage};
use oj_rc_core::persist::user::intercom::{IntercomWebServiceUserMessage, IntercomWebServiceWorkaroundMessage};
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>,
game_event_seq: std::sync::Arc<tokio::sync::Mutex<oj_rc_core::persist::config::GameEventSequence>>,
}
impl IntercomHandler {
@@ -14,12 +15,14 @@ impl IntercomHandler {
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>,
game_event_seq: &std::sync::Arc<tokio::sync::Mutex<oj_rc_core::persist::config::GameEventSequence>>,
) -> Self {
Self {
listener,
user: std::sync::Arc::downgrade(user),
emitter: emitter.to_owned().downgrade(),
keybind_workaround: keybind_workaround.to_owned(),
game_event_seq: game_event_seq.to_owned(),
}
}
@@ -28,6 +31,7 @@ impl IntercomHandler {
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>,
game_event_seq: std::sync::Arc<tokio::sync::Mutex<oj_rc_core::persist::config::GameEventSequence>>,
) {
use futures::StreamExt;
let mut listener = listener.listen().await;
@@ -54,7 +58,7 @@ impl IntercomHandler {
};
emitter.emit(event);
}
IntercomWebServiceUserMessage::Workaround(IntercomWorkaroundMessage::KeybindLockout { }) => {
IntercomWebServiceUserMessage::Workaround(IntercomWebServiceWorkaroundMessage::KeybindLockout { }) => {
let session = keybind_workaround.add_user(user.account_id(), user.public_id().to_owned()).await;
let non_me = session.users.first().unwrap();
let event = super::CustomGameInvite {
@@ -65,6 +69,22 @@ impl IntercomHandler {
invited_to_team_a: true,
};
emitter.emit(event);
},
IntercomWebServiceUserMessage::Workaround(IntercomWebServiceWorkaroundMessage::GameModeEventLock { event }) => {
let is_replaced = game_event_seq.lock().await.add_lockout(
user.account_id(),
oj_rc_core::persist::config::GameEvent {
map: event.map.into_conf(),
visibility: event.visibility.into_conf(),
mode: event.mode.into_conf(),
auto_heal: event.auto_heal,
},
);
log::info!("Activated game event lockout for user {} (replaced? {})", user.public_id(), is_replaced);
},
IntercomWebServiceUserMessage::Workaround(IntercomWebServiceWorkaroundMessage::GameModeEventUnlock { }) => {
let is_existed = game_event_seq.lock().await.remove_lockout(user.account_id());
log::info!("Deactivated game event lockout for user {} (existed? {})", user.public_id(), is_existed);
}
}
} else {
@@ -80,6 +100,6 @@ impl IntercomHandler {
}
pub fn run(self) -> tokio::task::JoinHandle<()> {
tokio::spawn(Self::run_loop(self.listener, self.user, self.emitter, self.keybind_workaround))
tokio::spawn(Self::run_loop(self.listener, self.user, self.emitter, self.keybind_workaround, self.game_event_seq))
}
}

View File

@@ -31,6 +31,7 @@ pub struct InitConfig {
pub custom_games: std::sync::Arc<custom_game_tracker::CustomGameMesh>,
pub user_mesh: std::sync::Arc<user_service::UserMesh>,
pub workarounds: workarounds::Workarounds,
pub game_event_sequence: std::sync::Arc<tokio::sync::Mutex<oj_rc_core::persist::config::GameEventSequence>>,
}
#[tokio::main]
@@ -49,6 +50,7 @@ async fn main() -> std::io::Result<()> {
&parsers,
vehicle_validator_plugins_path,
);
let game_event_sequence = std::sync::Arc::new(tokio::sync::Mutex::new(<oj_rc_core::ConfigImpl as oj_rc_core::ConfigProvider<()>>::gamemode_events(&cubes)));
let init_ctx = std::sync::Arc::new(InitConfig {
cubes,
users,
@@ -58,6 +60,7 @@ async fn main() -> std::io::Result<()> {
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(),
game_event_sequence,
});
let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler(&init_ctx), polariton_server::events::EventsHandler::new()));

View File

@@ -21,7 +21,7 @@ RC_Planet_Neptune_01_CTF
*/
pub struct GameEventsParamsProvider {
sequence: std::sync::Mutex<oj_rc_core::persist::config::GameEventSequence>,
sequence: std::sync::Arc<tokio::sync::Mutex<oj_rc_core::persist::config::GameEventSequence>>,
}
#[async_trait::async_trait]
@@ -32,7 +32,7 @@ impl Operation<()> for GameEventsParamsProvider {
match user.user() {
Ok(user_info) => {
let mut params = params.to_dict();
let current_mode = self.sequence.lock().unwrap().now(user_info.current_game_event_setter());
let current_mode = self.sequence.lock().await.now(user_info.current_game_event_setter(), user_info.account_id());
params.insert(MAP_NAMES_PARAM_KEY, current_mode.maps);
params.insert(VISIBILITY_PARAM_KEY, current_mode.visibilities);
params.insert(MODE_PARAM_KEY, current_mode.modes);
@@ -64,9 +64,8 @@ impl OperationCode for GameEventsParamsProvider {
}
}
pub(super) fn event_system_params_provider(conf: &oj_rc_core::ConfigImpl) -> GameEventsParamsProvider {
let game_seq = <oj_rc_core::ConfigImpl as oj_rc_core::ConfigProvider<()>>::gamemode_events(conf);
pub(super) fn event_system_params_provider(init_ctx: &crate::InitConfig) -> GameEventsParamsProvider {
GameEventsParamsProvider {
sequence: std::sync::Mutex::new(game_seq),
sequence: init_ctx.game_event_sequence.clone(),
}
}

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, init_ctx.workarounds.edit_mode_input_lockup()))
.add(more_auth::more_auth_provider(&init_ctx.user_mesh, init_ctx.workarounds.edit_mode_input_lockup(), &init_ctx.game_event_sequence))
.add(versioner::version_teller(&init_ctx.cubes))
.add(maintenancer::maintenace_teller(&init_ctx.cubes))
.add(game_quality::QualityConfigTeller)
@@ -166,7 +166,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
.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))
.add(game_event_params::event_system_params_provider(init_ctx))
.add(garage_bay_uuid::garage_id_provider())
.add(tech_tree_data::tech_tree_layout_provider(&init_ctx.cubes))
.add(item_shop_bundles::item_bundle_provider(&init_ctx.cubes))

View File

@@ -4,12 +4,14 @@ 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>,
game_event_seq: std::sync::Arc<tokio::sync::Mutex<oj_rc_core::persist::config::GameEventSequence>>,
}
pub fn more_auth_provider(mesh: &std::sync::Arc<crate::user_service::UserMesh>, keybind_workaround: std::sync::Arc<crate::workarounds::EditModeInputLockupWorkaround>) -> MoreLobbyAuth {
pub fn more_auth_provider(mesh: &std::sync::Arc<crate::user_service::UserMesh>, keybind_workaround: std::sync::Arc<crate::workarounds::EditModeInputLockupWorkaround>, game_event_seq: &std::sync::Arc<tokio::sync::Mutex<oj_rc_core::persist::config::GameEventSequence>>,) -> MoreLobbyAuth {
MoreLobbyAuth {
mesh: mesh.to_owned(),
keybind_workaround,
game_event_seq: game_event_seq.to_owned(),
}
}
@@ -50,6 +52,7 @@ impl <C: Send + 'static> Operation<C> for MoreLobbyAuth {
&user_info,
user.event_sender(),
&self.keybind_workaround,
&self.game_event_seq,
).run();
return polariton::operation::OperationResponse {
code: Self::op_code(),