mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add pit win conditions #34
This commit is contained in:
@@ -121,7 +121,7 @@ impl BuiltIn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prettify_re<'a>(regex: &'a str) -> &'a str {
|
fn prettify_re(regex: &str) -> &str {
|
||||||
regex.trim_start_matches("\\")
|
regex.trim_start_matches("\\")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -514,6 +514,7 @@ fn default_multiplayer() -> super::MultiplayerConfig {
|
|||||||
network: super::multiplayer::default_net_conf(),
|
network: super::multiplayer::default_net_conf(),
|
||||||
fakes: super::multiplayer::default_fake_users(),
|
fakes: super::multiplayer::default_fake_users(),
|
||||||
battle_arena: super::multiplayer::default_ba_conf(),
|
battle_arena: super::multiplayer::default_ba_conf(),
|
||||||
|
pit_config: super::multiplayer::default_pit_conf(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -462,4 +462,8 @@ impl <C: Clone + Send> super::ConfigProvider<C> for CubeConfig {
|
|||||||
data: self.battle.multiplayer.battle_arena.clone(),
|
data: self.battle.multiplayer.battle_arena.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pit_settings(&self) -> super::PitSettings {
|
||||||
|
self.battle.multiplayer.pit_config.clone().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ mod cubes_json;
|
|||||||
pub use cubes_json::CubeConfig;
|
pub use cubes_json::CubeConfig;
|
||||||
|
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::{ConfigProvider, CompleteCampaignProvider, DevMessageProvider, ServerConfig, GarageUpgrades, GarageUpgradeIncrement, ChatSystemConfig, GameEventSequence, GameEvents, GameRotationStrategy, GameEvent, GameMap, GameVisibility, GameType, SingleplayerConfig, VehicleInfo, VehicleDescriptor, QueueChangeMode, Point, Sphere, MapConfig, LinksConfig, FakePlayer, ClientEmulator, EnergyConfig, BattleArenaResolver};
|
pub use traits::{ConfigProvider, CompleteCampaignProvider, DevMessageProvider, ServerConfig, GarageUpgrades, GarageUpgradeIncrement, ChatSystemConfig, GameEventSequence, GameEvents, GameRotationStrategy, GameEvent, GameMap, GameVisibility, GameType, SingleplayerConfig, VehicleInfo, VehicleDescriptor, QueueChangeMode, Point, Sphere, MapConfig, LinksConfig, FakePlayer, ClientEmulator, EnergyConfig, BattleArenaResolver, PitSettings, PitWinCondition};
|
||||||
|
|
||||||
pub type ConfigImpl = CubeConfig;
|
pub type ConfigImpl = CubeConfig;
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ pub trait ConfigProvider<C: Clone> {
|
|||||||
fn fake_players(&self) -> Vec<FakePlayer>;
|
fn fake_players(&self) -> Vec<FakePlayer>;
|
||||||
fn energy(&self) -> EnergyConfig;
|
fn energy(&self) -> EnergyConfig;
|
||||||
fn ba_settings(&self) -> BattleArenaResolver;
|
fn ba_settings(&self) -> BattleArenaResolver;
|
||||||
|
fn pit_settings(&self) -> PitSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CompleteCampaignProvider {
|
pub struct CompleteCampaignProvider {
|
||||||
@@ -428,3 +429,18 @@ impl BattleArenaResolver {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PitSettings {
|
||||||
|
pub wins: Vec<PitWinCondition>,
|
||||||
|
pub respawn_time_seconds: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PitWinCondition {
|
||||||
|
StreakKills(u32),
|
||||||
|
TotalKills(u32),
|
||||||
|
Score(u32),
|
||||||
|
Damage(u32),
|
||||||
|
Time,
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ pub struct MultiplayerConfig {
|
|||||||
pub fakes: Vec<FakePlayerConf>,
|
pub fakes: Vec<FakePlayerConf>,
|
||||||
#[serde(default = "default_ba_conf")]
|
#[serde(default = "default_ba_conf")]
|
||||||
pub battle_arena: BattleArenaConfig,
|
pub battle_arena: BattleArenaConfig,
|
||||||
|
#[serde(default = "default_pit_conf")]
|
||||||
|
pub pit_config: PitConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
@@ -169,3 +171,67 @@ fn default_equalizer_duration() -> u64 {
|
|||||||
fn default_segments() -> u16 {
|
fn default_segments() -> u16 {
|
||||||
3
|
3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum PitWinCondition {
|
||||||
|
StreakKills {
|
||||||
|
streak: u32,
|
||||||
|
},
|
||||||
|
TotalKills {
|
||||||
|
kills: u32,
|
||||||
|
},
|
||||||
|
TotalScore {
|
||||||
|
score: u32,
|
||||||
|
},
|
||||||
|
TotalDamage {
|
||||||
|
damage: u32,
|
||||||
|
},
|
||||||
|
Time,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<PitWinCondition> for crate::persist::config::PitWinCondition {
|
||||||
|
fn from(value: PitWinCondition) -> Self {
|
||||||
|
match value {
|
||||||
|
PitWinCondition::StreakKills { streak } => crate::persist::config::PitWinCondition::StreakKills(streak),
|
||||||
|
PitWinCondition::TotalKills { kills } => crate::persist::config::PitWinCondition::TotalKills(kills),
|
||||||
|
PitWinCondition::TotalScore { score } => crate::persist::config::PitWinCondition::Score(score),
|
||||||
|
PitWinCondition::TotalDamage { damage } => crate::persist::config::PitWinCondition::Damage(damage),
|
||||||
|
PitWinCondition::Time => crate::persist::config::PitWinCondition::Time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct PitConfig {
|
||||||
|
pub wins: Vec<PitWinCondition>,
|
||||||
|
pub respawn_time_s: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<PitConfig> for crate::persist::config::PitSettings {
|
||||||
|
fn from(value: PitConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
wins: value.wins.into_iter().map(|x| x.into()).collect(),
|
||||||
|
respawn_time_seconds: value.respawn_time_s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn default_pit_conf() -> PitConfig {
|
||||||
|
PitConfig {
|
||||||
|
wins: default_pit_win_conditions(),
|
||||||
|
respawn_time_s: default_respawn_time(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_pit_win_conditions() -> Vec<PitWinCondition> {
|
||||||
|
vec![
|
||||||
|
PitWinCondition::StreakKills {
|
||||||
|
streak: 2,
|
||||||
|
},
|
||||||
|
PitWinCondition::TotalKills {
|
||||||
|
kills: 5,
|
||||||
|
},
|
||||||
|
PitWinCondition::Time,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ pub struct GameMatches {
|
|||||||
fake_players: Vec<oj_rc_core::persist::config::FakePlayer>,
|
fake_players: Vec<oj_rc_core::persist::config::FakePlayer>,
|
||||||
cube_parsers: std::sync::Arc<oj_rc_core::cubes::CubeParsers>,
|
cube_parsers: std::sync::Arc<oj_rc_core::cubes::CubeParsers>,
|
||||||
ba_settings: std::sync::Arc<oj_rc_core::persist::config::BattleArenaResolver>,
|
ba_settings: std::sync::Arc<oj_rc_core::persist::config::BattleArenaResolver>,
|
||||||
|
pit_settings: std::sync::Arc<oj_rc_core::persist::config::PitSettings>,
|
||||||
factory: std::sync::Arc<oj_rc_core::factory::Factory>,
|
factory: std::sync::Arc<oj_rc_core::factory::Factory>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ impl GameMatches {
|
|||||||
fake_players: <oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::fake_players(conf),
|
fake_players: <oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::fake_players(conf),
|
||||||
cube_parsers,
|
cube_parsers,
|
||||||
ba_settings: std::sync::Arc::new(<oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::ba_settings(conf)),
|
ba_settings: std::sync::Arc::new(<oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::ba_settings(conf)),
|
||||||
|
pit_settings: std::sync::Arc::new(<oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::pit_settings(conf)),
|
||||||
factory,
|
factory,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,6 +122,7 @@ impl GameMatches {
|
|||||||
&self.mode_configs.the_pit,
|
&self.mode_configs.the_pit,
|
||||||
&map_config,
|
&map_config,
|
||||||
&players,
|
&players,
|
||||||
|
self.pit_settings.clone(),
|
||||||
);
|
);
|
||||||
let engine = super::GenericGamemodeEngine::new(
|
let engine = super::GenericGamemodeEngine::new(
|
||||||
game_info,
|
game_info,
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ impl PlayerTracker {
|
|||||||
|
|
||||||
async fn players_on_team(&self, team: u8) -> Vec<u8> {
|
async fn players_on_team(&self, team: u8) -> Vec<u8> {
|
||||||
if let Some(members) = self.connected.lock().await.get(&team) {
|
if let Some(members) = self.connected.lock().await.get(&team) {
|
||||||
members.iter().map(|x| *x).collect()
|
members.iter().copied().collect()
|
||||||
} else {
|
} else {
|
||||||
Vec::default()
|
Vec::default()
|
||||||
}
|
}
|
||||||
@@ -434,12 +434,12 @@ impl EqualizerTracker {
|
|||||||
|| ba_config.equalizer_model.is_empty()
|
|| ba_config.equalizer_model.is_empty()
|
||||||
//|| ba_config.equalizer_trigger_time_seconds.is_empty()
|
//|| ba_config.equalizer_trigger_time_seconds.is_empty()
|
||||||
|| ba_config.equalizer_duration_seconds.is_empty()
|
|| ba_config.equalizer_duration_seconds.is_empty()
|
||||||
|| ba_config.equalizer_duration_seconds.iter().any(|&x| x == 0);
|
|| ba_config.equalizer_duration_seconds.contains(&0);
|
||||||
if is_disabled {
|
if is_disabled {
|
||||||
log::info!("Battle Arena equalizer is disabled by config (model ok? {}, health ok? {}, duration ok? {})",
|
log::info!("Battle Arena equalizer is disabled by config (model ok? {}, health ok? {}, duration ok? {})",
|
||||||
!ba_config.equalizer_model.is_empty(),
|
!ba_config.equalizer_model.is_empty(),
|
||||||
ba_config.equalizer_health > 0,
|
ba_config.equalizer_health > 0,
|
||||||
!ba_config.equalizer_duration_seconds.is_empty() && !ba_config.equalizer_duration_seconds.iter().any(|&x| x == 0)
|
!ba_config.equalizer_duration_seconds.is_empty() && !ba_config.equalizer_duration_seconds.contains(&0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Self {
|
Self {
|
||||||
@@ -704,6 +704,7 @@ impl BaseTracker {
|
|||||||
// undo cube_index update
|
// undo cube_index update
|
||||||
tracked_base.cube_index.fetch_sub(increment, std::sync::atomic::Ordering::SeqCst);
|
tracked_base.cube_index.fetch_sub(increment, std::sync::atomic::Ordering::SeqCst);
|
||||||
log::debug!("Skipping increment in favour of healing damaged/destroyed cube");
|
log::debug!("Skipping increment in favour of healing damaged/destroyed cube");
|
||||||
|
#[allow(clippy::unnecessary_unwrap)] // have you seen the mess this would be with another if statement?
|
||||||
let first_damaged = first_damaged.unwrap();
|
let first_damaged = first_damaged.unwrap();
|
||||||
let healing = crystal_health - tracked_base.calculate_crystal_health(first_damaged, crystal_health);
|
let healing = crystal_health - tracked_base.calculate_crystal_health(first_damaged, crystal_health);
|
||||||
tracked_base.crystals_healths[first_damaged].store(u8::MAX, std::sync::atomic::Ordering::Relaxed);
|
tracked_base.crystals_healths[first_damaged].store(u8::MAX, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
|||||||
@@ -1,5 +1,97 @@
|
|||||||
use crate::matches::CustomGameLogic;
|
use crate::matches::CustomGameLogic;
|
||||||
|
|
||||||
|
struct WinTracker {
|
||||||
|
ticker: super::trackers::TickTracker<{Self::TICK_MS}>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WinTracker {
|
||||||
|
const TICK_MS: i64 = 50;
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
ticker: super::trackers::TickTracker::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_win(generic: &crate::matches::GenericGamemodeEngine<PitLogic>, game: &PitLogic, winning_team: u8) {
|
||||||
|
generic.game_done();
|
||||||
|
game.abort_timer_sync().await;
|
||||||
|
let data = rlnl::events::ingame::GameLoseWin {
|
||||||
|
winning_team,
|
||||||
|
end_reason: rlnl::types::GameEndReason::PitMaxKillsAchieved,
|
||||||
|
};
|
||||||
|
let winning_team_i32 = winning_team as i32;
|
||||||
|
for conn in generic.users.read().await.values() {
|
||||||
|
let event = if conn.descriptor.team == winning_team_i32 {
|
||||||
|
rlnl::event_code::NetworkEvent::GameWon
|
||||||
|
} else {
|
||||||
|
rlnl::event_code::NetworkEvent::GameLost
|
||||||
|
};
|
||||||
|
crate::events::log_lnl_send_failure(conn.connection.rlnl().send_data(
|
||||||
|
&data,
|
||||||
|
event,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&conn.connection.connection
|
||||||
|
).await);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_win(generic: &crate::matches::GenericGamemodeEngine<PitLogic>, game: &PitLogic) {
|
||||||
|
for win_condition in game.settings.wins.iter() {
|
||||||
|
match win_condition {
|
||||||
|
oj_rc_core::persist::config::PitWinCondition::StreakKills(streak_threshold) => {
|
||||||
|
for (player_id, streak) in game.player_tracking.streaks.iter() {
|
||||||
|
let player_streak = streak.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
if player_streak >= *streak_threshold {
|
||||||
|
if let Some(conn) = generic.users.read().await.get(player_id) {
|
||||||
|
log::info!("Player {} has reached the streak win condition in game {}", player_id, generic.game_guid());
|
||||||
|
Self::do_win(generic, game, conn.descriptor.team as u8).await;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
log::warn!("Player {} has a winning kill streak but is not in game {}", player_id, generic.game_guid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
oj_rc_core::persist::config::PitWinCondition::TotalKills(kills_threshold) => {
|
||||||
|
for (player_id, conn) in generic.users.read().await.iter() {
|
||||||
|
if conn.counters.kills.load(std::sync::atomic::Ordering::Relaxed) >= *kills_threshold {
|
||||||
|
log::info!("Player {} has reached the total kills win condition in game {}", player_id, generic.game_guid());
|
||||||
|
Self::do_win(generic, game, conn.descriptor.team as u8).await;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
oj_rc_core::persist::config::PitWinCondition::Score(score_threshold) => {
|
||||||
|
for (player_id, conn) in generic.users.read().await.iter() {
|
||||||
|
if conn.counters.generic_score() >= *score_threshold {
|
||||||
|
log::info!("Player {} has reached the total score win condition in game {}", player_id, generic.game_guid());
|
||||||
|
Self::do_win(generic, game, conn.descriptor.team as u8).await;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
oj_rc_core::persist::config::PitWinCondition::Damage(dmg_threshold) => {
|
||||||
|
for (player_id, conn) in generic.users.read().await.iter() {
|
||||||
|
if conn.counters.cubes.load(std::sync::atomic::Ordering::Relaxed) >= *dmg_threshold {
|
||||||
|
log::info!("Player {} has reached the total damage win condition in game {}", player_id, generic.game_guid());
|
||||||
|
Self::do_win(generic, game, conn.descriptor.team as u8).await;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
oj_rc_core::persist::config::PitWinCondition::Time => { /* handled elsewhere */},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn tick(&self, generic: &crate::matches::GenericGamemodeEngine<PitLogic>, game: &PitLogic) {
|
||||||
|
let delta = self.ticker.tick();
|
||||||
|
if delta == 0 { return; }
|
||||||
|
Self::check_win(generic, game).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct PlayerTracker {
|
struct PlayerTracker {
|
||||||
streaks: std::collections::HashMap<u8, std::sync::atomic::AtomicU32>,
|
streaks: std::collections::HashMap<u8, std::sync::atomic::AtomicU32>,
|
||||||
respawns: std::collections::HashMap<u8, std::sync::atomic::AtomicI64>,
|
respawns: std::collections::HashMap<u8, std::sync::atomic::AtomicI64>,
|
||||||
@@ -51,16 +143,20 @@ pub struct PitLogic {
|
|||||||
respawn_full_heal_duration: f32,
|
respawn_full_heal_duration: f32,
|
||||||
respawn_heal_duration: f32,
|
respawn_heal_duration: f32,
|
||||||
player_tracking: PlayerTracker,
|
player_tracking: PlayerTracker,
|
||||||
|
win_tracking: WinTracker,
|
||||||
|
settings: std::sync::Arc<oj_rc_core::persist::config::PitSettings>,
|
||||||
timer_task: tokio::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
|
timer_task: tokio::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PitLogic {
|
impl PitLogic {
|
||||||
pub fn new(config: &oj_rc_core::data::game_mode::GameModeConfig, _map: &oj_rc_core::persist::config::MapConfig, players: &[oj_rc_core::persist::user::PlayerDescriptor]) -> Self {
|
pub fn new(config: &oj_rc_core::data::game_mode::GameModeConfig, _map: &oj_rc_core::persist::config::MapConfig, players: &[oj_rc_core::persist::user::PlayerDescriptor], pit_settings: std::sync::Arc<oj_rc_core::persist::config::PitSettings>) -> Self {
|
||||||
PitLogic {
|
PitLogic {
|
||||||
respawn_full_heal_duration: config.respawn_full_heal_duration,
|
respawn_full_heal_duration: config.respawn_full_heal_duration,
|
||||||
respawn_heal_duration: config.respawn_heal_duration,
|
respawn_heal_duration: config.respawn_heal_duration,
|
||||||
player_tracking: PlayerTracker::new(players),
|
player_tracking: PlayerTracker::new(players),
|
||||||
|
win_tracking: WinTracker::new(),
|
||||||
timer_task: tokio::sync::Mutex::new(None),
|
timer_task: tokio::sync::Mutex::new(None),
|
||||||
|
settings: pit_settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +232,7 @@ impl PitLogic {
|
|||||||
|
|
||||||
async fn do_respawn_tasks(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, player_id: u8) {
|
async fn do_respawn_tasks(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, player_id: u8) {
|
||||||
log::info!("Handling respawn player {} in game {}", player_id, generic.game_guid());
|
log::info!("Handling respawn player {} in game {}", player_id, generic.game_guid());
|
||||||
let respawn_time = std::time::Duration::from_secs(10);
|
let respawn_time = std::time::Duration::from_secs(self.settings.respawn_time_seconds);
|
||||||
let now = chrono::Utc::now();
|
let now = chrono::Utc::now();
|
||||||
let respawn_timestamp = now + respawn_time;
|
let respawn_timestamp = now + respawn_time;
|
||||||
if let Some(player_respawn) = self.player_tracking.respawns.get(&player_id) {
|
if let Some(player_respawn) = self.player_tracking.respawns.get(&player_id) {
|
||||||
@@ -144,7 +240,7 @@ impl PitLogic {
|
|||||||
}
|
}
|
||||||
let respawn_payload = rlnl::events::ingame::RespawnTime {
|
let respawn_payload = rlnl::events::ingame::RespawnTime {
|
||||||
owner: player_id,
|
owner: player_id,
|
||||||
waiting_time: 10,
|
waiting_time: self.settings.respawn_time_seconds as i16,
|
||||||
};
|
};
|
||||||
generic.broadcast(
|
generic.broadcast(
|
||||||
rlnl::event_code::NetworkEvent::SetRespawnWaitingTime,
|
rlnl::event_code::NetworkEvent::SetRespawnWaitingTime,
|
||||||
@@ -152,6 +248,7 @@ impl PitLogic {
|
|||||||
&respawn_payload,
|
&respawn_payload,
|
||||||
true
|
true
|
||||||
).await;
|
).await;
|
||||||
|
// FIXME use full map spawns instead of team base spawns
|
||||||
let spawn_point = if let Some(team_spawns) = generic.map_config.spawns.get(&0) {
|
let spawn_point = if let Some(team_spawns) = generic.map_config.spawns.get(&0) {
|
||||||
if team_spawns.is_empty() {
|
if team_spawns.is_empty() {
|
||||||
oj_rc_core::persist::config::Point {
|
oj_rc_core::persist::config::Point {
|
||||||
@@ -264,7 +361,20 @@ impl CustomGameLogic for PitLogic {
|
|||||||
senders.push((conn.connection.clone(), conn.state.clone()));
|
senders.push((conn.connection.clone(), conn.state.clone()));
|
||||||
}
|
}
|
||||||
drop(read_lock);
|
drop(read_lock);
|
||||||
let new_timer_task = crate::matches::timer::match_time_syncer(senders, game_start, game_end, Vec::default(), Vec::default());
|
let end_packets = if self.settings.wins.iter().any(|cond| matches!(cond, oj_rc_core::persist::config::PitWinCondition::Time)) {
|
||||||
|
vec![
|
||||||
|
crate::matches::RlnlPacket {
|
||||||
|
event: rlnl::event_code::NetworkEvent::EndGame,
|
||||||
|
property: literustlib::packet::Property::ReliableOrdered,
|
||||||
|
data: Box::new(rlnl::events::ingame::GameEnd {
|
||||||
|
reason: rlnl::types::GameEndReason::TimeOut,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
Vec::default()
|
||||||
|
};
|
||||||
|
let new_timer_task = crate::matches::timer::match_time_syncer(senders, game_start, game_end, Vec::default(), end_packets);
|
||||||
let mut timer_lock = self.timer_task.lock().await;
|
let mut timer_lock = self.timer_task.lock().await;
|
||||||
if let Some(timer_t) = &*timer_lock { // this is quite unlikely (i.e. impossible), but I've done it for completeness
|
if let Some(timer_t) = &*timer_lock { // this is quite unlikely (i.e. impossible), but I've done it for completeness
|
||||||
log::warn!("Aborting an existing timer task for pit mode suggests an assumption was wrong");
|
log::warn!("Aborting an existing timer task for pit mode suggests an assumption was wrong");
|
||||||
@@ -283,7 +393,12 @@ impl CustomGameLogic for PitLogic {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_motion(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _motion: &rlnl::machine_motion::MachineMotion, _location: (f32, f32, f32)) -> bool {
|
async fn on_motion(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, _motion: &rlnl::machine_motion::MachineMotion, _location: (f32, f32, f32)) -> bool {
|
||||||
|
if generic.is_game_done() {
|
||||||
|
self.abort_timer_sync().await;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
self.win_tracking.tick(generic, self).await;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ impl SurrenderTracker {
|
|||||||
let threshold = self.players.len() / 2;
|
let threshold = self.players.len() / 2;
|
||||||
let declined = if total_no_votes > threshold {
|
let declined = if total_no_votes > threshold {
|
||||||
Some(rlnl::events::ingame::SurrenderDeclined {
|
Some(rlnl::events::ingame::SurrenderDeclined {
|
||||||
surrendering_player_id: player_id.or_else(|| self.players.keys().next().map(|x| *x)).unwrap_or(0) as i32,
|
surrendering_player_id: player_id.or_else(|| self.players.keys().next().copied()).unwrap_or(0) as i32,
|
||||||
game_time_elapsed,
|
game_time_elapsed,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@@ -148,7 +148,7 @@ impl SurrenderGameTracker {
|
|||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
return result;
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn request_new<L: crate::matches::CustomGameLogic>(&self, team: u8, initiator: u8, players: impl std::iter::Iterator<Item=u8>, generic: &crate::matches::GenericGamemodeEngine<L>) -> SurrenderVoteResult {
|
pub async fn request_new<L: crate::matches::CustomGameLogic>(&self, team: u8, initiator: u8, players: impl std::iter::Iterator<Item=u8>, generic: &crate::matches::GenericGamemodeEngine<L>) -> SurrenderVoteResult {
|
||||||
|
|||||||
@@ -30,4 +30,5 @@ pub fn handler() -> OperationsHandler<crate::UserTy, crate::data::custom::Custom
|
|||||||
.add(polariton_server::operations::Ack::<6, _>::default()) // AvatarUpdatedRequest, sent on services_room avatar_set success (just needs to be ack-ed; no params)
|
.add(polariton_server::operations::Ack::<6, _>::default()) // AvatarUpdatedRequest, sent on services_room avatar_set success (just needs to be ack-ed; no params)
|
||||||
.add(calculate_mmr::mmr_provider())
|
.add(calculate_mmr::mmr_provider())
|
||||||
.add(polariton_server::operations::Ack::<25, _>::default()) // save social settings, sent on escape menu settings save (should probably be saved someday...)
|
.add(polariton_server::operations::Ack::<25, _>::default()) // save social settings, sent on escape menu settings save (should probably be saved someday...)
|
||||||
|
.add(polariton_server::operations::Ack::<0, _>::default()) // send friend request, can be sent from match leaderboard
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user