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

Add team death match support #33

This commit is contained in:
NG (Graham)
2025-09-14 12:08:52 -04:00
parent d194e29ff7
commit 525fa8b87e
13 changed files with 565 additions and 65 deletions

View File

@@ -882,7 +882,6 @@ enum WinMode {
}
pub struct BattleArenaLogic {
game_end: std::sync::atomic::AtomicI64,
respawn_full_heal_duration: f32,
respawn_heal_duration: f32,
timer_task: tokio::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
@@ -900,14 +899,11 @@ impl BattleArenaLogic {
const CLASP_ID: u32 = 606866102;
pub fn new(config: &oj_rc_core::data::game_mode::GameModeConfig, map: &oj_rc_core::persist::config::MapConfig, parsers: &oj_rc_core::cubes::CubeParsers, ba_config: oj_rc_core::data::battle_arena_config::BattleArenaData) -> Self {
let dur = std::time::Duration::from_secs((config.game_time_minutes as u64) * 60);
let fake_end = (chrono::Utc::now() + dur).timestamp();
let cube_parser = parsers.locations_of();
let crystals = cube_parser.locations_of_by_distance_to_first(&mut std::io::Cursor::new(&ba_config.base_machine_map), Self::CRYSTAL_ID, Self::CLASP_ID);
Self {
respawn_full_heal_duration: config.respawn_full_heal_duration,
respawn_heal_duration: config.respawn_heal_duration,
game_end: std::sync::atomic::AtomicI64::new(fake_end),
timer_task: tokio::sync::Mutex::new(None),
player_tracking: PlayerTracker::new(),
capture_tracking: PointTracker::new(map.capture_points.iter().map(|(_, speed)| *speed)),
@@ -955,7 +951,7 @@ impl BattleArenaLogic {
}
async fn check_if_match_time_is_done(&self, generic: &crate::matches::GenericGamemodeEngine<Self>) -> bool {
if self.game_end.load(std::sync::atomic::Ordering::Relaxed) <= chrono::Utc::now().timestamp() {
if generic.is_game_past_end_time() {
// find winning team
let mut winning_team = None;
for (base, tracking) in self.base_tracking.bases.iter() {
@@ -1063,35 +1059,13 @@ impl BattleArenaLogic {
z: 10.0 * (player_team as f32) + 10.0,
}
};
let connections = generic.users.read().await.values().map(|player_info| player_info.connection.clone()).collect();
tokio::task::spawn(Self::respawn_player_after(respawn_timestamp, connections, spawn_point, player_id));
let connections = generic.users.read().await.values().map(|player_info| player_info.connection.clone()).collect();
tokio::task::spawn(super::respawn_player_after(respawn_timestamp, connections, spawn_point, player_id));
} else {
log::error!("Player {} cannot respawn because they are not in a team!?", player_id);
}
}
async fn respawn_player_after(after: chrono::DateTime<chrono::Utc>, players: Vec<crate::matches::generic::UserSender>, spawn: oj_rc_core::persist::config::Point, player_id: u8) {
let sleep_dur = after.signed_duration_since(chrono::Utc::now()).to_std().expect("Respawn duration too long to sleep");
tokio::time::sleep(sleep_dur).await;
let spawn_payload = rlnl::events::sync::SpawnPoint {
pos: rlnl::types::PosQuatPair {
pos: rlnl::types::CompressedVec3::from((spawn.x, spawn.y, spawn.z)),
rot: rlnl::types::CompressedQuat { x: 0, y: 0, z: 0 },
},
owner: player_id,
};
log::debug!("Respawning player {} after {}ms", player_id, sleep_dur.as_millis());
for player in players {
if !player.connection.is_connected() { continue; }
crate::events::log_lnl_send_failure(player.rlnl().send_data(
&spawn_payload,
rlnl::event_code::NetworkEvent::FreeRespawnPoint,
literustlib::packet::Property::ReliableOrdered,
&player.connection,
).await);
}
}
async fn do_team_base_stealing(&self, cube_damage: &rlnl::events::ingame::DestroyCubeNoEffect, generic: &crate::matches::GenericGamemodeEngine<Self>, actual_damage_data: impl FnOnce(Vec<rlnl::types::CubeState>) -> crate::matches::RlnlPacket) {
let base_id = cube_damage.hit_machine_id as u8;
let mut total_destroyed = 0;
@@ -1175,6 +1149,9 @@ impl CustomGameLogic for BattleArenaLogic {
}
async fn on_player_end(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, player: &crate::matches::generic::UserConnection) -> bool {
if generic.is_game_done() {
return true;
}
let player_id = player.descriptor.player_id;
self.do_destruct_tasks(generic, player_id).await;
self.player_tracking.disconnect_player(player_id).await;
@@ -1372,7 +1349,6 @@ impl CustomGameLogic for BattleArenaLogic {
timer_t.abort();
}
*timer_lock = Some(new_timer_task);
self.game_end.store(game_end.timestamp(), std::sync::atomic::Ordering::Relaxed);
generic.broadcast(
rlnl::event_code::NetworkEvent::SetSurrenderTimes,
literustlib::packet::Property::ReliableOrdered,
@@ -1466,7 +1442,7 @@ impl CustomGameLogic for BattleArenaLogic {
async fn on_motion(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, motion: &rlnl::machine_motion::MachineMotion, location: (f32, f32, f32)) -> bool {
let game_start = generic.game_start.load(std::sync::atomic::Ordering::Relaxed);
if generic.game_start.load(std::sync::atomic::Ordering::Relaxed) == -1 || chrono::Utc::now().timestamp() < game_start {
if generic.game_start.load(std::sync::atomic::Ordering::Relaxed) == i64::MIN || chrono::Utc::now().timestamp() < game_start {
// game is not in progress, ignore motion event
log::debug!("Ignoring early motion event from player {}", motion.player_id);
return true;
@@ -1548,7 +1524,6 @@ impl CustomGameLogic for BattleArenaLogic {
).await;
},
(rlnl::event_code::NetworkEvent::SurrenderRequest, literustlib::packet::Property::ReliableOrdered) => {
// TODO
let maybe_init_surr = <dyn core::any::Any>::downcast_ref::<rlnl::events::ingame::InitiateSurrender>(data.as_ref());
if let Some(init_surr) = maybe_init_surr {
if let Some(team) = self.player_tracking.team(init_surr.player_id).await {
@@ -1576,7 +1551,6 @@ impl CustomGameLogic for BattleArenaLogic {
}
}
}
},
(rlnl::event_code::NetworkEvent::AwardTeamBaseProtoniumDestroyedRequest, literustlib::packet::Property::ReliableOrdered) => {
let maybe_crystal_bonus = <dyn core::any::Any>::downcast_ref::<rlnl::events::ingame::AwardProtoniumDestroyedCubes>(data.as_ref());