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:
@@ -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()
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user