mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Attempt to debounce kill bonus/counter
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
/target
|
/target
|
||||||
|
*/target
|
||||||
*.zip
|
*.zip
|
||||||
|
|
||||||
# files generated by running servers
|
# files generated by running servers
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ impl UserState {
|
|||||||
pub(super) struct MachineState {
|
pub(super) struct MachineState {
|
||||||
pub(super) selected_weapon: WeaponInfo,
|
pub(super) selected_weapon: WeaponInfo,
|
||||||
pub(super) location: Location,
|
pub(super) location: Location,
|
||||||
|
pub(super) is_alive: std::sync::Arc<std::sync::atomic::AtomicBool>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MachineState {
|
impl MachineState {
|
||||||
@@ -80,6 +81,7 @@ impl MachineState {
|
|||||||
Self {
|
Self {
|
||||||
selected_weapon: WeaponInfo::new(),
|
selected_weapon: WeaponInfo::new(),
|
||||||
location: Location::new(),
|
location: Location::new(),
|
||||||
|
is_alive: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,6 +227,35 @@ impl ConnectionMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UnclaimedStats {
|
||||||
|
kills: tokio::sync::Mutex<std::collections::HashMap<KillAttribution, chrono::DateTime<chrono::Utc>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnclaimedStats {
|
||||||
|
const DEBOUNCE_PERIOD: std::time::Duration = std::time::Duration::from_secs(2);
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
kills: tokio::sync::Mutex::new(std::collections::HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if is new
|
||||||
|
async fn debounce_kill(&self, attr: KillAttribution) -> bool {
|
||||||
|
let now = chrono::Utc::now();
|
||||||
|
if let Some(time) = self.kills.lock().await.insert(attr, now) {
|
||||||
|
(now - time).to_std().unwrap_or_default() > Self::DEBOUNCE_PERIOD
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, Eq, PartialEq, Copy, Clone)]
|
||||||
|
struct KillAttribution {
|
||||||
|
killer: u8,
|
||||||
|
victim: u8,
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) struct GenericGamemodeEngine<L: super::CustomGameLogic> {
|
pub(super) struct GenericGamemodeEngine<L: super::CustomGameLogic> {
|
||||||
pub users: tokio::sync::RwLock<std::collections::HashMap<u8, std::sync::Arc<UserConnection>>>,
|
pub users: tokio::sync::RwLock<std::collections::HashMap<u8, std::sync::Arc<UserConnection>>>,
|
||||||
descriptors: std::collections::HashMap<u8, UserDescriptor>,
|
descriptors: std::collections::HashMap<u8, UserDescriptor>,
|
||||||
@@ -241,6 +272,7 @@ pub(super) struct GenericGamemodeEngine<L: super::CustomGameLogic> {
|
|||||||
pub custom_logic_handler: L,
|
pub custom_logic_handler: L,
|
||||||
//pub fake_users: std::collections::HashMap<u8, FakeUser>,
|
//pub fake_users: std::collections::HashMap<u8, FakeUser>,
|
||||||
pub fakes_handler: super::fake::Handler,
|
pub fakes_handler: super::fake::Handler,
|
||||||
|
unclaimed: UnclaimedStats,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||||
@@ -279,6 +311,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
game_duration: std::time::Duration::from_secs((config.game_time_minutes as u64) * 60),
|
game_duration: std::time::Duration::from_secs((config.game_time_minutes as u64) * 60),
|
||||||
custom_logic_handler: custom,
|
custom_logic_handler: custom,
|
||||||
fakes_handler,
|
fakes_handler,
|
||||||
|
unclaimed: UnclaimedStats::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,14 +905,19 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
if self.custom_logic_handler.on_vehicle_destroyed(self, killer_player, remote_player).await {
|
if self.custom_logic_handler.on_vehicle_destroyed(self, killer_player, remote_player).await {
|
||||||
// the kill tracking is initiated separately by the client with kill bonus event
|
// the kill tracking is initiated separately by the client with kill bonus event
|
||||||
if let Some(killed) = self.user_descriptor(remote_player) {
|
if let Some(killed) = self.user_descriptor(remote_player) {
|
||||||
killed.counters.deaths.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
let was_killed = killed.machine.is_alive.swap(false, std::sync::atomic::Ordering::Relaxed);
|
||||||
let data = killed.counters.get_generic_packet(remote_player, rlnl::types::IngameStatId::RobotDestroyed, None);
|
if was_killed {
|
||||||
self.broadcast(
|
killed.counters.deaths.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
killed.machine.is_alive.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||||
literustlib::packet::Property::ReliableOrdered,
|
let data = killed.counters.get_generic_packet(remote_player, rlnl::types::IngameStatId::RobotDestroyed, None);
|
||||||
&data,
|
self.broadcast(
|
||||||
true,
|
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
||||||
).await;
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&data,
|
||||||
|
true,
|
||||||
|
).await;
|
||||||
|
//self.unclaimed.debounce_kill(KillAttribution { killer: killer_player, victim: remote_player }).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -950,25 +988,27 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
shooter: u8,
|
shooter: u8,
|
||||||
) {
|
) {
|
||||||
if self.custom_logic_handler.on_kill_bonus(self, shooter, shootee).await {
|
if self.custom_logic_handler.on_kill_bonus(self, shooter, shootee).await {
|
||||||
if let Some(to_reward) = self.users.read().await.get(&shooter) {
|
if self.unclaimed.debounce_kill(KillAttribution { killer: shooter, victim: shootee }).await {
|
||||||
let to_reward_desc = self.user_descriptor(shooter).unwrap();
|
if let Some(to_reward) = self.users.read().await.get(&shooter) {
|
||||||
to_reward_desc.counters.kills.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
let to_reward_desc = self.user_descriptor(shooter).unwrap();
|
||||||
crate::events::log_lnl_send_failure(to_reward.connection.rlnl().send_data(
|
to_reward_desc.counters.kills.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
&rlnl::events::ingame::Kill {
|
crate::events::log_lnl_send_failure(to_reward.connection.rlnl().send_data(
|
||||||
killee_player_id: shootee,
|
&rlnl::events::ingame::Kill {
|
||||||
killer_player_id: shooter,
|
killee_player_id: shootee,
|
||||||
},
|
killer_player_id: shooter,
|
||||||
rlnl::event_code::NetworkEvent::ConfirmedKill,
|
},
|
||||||
literustlib::packet::Property::ReliableOrdered,
|
rlnl::event_code::NetworkEvent::ConfirmedKill,
|
||||||
&to_reward.connection.connection
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
).await);
|
&to_reward.connection.connection
|
||||||
let data = to_reward_desc.counters.get_generic_packet(shooter, rlnl::types::IngameStatId::Kill, None);
|
).await);
|
||||||
self.broadcast(
|
let data = to_reward_desc.counters.get_generic_packet(shooter, rlnl::types::IngameStatId::Kill, None);
|
||||||
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
self.broadcast(
|
||||||
literustlib::packet::Property::ReliableOrdered,
|
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
||||||
&data,
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
true,
|
&data,
|
||||||
).await;
|
true,
|
||||||
|
).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1130,8 +1130,18 @@ impl BattleArenaLogic {
|
|||||||
z: 10.0 * (player_team as f32) + 10.0,
|
z: 10.0 * (player_team as f32) + 10.0,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let connections = generic.users.read().await.values().map(|player_info| player_info.connection.clone()).collect();
|
if let Some(player_desc) = generic.user_descriptor(player_id) {
|
||||||
tokio::task::spawn(super::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,
|
||||||
|
player_desc.machine.is_alive.clone(),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
log::error!("Player {} cannot respawn because they are not in the game!?", player_id);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log::error!("Player {} cannot respawn because they are not in a team!?", player_id);
|
log::error!("Player {} cannot respawn because they are not in a team!?", player_id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub use team_death_match::TeamDeathMatchLogic;
|
|||||||
|
|
||||||
mod trackers;
|
mod trackers;
|
||||||
|
|
||||||
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) {
|
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, alive_flag: std::sync::Arc<std::sync::atomic::AtomicBool>) {
|
||||||
let sleep_dur = after.signed_duration_since(chrono::Utc::now()).to_std().expect("Respawn duration too long to sleep");
|
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;
|
tokio::time::sleep(sleep_dur).await;
|
||||||
let spawn_payload = rlnl::events::sync::SpawnPoint {
|
let spawn_payload = rlnl::events::sync::SpawnPoint {
|
||||||
@@ -36,4 +36,5 @@ async fn respawn_player_after(after: chrono::DateTime<chrono::Utc>, players: Vec
|
|||||||
&player.connection,
|
&player.connection,
|
||||||
).await);
|
).await);
|
||||||
}
|
}
|
||||||
|
alive_flag.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,7 +238,12 @@ impl PitLogic {
|
|||||||
player_streak.store(0, std::sync::atomic::Ordering::Relaxed);
|
player_streak.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
if let Some(player_streak) = self.player_tracking.streaks.get(&killer) {
|
if let Some(player_streak) = self.player_tracking.streaks.get(&killer) {
|
||||||
player_streak.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
if let Some(victim_respawn) = self.player_tracking.respawns.get(&victim) {
|
||||||
|
let now = chrono::Utc::now().timestamp();
|
||||||
|
if victim_respawn.load(std::sync::atomic::Ordering::Relaxed) <= now {
|
||||||
|
player_streak.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.do_leader_update(generic, killer, victim).await;
|
self.do_leader_update(generic, killer, victim).await;
|
||||||
}
|
}
|
||||||
@@ -314,19 +319,27 @@ impl PitLogic {
|
|||||||
if let Some(player_respawn) = self.player_tracking.respawns.get(&player_id) {
|
if let Some(player_respawn) = self.player_tracking.respawns.get(&player_id) {
|
||||||
player_respawn.store(respawn_timestamp.timestamp(), std::sync::atomic::Ordering::Relaxed);
|
player_respawn.store(respawn_timestamp.timestamp(), std::sync::atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
let respawn_payload = rlnl::events::ingame::RespawnTime {
|
if let Some(player_desc) = generic.user_descriptor(player_id) {
|
||||||
owner: player_id,
|
let respawn_payload = rlnl::events::ingame::RespawnTime {
|
||||||
waiting_time: self.settings.respawn_time_seconds as i16,
|
owner: player_id,
|
||||||
};
|
waiting_time: self.settings.respawn_time_seconds as i16,
|
||||||
generic.broadcast(
|
};
|
||||||
rlnl::event_code::NetworkEvent::SetRespawnWaitingTime,
|
generic.broadcast(
|
||||||
literustlib::packet::Property::ReliableOrdered,
|
rlnl::event_code::NetworkEvent::SetRespawnWaitingTime,
|
||||||
&respawn_payload,
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
true
|
&respawn_payload,
|
||||||
).await;
|
true
|
||||||
let spawn_point = Self::choose_spawn_point(&generic.map_config, player_id).1;
|
).await;
|
||||||
let connections = generic.users.read().await.values().map(|player_info| player_info.connection.clone()).collect();
|
let spawn_point = Self::choose_spawn_point(&generic.map_config, player_id).1;
|
||||||
tokio::task::spawn(super::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,
|
||||||
|
player_desc.machine.is_alive.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -297,8 +297,18 @@ impl TeamDeathMatchLogic {
|
|||||||
z: 10.0 * (player_team as f32) + 10.0,
|
z: 10.0 * (player_team as f32) + 10.0,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let connections = generic.users.read().await.values().map(|player_info| player_info.connection.clone()).collect();
|
if let Some(player_desc) = generic.user_descriptor(player_id) {
|
||||||
tokio::task::spawn(super::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,
|
||||||
|
player_desc.machine.is_alive.clone(),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
log::error!("Player {} cannot respawn because they are not in the game!?", player_id);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log::error!("Player {} cannot respawn because they are not connected!?", player_id);
|
log::error!("Player {} cannot respawn because they are not connected!?", player_id);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user