mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add minimal Pit implementation (without win conditions) #34
This commit is contained in:
@@ -378,7 +378,7 @@ fn default_rotation() -> GameEventSequence {
|
|||||||
multiplayer: GameEvent {
|
multiplayer: GameEvent {
|
||||||
map: GameMap::Neptune3,
|
map: GameMap::Neptune3,
|
||||||
visibility: GameVisibility::Good,
|
visibility: GameVisibility::Good,
|
||||||
mode: GameType::BattleArena,
|
mode: GameType::Pit,
|
||||||
auto_heal: true,
|
auto_heal: true,
|
||||||
},
|
},
|
||||||
duration_s: 5*60, // 5 minutes
|
duration_s: 5*60, // 5 minutes
|
||||||
|
|||||||
@@ -59,13 +59,20 @@ impl QueueHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn enter_match(&self, key: QueueKey, players: Vec<QueueUser>, user: &(dyn oj_rc_core::persist::user::LobbyUser + Send + Sync)) {
|
async fn enter_match(&self, key: QueueKey, mut players: Vec<QueueUser>, user: &(dyn oj_rc_core::persist::user::LobbyUser + Send + Sync)) {
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
let mut hasher = std::hash::DefaultHasher::new();
|
let mut hasher = std::hash::DefaultHasher::new();
|
||||||
key.hash(&mut hasher);
|
key.hash(&mut hasher);
|
||||||
chrono::Utc::now().timestamp_micros().hash(&mut hasher);
|
chrono::Utc::now().timestamp_micros().hash(&mut hasher);
|
||||||
let guid = oj_rc_core::persist::user::uuid_sanitize(hasher.finish() as i64);
|
let guid = oj_rc_core::persist::user::uuid_sanitize(hasher.finish() as i64);
|
||||||
let guid_str = oj_rc_core::persist::user::i64_as_uuid_str(guid);
|
let guid_str = oj_rc_core::persist::user::i64_as_uuid_str(guid);
|
||||||
|
let team_picker = match key.mode {
|
||||||
|
oj_rc_core::data::game_mode::GameMode::Pit => |i| i as i32, // each player is on a different team
|
||||||
|
_ => |i| (i % 2) as i32, // alternate teams
|
||||||
|
};
|
||||||
|
for (i, player) in players.iter_mut().enumerate() {
|
||||||
|
player.player.team = team_picker(i);
|
||||||
|
}
|
||||||
let player_descs = players.iter().map(|x| oj_rc_core::persist::user::PlayerLobbyDescriptor {
|
let player_descs = players.iter().map(|x| oj_rc_core::persist::user::PlayerLobbyDescriptor {
|
||||||
user_id: x.user_id,
|
user_id: x.user_id,
|
||||||
team: x.player.team,
|
team: x.player.team,
|
||||||
@@ -137,7 +144,7 @@ impl QueueHandler {
|
|||||||
};
|
};
|
||||||
match user.player_data(&self.cpu_counter).await {
|
match user.player_data(&self.cpu_counter).await {
|
||||||
Ok(player_data) => {
|
Ok(player_data) => {
|
||||||
let mut new_player = QueueUser {
|
let new_player = QueueUser {
|
||||||
emitter: event_emitter,
|
emitter: event_emitter,
|
||||||
player: player_data,
|
player: player_data,
|
||||||
user_id: user.user_id(),
|
user_id: user.user_id(),
|
||||||
@@ -185,7 +192,6 @@ impl QueueHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let players_len = if let Some(players) = lock.get_mut(&key) {
|
let players_len = if let Some(players) = lock.get_mut(&key) {
|
||||||
new_player.player.team = (players.len() % 2) as _; // alternate teams
|
|
||||||
players.push(new_player);
|
players.push(new_player);
|
||||||
players.len()
|
players.len()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ mod all_loading_progress;
|
|||||||
mod weapon_select;
|
mod weapon_select;
|
||||||
mod activate_sync;
|
mod activate_sync;
|
||||||
mod loading_done;
|
mod loading_done;
|
||||||
//mod player_input;
|
mod player_input;
|
||||||
mod spot_player;
|
mod spot_player;
|
||||||
mod kill_player;
|
mod kill_player;
|
||||||
mod client_unregister;
|
mod client_unregister;
|
||||||
@@ -25,14 +25,14 @@ pub async fn handler(init_ctx: &crate::InitConfig) -> crate::handler::LnlEventHa
|
|||||||
.add(weapon_select::handler(init_ctx))
|
.add(weapon_select::handler(init_ctx))
|
||||||
.add(activate_sync::handler(init_ctx))
|
.add(activate_sync::handler(init_ctx))
|
||||||
.add(loading_done::handler(init_ctx))
|
.add(loading_done::handler(init_ctx))
|
||||||
//.add(player_input::handler(init_ctx))
|
.add(player_input::handler(init_ctx))
|
||||||
.add(crate::handlers::Broadcaster::<
|
/*.add(crate::handlers::Broadcaster::<
|
||||||
true,
|
true,
|
||||||
{rlnl::event_code::NetworkEvent::OnPlayerInputChanged as i16},
|
{rlnl::event_code::NetworkEvent::OnPlayerInputChanged as i16},
|
||||||
{rlnl::event_code::NetworkEvent::OnServerReceivedInputChange as i16},
|
{rlnl::event_code::NetworkEvent::OnServerReceivedInputChange as i16},
|
||||||
{literustlib::packet::Property::Unreliable as u8},
|
{literustlib::packet::Property::Unreliable as u8},
|
||||||
rlnl::events::ingame::PlayerIdAndInputData,
|
rlnl::events::ingame::PlayerIdAndInputData,
|
||||||
>::handler(init_ctx))
|
>::handler(init_ctx))*/
|
||||||
.add(flipper_start::handler(init_ctx))
|
.add(flipper_start::handler(init_ctx))
|
||||||
/*.add(crate::handlers::Broadcaster::<
|
/*.add(crate::handlers::Broadcaster::<
|
||||||
true,
|
true,
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ pub struct PlayerInput {
|
|||||||
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn handler(init_ctx: &crate::InitConfig) -> crate::handlers::simple_typed::SimpleRlnl<rlnl::events::ingame::MultiPlayerInputChanged, PlayerInput> {
|
pub(super) fn handler(init_ctx: &crate::InitConfig) -> crate::handlers::SimpleRlnl<rlnl::events::ingame::PlayerIdAndInputData, PlayerInput> {
|
||||||
crate::handlers::simple_typed::SimpleRlnl::new(PlayerInput::new(init_ctx))
|
crate::handlers::SimpleRlnl::new(PlayerInput::new(init_ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerInput {
|
impl PlayerInput {
|
||||||
@@ -15,17 +15,15 @@ impl PlayerInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl crate::handlers::simple_typed::RlnlEventCodeHandler for PlayerInput {
|
impl crate::handlers::RlnlEventCodeHandler for PlayerInput {
|
||||||
type In = rlnl::events::ingame::MultiPlayerInputChanged;
|
type In = rlnl::events::ingame::PlayerIdAndInputData;
|
||||||
const CODE: rlnl::event_code::NetworkEvent = rlnl::event_code::NetworkEvent::OnPlayerInputChanged;
|
const CODE: rlnl::event_code::NetworkEvent = rlnl::event_code::NetworkEvent::OnPlayerInputChanged;
|
||||||
|
|
||||||
async fn handle(&self, data: Self::In, _peer: &std::sync::Arc<literustlib_server::Connection<crate::PacketData>>, user: &crate::UserData, _sender: &std::sync::Arc<literustlib_server::DataSender<crate::PacketData>>) {
|
async fn handle(&self, data: Self::In, _peer: &std::sync::Arc<literustlib_server::Connection<crate::PacketData>>, user: &crate::UserData, _sender: &std::sync::Arc<literustlib_server::DataSender<crate::PacketData>>) {
|
||||||
if let Some(user_info) = user.user().await {
|
if let Some(user_info) = user.user().await {
|
||||||
super::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::BroadcastRlnl {
|
super::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::PlayerInputChanged {
|
||||||
user_id: user_info.user_id(),
|
user_id: user_info.user_id(),
|
||||||
event: rlnl::event_code::NetworkEvent::OnServerReceivedInputChange,
|
data,
|
||||||
property: literustlib::packet::Property::Unreliable,
|
|
||||||
data: Some(Box::new(data)),
|
|
||||||
}).await);
|
}).await);
|
||||||
} else {
|
} else {
|
||||||
log::error!("Failed to rebroadcast OnPlayerInputChanged for user (no auth!)");
|
log::error!("Failed to rebroadcast OnPlayerInputChanged for user (no auth!)");
|
||||||
|
|||||||
@@ -113,9 +113,26 @@ impl GameMatches {
|
|||||||
fakes_handler,
|
fakes_handler,
|
||||||
);
|
);
|
||||||
Ok(engine.spawn())
|
Ok(engine.spawn())
|
||||||
}
|
},
|
||||||
|
oj_rc_core::data::game_mode::GameMode::Pit => {
|
||||||
|
log::warn!("Game {}: Pit is experimental", guid);
|
||||||
|
let inner = super::modes::PitLogic::new(
|
||||||
|
&self.mode_configs.the_pit,
|
||||||
|
&map_config,
|
||||||
|
&players,
|
||||||
|
);
|
||||||
|
let engine = super::GenericGamemodeEngine::new(
|
||||||
|
game_info,
|
||||||
|
map_config,
|
||||||
|
&self.mode_configs.battle_arena,
|
||||||
|
players,
|
||||||
|
inner,
|
||||||
|
fakes_handler,
|
||||||
|
);
|
||||||
|
Ok(engine.spawn())
|
||||||
|
},
|
||||||
mode => {
|
mode => {
|
||||||
// TODO support mode gamemodes
|
// TODO support more gamemodes
|
||||||
Err(oj_rc_core::persist::user::MultiplayerError {
|
Err(oj_rc_core::persist::user::MultiplayerError {
|
||||||
code: oj_rc_core::persist::user::MultiplayerErrorCode::CustomString,
|
code: oj_rc_core::persist::user::MultiplayerErrorCode::CustomString,
|
||||||
message: format!("Game mode {:?} is not supported (yet)", mode),
|
message: format!("Game mode {:?} is not supported (yet)", mode),
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ pub trait CustomGameLogic: Sized + Send + Sync + 'static {
|
|||||||
async fn on_vehicle_destroyed(&self, generic: &super::GenericGamemodeEngine<Self>, killer: u8, victim: u8) -> bool;
|
async fn on_vehicle_destroyed(&self, generic: &super::GenericGamemodeEngine<Self>, killer: u8, victim: u8) -> bool;
|
||||||
/// Called when a player's vehicle self-destructs.
|
/// Called when a player's vehicle self-destructs.
|
||||||
async fn on_vehicle_self_destruct(&self, generic: &super::GenericGamemodeEngine<Self>, user: u8, is_classic: bool) -> bool;
|
async fn on_vehicle_self_destruct(&self, generic: &super::GenericGamemodeEngine<Self>, user: u8, is_classic: bool) -> bool;
|
||||||
|
/// Called when a kill bonus event is received after a confirmed kill
|
||||||
|
async fn on_kill_bonus(&self, generic: &super::GenericGamemodeEngine<Self>, killer: u8, victim: u8) -> bool;
|
||||||
/// Called during loading, before the sync stage is entered.
|
/// Called during loading, before the sync stage is entered.
|
||||||
///
|
///
|
||||||
/// Roughly, loading is as follows: `Loading -> WaitingForSync -> Sync -> WaitingToStart -> InGame`.
|
/// Roughly, loading is as follows: `Loading -> WaitingForSync -> Sync -> WaitingToStart -> InGame`.
|
||||||
@@ -31,5 +33,8 @@ pub trait CustomGameLogic: Sized + Send + Sync + 'static {
|
|||||||
async fn on_motion(&self, generic: &super::GenericGamemodeEngine<Self>, motion: &rlnl::machine_motion::MachineMotion, location: (f32, f32, f32)) -> bool;
|
async fn on_motion(&self, generic: &super::GenericGamemodeEngine<Self>, motion: &rlnl::machine_motion::MachineMotion, location: (f32, f32, f32)) -> bool;
|
||||||
/// Called when a gamemode-specific network event is received from a client
|
/// Called when a gamemode-specific network event is received from a client
|
||||||
async fn on_custom(&self, generic: &super::GenericGamemodeEngine<Self>, user_id: i32, event: rlnl::event_code::NetworkEvent, property: literustlib::packet::Property, data: Box<dyn crate::Broadcastable>);
|
async fn on_custom(&self, generic: &super::GenericGamemodeEngine<Self>, user_id: i32, event: rlnl::event_code::NetworkEvent, property: literustlib::packet::Property, data: Box<dyn crate::Broadcastable>);
|
||||||
|
|
||||||
|
/// Called when an enemy vehicle is spotted
|
||||||
|
async fn on_spot_vehicle(&self, generic: &super::GenericGamemodeEngine<Self>, user_id: i32, remote_player: u8) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -317,6 +317,11 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
((now - game_start) as f32) / duration as f32
|
((now - game_start) as f32) / duration as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// seconds since Unix epoch
|
||||||
|
pub(super) fn game_end(&self) -> i64 {
|
||||||
|
self.game_start.load(std::sync::atomic::Ordering::Relaxed) + self.game_duration.as_secs() as i64
|
||||||
|
}
|
||||||
|
|
||||||
/*pub(super) async fn send_to_player<T: byteserde::ser_heap::ByteSerializeHeap + ?Sized>(&self, player_id: u8, code: rlnl::event_code::NetworkEvent, property: literustlib::packet::Property, data: &T) {
|
/*pub(super) async fn send_to_player<T: byteserde::ser_heap::ByteSerializeHeap + ?Sized>(&self, player_id: u8, code: rlnl::event_code::NetworkEvent, property: literustlib::packet::Property, data: &T) {
|
||||||
if let Some(player) = self.users.read().await.get(&player_id) {
|
if let Some(player) = self.users.read().await.get(&player_id) {
|
||||||
crate::events::log_lnl_send_failure(player.connection.rlnl().send_data(
|
crate::events::log_lnl_send_failure(player.connection.rlnl().send_data(
|
||||||
@@ -364,13 +369,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
self.on_load_complete(user_id).await;
|
self.on_load_complete(user_id).await;
|
||||||
},
|
},
|
||||||
super::GameMessage::SpotVehicle { user_id, remote_player } => {
|
super::GameMessage::SpotVehicle { user_id, remote_player } => {
|
||||||
self.rebroadcast(
|
self.on_spot_vehicle(user_id, remote_player).await;
|
||||||
user_id,
|
|
||||||
rlnl::event_code::NetworkEvent::RemoteEnemySpotted,
|
|
||||||
literustlib::packet::Property::ReliableOrdered,
|
|
||||||
&rlnl::events::ingame::PlayerId { player: remote_player },
|
|
||||||
true
|
|
||||||
).await;
|
|
||||||
},
|
},
|
||||||
super::GameMessage::DestroyVehicle { user_id, remote_player, killer_player } => {
|
super::GameMessage::DestroyVehicle { user_id, remote_player, killer_player } => {
|
||||||
self.on_destroy_vehicle(user_id, remote_player, killer_player).await;
|
self.on_destroy_vehicle(user_id, remote_player, killer_player).await;
|
||||||
@@ -405,6 +404,9 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
super::GameMessage::CustomLogicRlnl { user_id, event, property, data } => {
|
super::GameMessage::CustomLogicRlnl { user_id, event, property, data } => {
|
||||||
self.custom_logic_handler.on_custom(&self, user_id, event, property, data).await;
|
self.custom_logic_handler.on_custom(&self, user_id, event, property, data).await;
|
||||||
},
|
},
|
||||||
|
super::GameMessage::PlayerInputChanged { user_id, data } => {
|
||||||
|
self.on_player_input_changed(user_id, data).await;
|
||||||
|
},
|
||||||
super::GameMessage::Motion { user_id, motion } => {
|
super::GameMessage::Motion { user_id, motion } => {
|
||||||
self.on_motion(user_id, motion).await;
|
self.on_motion(user_id, motion).await;
|
||||||
},
|
},
|
||||||
@@ -706,6 +708,18 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn on_spot_vehicle(&self, user_id: i32, remote_player: u8) {
|
||||||
|
if self.custom_logic_handler.on_spot_vehicle(self, user_id, remote_player).await {
|
||||||
|
self.rebroadcast(
|
||||||
|
user_id,
|
||||||
|
rlnl::event_code::NetworkEvent::RemoteEnemySpotted,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&rlnl::events::ingame::PlayerId { player: remote_player },
|
||||||
|
true
|
||||||
|
).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn on_destroy_vehicle(&self,
|
async fn on_destroy_vehicle(&self,
|
||||||
user_id: i32,
|
user_id: i32,
|
||||||
remote_player: u8,
|
remote_player: u8,
|
||||||
@@ -798,24 +812,26 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
shootee: u8,
|
shootee: u8,
|
||||||
shooter: u8,
|
shooter: u8,
|
||||||
) {
|
) {
|
||||||
if let Some(to_reward) = self.users.read().await.get(&shooter) {
|
if self.custom_logic_handler.on_kill_bonus(self, shooter, shootee).await {
|
||||||
to_reward.counters.kills.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
if let Some(to_reward) = self.users.read().await.get(&shooter) {
|
||||||
crate::events::log_lnl_send_failure(to_reward.connection.rlnl().send_data(
|
to_reward.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.counters.get_generic_packet(shooter, rlnl::types::IngameStatId::Kill, None);
|
).await);
|
||||||
self.broadcast(
|
let data = to_reward.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -936,6 +952,20 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn on_player_input_changed(&self, user_id: i32, input: rlnl::events::ingame::PlayerIdAndInputData) {
|
||||||
|
let full_payload = rlnl::events::ingame::MultiPlayerInputChanged {
|
||||||
|
num_players: 1,
|
||||||
|
changes: vec![input],
|
||||||
|
};
|
||||||
|
self.rebroadcast(
|
||||||
|
user_id,
|
||||||
|
rlnl::event_code::NetworkEvent::OnServerReceivedInputChange,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&full_payload,
|
||||||
|
true,
|
||||||
|
).await;
|
||||||
|
}
|
||||||
|
|
||||||
async fn on_motion(&self, user_id: i32, motion: rlnl::machine_motion::MachineMotion) {
|
async fn on_motion(&self, user_id: i32, motion: rlnl::machine_motion::MachineMotion) {
|
||||||
//let (looking_at_x, looking_at_y, looking_at_z) = motion.target_point.clone().into();
|
//let (looking_at_x, looking_at_y, looking_at_z) = motion.target_point.clone().into();
|
||||||
//log::info!("Player {} looking at ({}, {}, {})", motion.player_id, looking_at_x, looking_at_y, looking_at_z);
|
//log::info!("Player {} looking at ({}, {}, {})", motion.player_id, looking_at_x, looking_at_y, looking_at_z);
|
||||||
|
|||||||
@@ -90,6 +90,10 @@ pub enum GameMessage {
|
|||||||
property: literustlib::packet::Property,
|
property: literustlib::packet::Property,
|
||||||
data: Box<dyn crate::Broadcastable>,
|
data: Box<dyn crate::Broadcastable>,
|
||||||
},
|
},
|
||||||
|
PlayerInputChanged {
|
||||||
|
user_id: i32,
|
||||||
|
data: rlnl::events::ingame::PlayerIdAndInputData,
|
||||||
|
},
|
||||||
Motion {
|
Motion {
|
||||||
user_id: i32,
|
user_id: i32,
|
||||||
motion: rlnl::machine_motion::MachineMotion,
|
motion: rlnl::machine_motion::MachineMotion,
|
||||||
@@ -122,6 +126,7 @@ impl GameMessage {
|
|||||||
Self::BroadcastRlnl { user_id, .. } => *user_id,
|
Self::BroadcastRlnl { user_id, .. } => *user_id,
|
||||||
Self::RebroadcastRlnl { skip_user_id, .. } => *skip_user_id,
|
Self::RebroadcastRlnl { skip_user_id, .. } => *skip_user_id,
|
||||||
Self::CustomLogicRlnl { user_id, .. } => *user_id,
|
Self::CustomLogicRlnl { user_id, .. } => *user_id,
|
||||||
|
Self::PlayerInputChanged { user_id, .. } => *user_id,
|
||||||
Self::Motion { user_id, .. } => *user_id,
|
Self::Motion { user_id, .. } => *user_id,
|
||||||
Self::NoOp => unreachable!("NoOp is irrelevant for user ID"),
|
Self::NoOp => unreachable!("NoOp is irrelevant for user ID"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1195,6 +1195,10 @@ impl CustomGameLogic for BattleArenaLogic {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn on_kill_bonus(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _killer: u8, _victim: u8) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
async fn extra_sync_events(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, _player: &crate::matches::generic::UserConnection) -> Vec<crate::matches::RlnlPacket> {
|
async fn extra_sync_events(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, _player: &crate::matches::generic::UserConnection) -> Vec<crate::matches::RlnlPacket> {
|
||||||
vec![
|
vec![
|
||||||
Some(crate::matches::RlnlPacket {
|
Some(crate::matches::RlnlPacket {
|
||||||
@@ -1591,4 +1595,8 @@ impl CustomGameLogic for BattleArenaLogic {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn on_spot_vehicle(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _remote_player: u8) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,14 +331,11 @@ pub struct EliminationLogic {
|
|||||||
bases: BaseTracker,
|
bases: BaseTracker,
|
||||||
respawn_full_heal_duration: f32,
|
respawn_full_heal_duration: f32,
|
||||||
respawn_heal_duration: f32,
|
respawn_heal_duration: f32,
|
||||||
game_end: std::sync::atomic::AtomicI64,
|
|
||||||
timer_task: tokio::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
|
timer_task: tokio::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EliminationLogic {
|
impl EliminationLogic {
|
||||||
pub fn new(config: &oj_rc_core::data::game_mode::GameModeConfig, map: &oj_rc_core::persist::config::MapConfig) -> Self {
|
pub fn new(config: &oj_rc_core::data::game_mode::GameModeConfig, map: &oj_rc_core::persist::config::MapConfig) -> Self {
|
||||||
let dur = std::time::Duration::from_secs((config.game_time_minutes as u64) * 60);
|
|
||||||
let fake_end = (chrono::Utc::now() + dur).timestamp();
|
|
||||||
Self {
|
Self {
|
||||||
tracked: PlayerTracker {
|
tracked: PlayerTracker {
|
||||||
alive: tokio::sync::Mutex::new(std::collections::HashMap::new()),
|
alive: tokio::sync::Mutex::new(std::collections::HashMap::new()),
|
||||||
@@ -351,7 +348,6 @@ impl EliminationLogic {
|
|||||||
},
|
},
|
||||||
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,
|
||||||
game_end: std::sync::atomic::AtomicI64::new(fake_end),
|
|
||||||
timer_task: tokio::sync::Mutex::new(None),
|
timer_task: tokio::sync::Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -416,7 +412,7 @@ impl CustomGameLogic for EliminationLogic {
|
|||||||
if generic.is_game_done() {
|
if generic.is_game_done() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if chrono::Utc::now().timestamp() >= self.game_end.load(std::sync::atomic::Ordering::Relaxed) {
|
if chrono::Utc::now().timestamp() >= generic.game_end() {
|
||||||
generic.game_done();
|
generic.game_done();
|
||||||
self.abort_timer_sync().await;
|
self.abort_timer_sync().await;
|
||||||
return true;
|
return true;
|
||||||
@@ -466,6 +462,10 @@ impl CustomGameLogic for EliminationLogic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn on_kill_bonus(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _killer: u8, _victim: u8) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
async fn extra_sync_events(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, _player: &crate::matches::generic::UserConnection) -> Vec<crate::matches::RlnlPacket> {
|
async fn extra_sync_events(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, _player: &crate::matches::generic::UserConnection) -> Vec<crate::matches::RlnlPacket> {
|
||||||
vec![
|
vec![
|
||||||
crate::matches::RlnlPacket {
|
crate::matches::RlnlPacket {
|
||||||
@@ -518,7 +518,6 @@ impl CustomGameLogic for EliminationLogic {
|
|||||||
timer_t.abort();
|
timer_t.abort();
|
||||||
}
|
}
|
||||||
*timer_lock = Some(new_timer_task);
|
*timer_lock = Some(new_timer_task);
|
||||||
self.game_end.store(game_end.timestamp(), std::sync::atomic::Ordering::Relaxed);
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,6 +562,10 @@ impl CustomGameLogic for EliminationLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn on_custom(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _event: rlnl::event_code::NetworkEvent, _property: literustlib::packet::Property, _data: Box<dyn crate::Broadcastable>) {}
|
async fn on_custom(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _event: rlnl::event_code::NetworkEvent, _property: literustlib::packet::Property, _data: Box<dyn crate::Broadcastable>) {}
|
||||||
|
|
||||||
|
async fn on_spot_vehicle(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _remote_player: u8) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// spawn points (best guess)
|
// spawn points (best guess)
|
||||||
|
|||||||
@@ -8,4 +8,7 @@ pub use elimination::EliminationLogic;
|
|||||||
mod battle_arena;
|
mod battle_arena;
|
||||||
pub use battle_arena::BattleArenaLogic;
|
pub use battle_arena::BattleArenaLogic;
|
||||||
|
|
||||||
|
mod pit;
|
||||||
|
pub use pit::PitLogic;
|
||||||
|
|
||||||
mod trackers;
|
mod trackers;
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ impl CustomGameLogic for NoOpLogic {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn on_kill_bonus(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _killer: u8, _victim: u8) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
async fn extra_sync_events(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _player: &crate::matches::generic::UserConnection) -> Vec<crate::matches::RlnlPacket> {
|
async fn extra_sync_events(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _player: &crate::matches::generic::UserConnection) -> Vec<crate::matches::RlnlPacket> {
|
||||||
Vec::default()
|
Vec::default()
|
||||||
}
|
}
|
||||||
@@ -42,4 +46,8 @@ impl CustomGameLogic for NoOpLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn on_custom(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _event: rlnl::event_code::NetworkEvent, _property: literustlib::packet::Property, _data: Box<dyn crate::Broadcastable>) {}
|
async fn on_custom(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _event: rlnl::event_code::NetworkEvent, _property: literustlib::packet::Property, _data: Box<dyn crate::Broadcastable>) {}
|
||||||
|
|
||||||
|
async fn on_spot_vehicle(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _remote_player: u8) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
295
rc_multiplayer/src/matches/modes/pit.rs
Normal file
295
rc_multiplayer/src/matches/modes/pit.rs
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
use crate::matches::CustomGameLogic;
|
||||||
|
|
||||||
|
struct PlayerTracker {
|
||||||
|
streaks: std::collections::HashMap<u8, std::sync::atomic::AtomicU32>,
|
||||||
|
respawns: std::collections::HashMap<u8, std::sync::atomic::AtomicI64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerTracker {
|
||||||
|
fn new(players: &[oj_rc_core::persist::user::PlayerDescriptor]) -> Self {
|
||||||
|
Self {
|
||||||
|
streaks: players.iter().map(|p| (p.player_id, std::sync::atomic::AtomicU32::new(0))).collect(),
|
||||||
|
respawns: players.iter().map(|p| (p.player_id, std::sync::atomic::AtomicI64::new(0))).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn streak_leader(&self) -> Option<u8> {
|
||||||
|
let mut max = None;
|
||||||
|
for (player_id, streak) in self.streaks.iter() {
|
||||||
|
if let Some((_, cur_max_streak)) = max {
|
||||||
|
let streak = streak.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
if streak > cur_max_streak {
|
||||||
|
max = Some((*player_id, streak));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
max = Some((*player_id, streak.load(std::sync::atomic::Ordering::Relaxed)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
max.map(|(player_id, _streak)| player_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pit_stats(&self, users: &std::collections::HashMap<u8, crate::matches::generic::UserConnection>) -> rlnl::events::ingame::PitModeState {
|
||||||
|
let mut player_stats = Vec::with_capacity(self.streaks.len());
|
||||||
|
for (player_id, streak) in self.streaks.iter() {
|
||||||
|
if let Some(user) = users.get(player_id) {
|
||||||
|
player_stats.push(rlnl::types::PitPlayerStats {
|
||||||
|
player_id: *player_id,
|
||||||
|
score: user.counters.generic_score(),
|
||||||
|
streak: streak.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rlnl::events::ingame::PitModeState {
|
||||||
|
num_scores: player_stats.len() as u8,
|
||||||
|
scores: player_stats,
|
||||||
|
leader_id: self.streak_leader().map(|x| x as i32).unwrap_or(-1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PitLogic {
|
||||||
|
respawn_full_heal_duration: f32,
|
||||||
|
respawn_heal_duration: f32,
|
||||||
|
player_tracking: PlayerTracker,
|
||||||
|
timer_task: tokio::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
PitLogic {
|
||||||
|
respawn_full_heal_duration: config.respawn_full_heal_duration,
|
||||||
|
respawn_heal_duration: config.respawn_heal_duration,
|
||||||
|
player_tracking: PlayerTracker::new(players),
|
||||||
|
timer_task: tokio::sync::Mutex::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn abort_timer_sync(&self) {
|
||||||
|
let mut lock = self.timer_task.lock().await;
|
||||||
|
if let Some(timer_t) = &*lock {
|
||||||
|
timer_t.abort();
|
||||||
|
log::debug!("Aborted elimination match timer task");
|
||||||
|
}
|
||||||
|
*lock = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_leader_update(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, killer: u8, victim: u8) {
|
||||||
|
let leader = if let Some(leader_id) = self.player_tracking.streak_leader() {
|
||||||
|
leader_id
|
||||||
|
} else {
|
||||||
|
log::warn!("Pit game {} has no leader", generic.game_guid());
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let killer_score = if let Some(user) = generic.users.read().await.get(&killer) {
|
||||||
|
user.counters.generic_score()
|
||||||
|
} else {
|
||||||
|
log::warn!("Player {} score not found", killer);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let killer_streak = if let Some(streak) = self.player_tracking.streaks.get(&killer) {
|
||||||
|
streak.load(std::sync::atomic::Ordering::Relaxed)
|
||||||
|
} else {
|
||||||
|
log::warn!("Player {} streak not found", killer);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let payload = rlnl::events::ingame::UpdatePitScore {
|
||||||
|
player_id: killer,
|
||||||
|
score: killer_score,
|
||||||
|
streak: killer_streak,
|
||||||
|
destroyed_id: victim,
|
||||||
|
leader_id: leader,
|
||||||
|
};
|
||||||
|
generic.broadcast(
|
||||||
|
rlnl::event_code::NetworkEvent::PitLeaderBoardUpdate,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&payload,
|
||||||
|
true,
|
||||||
|
).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_leaderboard_update(&self, generic: &crate::matches::GenericGamemodeEngine<Self>) {
|
||||||
|
let latest_stats = self.player_tracking.pit_stats(&*generic.users.read().await);
|
||||||
|
generic.broadcast(
|
||||||
|
rlnl::event_code::NetworkEvent::PitModeState,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&latest_stats,
|
||||||
|
true,
|
||||||
|
).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_selfdestruct_tasks(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, player_id: u8) {
|
||||||
|
if let Some(player_streak) = self.player_tracking.streaks.get(&player_id) {
|
||||||
|
player_streak.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
self.do_leader_update(generic, player_id, player_id).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_kill_tasks(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, killer: u8, victim: u8) {
|
||||||
|
if let Some(player_streak) = self.player_tracking.streaks.get(&victim) {
|
||||||
|
player_streak.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
if let Some(player_streak) = self.player_tracking.streaks.get(&killer) {
|
||||||
|
player_streak.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
self.do_leader_update(generic, killer, victim).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
let respawn_time = std::time::Duration::from_secs(10);
|
||||||
|
let now = chrono::Utc::now();
|
||||||
|
let respawn_timestamp = now + respawn_time;
|
||||||
|
if let Some(player_respawn) = self.player_tracking.respawns.get(&player_id) {
|
||||||
|
player_respawn.store(respawn_timestamp.timestamp(), std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
let respawn_payload = rlnl::events::ingame::RespawnTime {
|
||||||
|
owner: player_id,
|
||||||
|
waiting_time: 10,
|
||||||
|
};
|
||||||
|
generic.broadcast(
|
||||||
|
rlnl::event_code::NetworkEvent::SetRespawnWaitingTime,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&respawn_payload,
|
||||||
|
true
|
||||||
|
).await;
|
||||||
|
let spawn_point = if let Some(team_spawns) = generic.map_config.spawns.get(&0) {
|
||||||
|
if team_spawns.is_empty() {
|
||||||
|
oj_rc_core::persist::config::Point {
|
||||||
|
x: 10.0 * (player_id as f32),
|
||||||
|
y: 100.0,
|
||||||
|
z: 10.0,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let index = (player_id as usize) % team_spawns.len();
|
||||||
|
team_spawns[index].clone()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oj_rc_core::persist::config::Point {
|
||||||
|
x: 10.0 * (player_id as f32),
|
||||||
|
y: 100.0,
|
||||||
|
z: 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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_trait::async_trait]
|
||||||
|
impl CustomGameLogic for PitLogic {
|
||||||
|
async fn on_player_join(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _player: &crate::matches::generic::UserConnection, _others: &[oj_rc_core::persist::user::PlayerDescriptor]) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_player_end(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _player: &crate::matches::generic::UserConnection) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_vehicle_destroyed(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, killer: u8, victim: u8) -> bool {
|
||||||
|
self.do_kill_tasks(generic, killer, victim).await;
|
||||||
|
self.do_respawn_tasks(generic, victim).await;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_vehicle_self_destruct(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, user: u8, _is_classic: bool) -> bool {
|
||||||
|
self.do_selfdestruct_tasks(generic, user).await;
|
||||||
|
self.do_respawn_tasks(generic, user).await;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_kill_bonus(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, killer: u8, victim: u8) -> bool {
|
||||||
|
if let Some(to_reward) = generic.users.read().await.get(&killer) {
|
||||||
|
to_reward.counters.kills.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
crate::events::log_lnl_send_failure(to_reward.connection.rlnl().send_data(
|
||||||
|
&rlnl::events::ingame::Kill {
|
||||||
|
killee_player_id: victim,
|
||||||
|
killer_player_id: killer,
|
||||||
|
},
|
||||||
|
rlnl::event_code::NetworkEvent::ConfirmedKill,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&to_reward.connection.connection
|
||||||
|
).await);
|
||||||
|
let data = to_reward.counters.get_generic_packet(killer, rlnl::types::IngameStatId::Kill, None);
|
||||||
|
generic.broadcast(
|
||||||
|
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&data,
|
||||||
|
true,
|
||||||
|
).await;
|
||||||
|
self.do_leaderboard_update(generic).await;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn extra_sync_events(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _player: &crate::matches::generic::UserConnection) -> Vec<crate::matches::RlnlPacket> {
|
||||||
|
vec![
|
||||||
|
crate::matches::RlnlPacket {
|
||||||
|
event: rlnl::event_code::NetworkEvent::GameModeSettings,
|
||||||
|
property: literustlib::packet::Property::ReliableOrdered,
|
||||||
|
data: Box::new(rlnl::events::sync::UpdateGameModeSettings {
|
||||||
|
respawn_heal_duration: self.respawn_heal_duration,
|
||||||
|
respawn_full_heal_duration: self.respawn_full_heal_duration,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_countdown_start(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, game_start: chrono::DateTime<chrono::Utc>) -> bool {
|
||||||
|
let read_lock = generic.users.read().await;
|
||||||
|
let game_end = game_start + generic.game_duration;
|
||||||
|
let mut senders = Vec::with_capacity(read_lock.len());
|
||||||
|
for conn in read_lock.values() {
|
||||||
|
senders.push((conn.connection.clone(), conn.state.clone()));
|
||||||
|
}
|
||||||
|
drop(read_lock);
|
||||||
|
let new_timer_task = crate::matches::timer::match_time_syncer(senders, game_start, game_end, Vec::default(), Vec::default());
|
||||||
|
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
|
||||||
|
log::warn!("Aborting an existing timer task for pit mode suggests an assumption was wrong");
|
||||||
|
timer_t.abort();
|
||||||
|
}
|
||||||
|
*timer_lock = Some(new_timer_task);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_game_completed(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>) -> bool {
|
||||||
|
self.abort_timer_sync().await;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_broadcast(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _event_out: rlnl::event_code::NetworkEvent, _event_in: rlnl::event_code::NetworkEvent, _property: literustlib::packet::Property, _data: &Option<Box<dyn crate::Broadcastable>>, _skip_user: bool) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_motion(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _motion: &rlnl::machine_motion::MachineMotion, _location: (f32, f32, f32)) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_custom(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _event: rlnl::event_code::NetworkEvent, _property: literustlib::packet::Property, _data: Box<dyn crate::Broadcastable>) {}
|
||||||
|
|
||||||
|
async fn on_spot_vehicle(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _remote_player: u8) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user