mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Implement most of the occasional packets sent in elimination, expand interface for custom game logic #30
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -452,6 +452,12 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic_float"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -2704,6 +2710,7 @@ name = "oj_rc_lobby_room"
|
|||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
@@ -3334,6 +3341,7 @@ name = "rc_multiplayer"
|
|||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"atomic_float",
|
||||||
"bytes",
|
"bytes",
|
||||||
"byteserde",
|
"byteserde",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ oj_polariton_auth = { version = "*", path = "../polariton_auth" }
|
|||||||
polariton_server.workspace = true
|
polariton_server.workspace = true
|
||||||
oj_rc_core = { version = "*", path = "../rc_core" }
|
oj_rc_core = { version = "*", path = "../rc_core" }
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
|
chrono.workspace = true
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ impl QueueHandler {
|
|||||||
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().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 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 {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ async-trait.workspace = true
|
|||||||
bytes = "1.10"
|
bytes = "1.10"
|
||||||
byteserde = "0.6.2"
|
byteserde = "0.6.2"
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
|
atomic_float = "1.1"
|
||||||
|
|
||||||
literustlib_server = { version = "0.1", path = "../../LiteRustLib/server" }
|
literustlib_server = { version = "0.1", path = "../../LiteRustLib/server" }
|
||||||
literustlib = { version = "0.1", path = "../../LiteRustLib" }
|
literustlib = { version = "0.1", path = "../../LiteRustLib" }
|
||||||
|
|||||||
30
rc_multiplayer/src/events/flipper_start.rs
Normal file
30
rc_multiplayer/src/events/flipper_start.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
pub struct RectifierStart {
|
||||||
|
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handler(init_ctx: &crate::InitConfig) -> crate::handlers::Dataless<RectifierStart> {
|
||||||
|
crate::handlers::Dataless::new(RectifierStart::new(init_ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RectifierStart {
|
||||||
|
fn new(init_ctx: &crate::InitConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
msg_router: init_ctx.matches_chann.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl crate::handlers::DatalessEventCodeHandler for RectifierStart {
|
||||||
|
const CODE: rlnl::event_code::NetworkEvent = rlnl::event_code::NetworkEvent::AlignmentRectifierStarted;
|
||||||
|
|
||||||
|
async fn handle(&self, _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 {
|
||||||
|
super::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::FlippingStarted {
|
||||||
|
user_id: user_info.user_id(),
|
||||||
|
}).await);
|
||||||
|
} else {
|
||||||
|
log::error!("Failed to handle sync loading request for unknown user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@ mod loading_done;
|
|||||||
mod spot_player;
|
mod spot_player;
|
||||||
mod kill_player;
|
mod kill_player;
|
||||||
mod client_unregister;
|
mod client_unregister;
|
||||||
|
mod flipper_start;
|
||||||
|
mod self_destruct_elimination;
|
||||||
|
|
||||||
pub async fn handler(init_ctx: &crate::InitConfig) -> crate::handler::LnlEventHandler {
|
pub async fn handler(init_ctx: &crate::InitConfig) -> crate::handler::LnlEventHandler {
|
||||||
crate::handler::LnlEventHandler::new(init_ctx.users.clone(), crate::vehicle_motion::handler(init_ctx))
|
crate::handler::LnlEventHandler::new(init_ctx.users.clone(), crate::vehicle_motion::handler(init_ctx))
|
||||||
@@ -25,12 +27,14 @@ pub async fn handler(init_ctx: &crate::InitConfig) -> crate::handler::LnlEventHa
|
|||||||
{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(crate::handlers::DatalessBroadcaster::<
|
.add(flipper_start::handler(init_ctx))
|
||||||
|
/*.add(crate::handlers::Broadcaster::<
|
||||||
true,
|
true,
|
||||||
{rlnl::event_code::NetworkEvent::AlignmentRectifierStarted as i16},
|
{rlnl::event_code::NetworkEvent::AlignmentRectifierStarted as i16},
|
||||||
{rlnl::event_code::NetworkEvent::AlignmentRectifierStarted as i16},
|
{rlnl::event_code::NetworkEvent::AlignmentRectifierStarted as i16},
|
||||||
{literustlib::packet::Property::Unreliable as u8},
|
{literustlib::packet::Property::Unreliable as u8},
|
||||||
>::handler(init_ctx))
|
rlnl::events::ingame::PlayerId,
|
||||||
|
>::handler(init_ctx))*/
|
||||||
.add(crate::handlers::Broadcaster::<
|
.add(crate::handlers::Broadcaster::<
|
||||||
true,
|
true,
|
||||||
{rlnl::event_code::NetworkEvent::FireWeaponEffect as i16},
|
{rlnl::event_code::NetworkEvent::FireWeaponEffect as i16},
|
||||||
@@ -106,28 +110,28 @@ pub async fn handler(init_ctx: &crate::InitConfig) -> crate::handler::LnlEventHa
|
|||||||
>::handler(init_ctx))
|
>::handler(init_ctx))
|
||||||
.add(crate::handlers::Broadcaster::<
|
.add(crate::handlers::Broadcaster::<
|
||||||
true,
|
true,
|
||||||
{rlnl::event_code::NetworkEvent::ActivateTeleportEffect as i16},
|
{rlnl::event_code::NetworkEvent::BroadcastActivateTeleportEffect as i16},
|
||||||
{rlnl::event_code::NetworkEvent::ActivateTeleportEffect as i16},
|
{rlnl::event_code::NetworkEvent::ActivateTeleportEffect as i16},
|
||||||
{literustlib::packet::Property::ReliableOrdered as u8},
|
{literustlib::packet::Property::ReliableOrdered as u8},
|
||||||
rlnl::events::ingame::PlayerId,
|
rlnl::events::ingame::TeleportActivateEffect,
|
||||||
>::handler(init_ctx))
|
>::handler(init_ctx))
|
||||||
.add(crate::handlers::Broadcaster::<
|
.add(crate::handlers::Broadcaster::<
|
||||||
true,
|
true,
|
||||||
{rlnl::event_code::NetworkEvent::SpawnEmpLocator as i16},
|
{rlnl::event_code::NetworkEvent::BroadcastSpawnEmpLocator as i16},
|
||||||
{rlnl::event_code::NetworkEvent::SpawnEmpLocator as i16},
|
{rlnl::event_code::NetworkEvent::SpawnEmpLocator as i16},
|
||||||
{literustlib::packet::Property::ReliableOrdered as u8},
|
{literustlib::packet::Property::ReliableOrdered as u8},
|
||||||
rlnl::events::ingame::SpawnEmpLocator,
|
rlnl::events::ingame::SpawnEmpLocator,
|
||||||
>::handler(init_ctx))
|
>::handler(init_ctx))
|
||||||
.add(crate::handlers::Broadcaster::<
|
.add(crate::handlers::Broadcaster::<
|
||||||
true,
|
true,
|
||||||
{rlnl::event_code::NetworkEvent::SpawnEmpMachineEffect as i16},
|
{rlnl::event_code::NetworkEvent::BroadcastSpawnEmpMachineEffect as i16},
|
||||||
{rlnl::event_code::NetworkEvent::SpawnEmpMachineEffect as i16},
|
{rlnl::event_code::NetworkEvent::SpawnEmpMachineEffect as i16},
|
||||||
{literustlib::packet::Property::ReliableOrdered as u8},
|
{literustlib::packet::Property::ReliableOrdered as u8},
|
||||||
rlnl::events::ingame::NetworkStunnedMachineEffect,
|
rlnl::events::ingame::NetworkStunnedMachineEffect,
|
||||||
>::handler(init_ctx))
|
>::handler(init_ctx))
|
||||||
.add(crate::handlers::Broadcaster::<
|
.add(crate::handlers::Broadcaster::<
|
||||||
true,
|
true,
|
||||||
{rlnl::event_code::NetworkEvent::SpawnShield as i16},
|
{rlnl::event_code::NetworkEvent::ShieldSpawned as i16},
|
||||||
{rlnl::event_code::NetworkEvent::SpawnShield as i16},
|
{rlnl::event_code::NetworkEvent::SpawnShield as i16},
|
||||||
{literustlib::packet::Property::ReliableOrdered as u8},
|
{literustlib::packet::Property::ReliableOrdered as u8},
|
||||||
rlnl::events::ingame::ShieldModuleEvent,
|
rlnl::events::ingame::ShieldModuleEvent,
|
||||||
@@ -147,6 +151,28 @@ pub async fn handler(init_ctx: &crate::InitConfig) -> crate::handler::LnlEventHa
|
|||||||
rlnl::events::ingame::CosmeticAction,
|
rlnl::events::ingame::CosmeticAction,
|
||||||
>::handler(init_ctx))
|
>::handler(init_ctx))
|
||||||
.add(client_unregister::handler(init_ctx))
|
.add(client_unregister::handler(init_ctx))
|
||||||
|
.add(crate::handlers::Broadcaster::<
|
||||||
|
true,
|
||||||
|
{rlnl::event_code::NetworkEvent::BroadcastInvisible as i16},
|
||||||
|
{rlnl::event_code::NetworkEvent::MakeInvisible as i16},
|
||||||
|
{literustlib::packet::Property::ReliableOrdered as u8},
|
||||||
|
rlnl::events::ingame::PlayerId,
|
||||||
|
>::handler(init_ctx))
|
||||||
|
.add(crate::handlers::Broadcaster::<
|
||||||
|
true,
|
||||||
|
{rlnl::event_code::NetworkEvent::BroadcastVisible as i16},
|
||||||
|
{rlnl::event_code::NetworkEvent::MakeVisible as i16},
|
||||||
|
{literustlib::packet::Property::ReliableOrdered as u8},
|
||||||
|
rlnl::events::ingame::PlayerId,
|
||||||
|
>::handler(init_ctx))
|
||||||
|
.add(crate::handlers::Broadcaster::<
|
||||||
|
true,
|
||||||
|
{rlnl::event_code::NetworkEvent::UpdateVotingAfterBattle as i16},
|
||||||
|
{rlnl::event_code::NetworkEvent::UpdateVotingAfterBattle as i16},
|
||||||
|
{literustlib::packet::Property::ReliableOrdered as u8},
|
||||||
|
rlnl::events::ingame::UpdateVotingAfterBattle,
|
||||||
|
>::handler(init_ctx))
|
||||||
|
.add(self_destruct_elimination::handler(init_ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -180,7 +206,10 @@ mod _broadcast_impls {
|
|||||||
impl Broadcastable for rlnl::events::ingame::ShieldModuleEvent {}
|
impl Broadcastable for rlnl::events::ingame::ShieldModuleEvent {}
|
||||||
impl Broadcastable for rlnl::events::ingame::Taunt {}
|
impl Broadcastable for rlnl::events::ingame::Taunt {}
|
||||||
impl Broadcastable for rlnl::events::ingame::CosmeticAction {}
|
impl Broadcastable for rlnl::events::ingame::CosmeticAction {}
|
||||||
|
impl Broadcastable for rlnl::events::ingame::UpdateVotingAfterBattle {}
|
||||||
|
impl Broadcastable for rlnl::events::ingame::TeleportActivateEffect {}
|
||||||
|
|
||||||
impl Broadcastable for rlnl::events::sync::UpdateGameModeSettings {}
|
impl Broadcastable for rlnl::events::sync::UpdateGameModeSettings {}
|
||||||
impl Broadcastable for rlnl::events::GameTime {}
|
impl Broadcastable for rlnl::events::GameTime {}
|
||||||
|
impl Broadcastable for rlnl::events::ingame::TeamBaseState {}
|
||||||
}
|
}
|
||||||
|
|||||||
31
rc_multiplayer/src/events/self_destruct_elimination.rs
Normal file
31
rc_multiplayer/src/events/self_destruct_elimination.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
pub struct SelfDestructElim {
|
||||||
|
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handler(init_ctx: &crate::InitConfig) -> crate::handlers::Dataless<SelfDestructElim> {
|
||||||
|
crate::handlers::Dataless::new(SelfDestructElim::new(init_ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelfDestructElim {
|
||||||
|
fn new(init_ctx: &crate::InitConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
msg_router: init_ctx.matches_chann.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl crate::handlers::DatalessEventCodeHandler for SelfDestructElim {
|
||||||
|
const CODE: rlnl::event_code::NetworkEvent = rlnl::event_code::NetworkEvent::SelfDestructClassicMode;
|
||||||
|
|
||||||
|
async fn handle(&self, _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 {
|
||||||
|
super::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::SelfDestruct {
|
||||||
|
user_id: user_info.user_id(),
|
||||||
|
is_classic: true,
|
||||||
|
}).await);
|
||||||
|
} else {
|
||||||
|
log::error!("Failed to handle sync loading request for unknown user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ impl <const EXCLUDE_SENDER: bool, const CODE_IN: i16, const CODE_OUT: i16, const
|
|||||||
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::RebroadcastRlnl {
|
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::RebroadcastRlnl {
|
||||||
skip_user_id: user_info.user_id(),
|
skip_user_id: user_info.user_id(),
|
||||||
event: self.code_out,
|
event: self.code_out,
|
||||||
|
event_in: Self::CODE,
|
||||||
property: self.property,
|
property: self.property,
|
||||||
data: Some(Box::new(data)),
|
data: Some(Box::new(data)),
|
||||||
}).await);
|
}).await);
|
||||||
@@ -39,6 +40,7 @@ impl <const EXCLUDE_SENDER: bool, const CODE_IN: i16, const CODE_OUT: i16, const
|
|||||||
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::BroadcastRlnl {
|
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::BroadcastRlnl {
|
||||||
user_id: user_info.user_id(),
|
user_id: user_info.user_id(),
|
||||||
event: self.code_out,
|
event: self.code_out,
|
||||||
|
event_in: Self::CODE,
|
||||||
property: self.property,
|
property: self.property,
|
||||||
data: Some(Box::new(data)),
|
data: Some(Box::new(data)),
|
||||||
}).await);
|
}).await);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
pub struct DatalessBroadcaster<const EXCLUDE_SENDER: bool, const CODE_IN: i16, const CODE_OUT: i16, const PROPERTY: u8> {
|
pub struct DatalessBroadcaster<const EXCLUDE_SENDER: bool, const CODE_IN: i16, const CODE_OUT: i16, const PROPERTY: u8> {
|
||||||
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
||||||
code_out: rlnl::event_code::NetworkEvent,
|
code_out: rlnl::event_code::NetworkEvent,
|
||||||
@@ -28,6 +29,7 @@ impl <const EXCLUDE_SENDER: bool, const CODE_IN: i16, const CODE_OUT: i16, const
|
|||||||
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::RebroadcastRlnl {
|
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::RebroadcastRlnl {
|
||||||
skip_user_id: user_info.user_id(),
|
skip_user_id: user_info.user_id(),
|
||||||
event: self.code_out,
|
event: self.code_out,
|
||||||
|
event_in: Self::CODE,
|
||||||
property: self.property,
|
property: self.property,
|
||||||
data: None,
|
data: None,
|
||||||
}).await);
|
}).await);
|
||||||
@@ -35,6 +37,7 @@ impl <const EXCLUDE_SENDER: bool, const CODE_IN: i16, const CODE_OUT: i16, const
|
|||||||
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::BroadcastRlnl {
|
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::BroadcastRlnl {
|
||||||
user_id: user_info.user_id(),
|
user_id: user_info.user_id(),
|
||||||
event: self.code_out,
|
event: self.code_out,
|
||||||
|
event_in: Self::CODE,
|
||||||
property: self.property,
|
property: self.property,
|
||||||
data: None,
|
data: None,
|
||||||
}).await);
|
}).await);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ mod ingame_broadcast;
|
|||||||
pub use ingame_broadcast::Broadcaster;
|
pub use ingame_broadcast::Broadcaster;
|
||||||
|
|
||||||
mod ingame_broadcast_dataless;
|
mod ingame_broadcast_dataless;
|
||||||
|
#[allow(unused_imports)]
|
||||||
pub use ingame_broadcast_dataless::DatalessBroadcaster;
|
pub use ingame_broadcast_dataless::DatalessBroadcaster;
|
||||||
|
|
||||||
mod stub;
|
mod stub;
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ pub trait CustomGameLogic: Sized + Send + Sync + 'static {
|
|||||||
async fn on_player_join(&self, generic: &super::GenericGamemodeEngine<Self>, player: &crate::matches::generic::UserConnection, others: &[oj_rc_core::persist::user::PlayerDescriptor]) -> bool;
|
async fn on_player_join(&self, generic: &super::GenericGamemodeEngine<Self>, player: &crate::matches::generic::UserConnection, others: &[oj_rc_core::persist::user::PlayerDescriptor]) -> bool;
|
||||||
async fn on_player_end(&self, generic: &super::GenericGamemodeEngine<Self>, player: &crate::matches::generic::UserConnection) -> bool;
|
async fn on_player_end(&self, generic: &super::GenericGamemodeEngine<Self>, player: &crate::matches::generic::UserConnection) -> bool;
|
||||||
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;
|
||||||
|
async fn on_vehicle_self_destruct(&self, generic: &super::GenericGamemodeEngine<Self>, user: u8, is_classic: bool) -> bool;
|
||||||
async fn extra_sync_events(&self, generic: &super::GenericGamemodeEngine<Self>, player: &crate::matches::generic::UserConnection) -> Vec<RlnlPacket>;
|
async fn extra_sync_events(&self, generic: &super::GenericGamemodeEngine<Self>, player: &crate::matches::generic::UserConnection) -> Vec<RlnlPacket>;
|
||||||
|
async fn on_countdown_start(&self, generic: &super::GenericGamemodeEngine<Self>, game_start: chrono::DateTime<chrono::Utc>) -> bool;
|
||||||
|
async fn on_game_completed(&self, generic: &super::GenericGamemodeEngine<Self>) -> bool;
|
||||||
|
async fn on_broadcast(&self, generic: &super::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;
|
||||||
|
async fn on_motion(&self, generic: &super::GenericGamemodeEngine<Self>, motion: &rlnl::machine_motion::MachineMotion) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,12 +34,14 @@ impl UserState {
|
|||||||
|
|
||||||
pub(super) struct MachineState {
|
pub(super) struct MachineState {
|
||||||
pub(super) selected_weapon: WeaponInfo,
|
pub(super) selected_weapon: WeaponInfo,
|
||||||
|
pub(super) location: Location,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MachineState {
|
impl MachineState {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
selected_weapon: WeaponInfo::new(),
|
selected_weapon: WeaponInfo::new(),
|
||||||
|
location: Location::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,6 +60,22 @@ impl WeaponInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) struct Location {
|
||||||
|
pub x: atomic_float::AtomicF32,
|
||||||
|
pub y: atomic_float::AtomicF32,
|
||||||
|
pub z: atomic_float::AtomicF32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Location {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
x: atomic_float::AtomicF32::new(0.0),
|
||||||
|
y: atomic_float::AtomicF32::new(0.0),
|
||||||
|
z: atomic_float::AtomicF32::new(0.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub(super) enum ConnectionMode {
|
pub(super) enum ConnectionMode {
|
||||||
@@ -267,6 +285,13 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
has_active_connections |= !matches!(mode, ConnectionMode::Disconnected);
|
has_active_connections |= !matches!(mode, ConnectionMode::Disconnected);
|
||||||
}
|
}
|
||||||
is_engaged = has_active_connections;
|
is_engaged = has_active_connections;
|
||||||
|
if !has_active_connections {
|
||||||
|
if self.custom_logic_handler.on_game_completed(&self).await {
|
||||||
|
if let Err(e) = conn.user.complete_game(&self.game_guid).await {
|
||||||
|
log::error!("Failed to mark game {} as complete: {}", self.game_guid, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
conn.connection.connection.goodbye(&conn.connection.sender).await;
|
conn.connection.connection.goodbye(&conn.connection.sender).await;
|
||||||
}
|
}
|
||||||
@@ -437,13 +462,15 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
let player_count = self.player_count.load(std::sync::atomic::Ordering::Relaxed) as usize;
|
let player_count = self.player_count.load(std::sync::atomic::Ordering::Relaxed) as usize;
|
||||||
log::info!("All players ({}) are ready for game {}", player_count, self.game_guid);
|
log::info!("All players ({}) are ready for game {}", player_count, self.game_guid);
|
||||||
tokio::time::sleep(Self::END_OF_SYNC_DELAY).await;
|
tokio::time::sleep(Self::END_OF_SYNC_DELAY).await;
|
||||||
let mut senders = Vec::new();
|
|
||||||
for conn in self.users.read().await.values() {
|
|
||||||
senders.push((conn.connection.clone(), conn.state.clone()));
|
|
||||||
}
|
|
||||||
let game_start = chrono::Utc::now() + Self::COUNTDOWN_DURATION;
|
let game_start = chrono::Utc::now() + Self::COUNTDOWN_DURATION;
|
||||||
self.game_start.store(game_start.timestamp(), std::sync::atomic::Ordering::Relaxed);
|
if self.custom_logic_handler.on_countdown_start(&self, game_start).await {
|
||||||
super::countdown::match_countdown(senders, game_start);
|
let mut senders = Vec::new();
|
||||||
|
for conn in self.users.read().await.values() {
|
||||||
|
senders.push((conn.connection.clone(), conn.state.clone()));
|
||||||
|
}
|
||||||
|
self.game_start.store(game_start.timestamp(), std::sync::atomic::Ordering::Relaxed);
|
||||||
|
super::countdown::match_countdown(senders, game_start);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
super::GameMessage::SpotVehicle { user_id, remote_player } => {
|
super::GameMessage::SpotVehicle { user_id, remote_player } => {
|
||||||
@@ -466,31 +493,96 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
).await;
|
).await;
|
||||||
log::info!("Player {} was destroyed by {} ({}) in game {}", remote_player, killer_player, user_id, self.game_guid);
|
log::info!("Player {} was destroyed by {} ({}) in game {}", remote_player, killer_player, user_id, self.game_guid);
|
||||||
self.custom_logic_handler.on_vehicle_destroyed(&self, killer_player, remote_player).await;
|
self.custom_logic_handler.on_vehicle_destroyed(&self, killer_player, remote_player).await;
|
||||||
}
|
|
||||||
super::GameMessage::BroadcastRlnl { user_id: _, event, property, data } => {
|
|
||||||
if let Some(data) = data {
|
|
||||||
self.broadcast(event, property, &*data, true).await;
|
|
||||||
} else {
|
|
||||||
self.broadcast_dataless(event, property, true).await;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
super::GameMessage::RebroadcastRlnl { skip_user_id, event, property, data } => {
|
super::GameMessage::SelfDestruct { user_id, is_classic } => {
|
||||||
if let Some(data) = data {
|
if let Some(player_id) = self.user_key_by_user_id(user_id).await {
|
||||||
self.rebroadcast(skip_user_id, event, property, &*data, true).await;
|
self.rebroadcast(
|
||||||
} else {
|
user_id,
|
||||||
self.rebroadcast_dataless(skip_user_id, event, property, true).await;
|
rlnl::event_code::NetworkEvent::MachineDestroyedConfirmed,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&rlnl::events::ingame::Kill { killee_player_id: player_id, killer_player_id: player_id },
|
||||||
|
true,
|
||||||
|
).await;
|
||||||
|
log::info!("Player {} ({}) self-destructed in game {} (elimination? {})", player_id, user_id, self.game_guid, is_classic);
|
||||||
|
if self.custom_logic_handler.on_vehicle_self_destruct(&self, player_id, is_classic).await {
|
||||||
|
if is_classic {
|
||||||
|
self.rebroadcast(
|
||||||
|
user_id,
|
||||||
|
rlnl::event_code::NetworkEvent::OnAnotherClientDisconnected,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&rlnl::events::ingame::PlayerId { player: player_id },
|
||||||
|
true,
|
||||||
|
).await;
|
||||||
|
if let Some(conn) = self.users.read().await.get(&player_id) {
|
||||||
|
crate::events::log_lnl_send_failure(conn.connection.rlnl().send_empty(
|
||||||
|
rlnl::event_code::NetworkEvent::PlayerQuitRequestComplete,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&conn.connection.connection
|
||||||
|
).await);
|
||||||
|
conn.state.mode.store(ConnectionMode::Disconnected.to_u8(), std::sync::atomic::Ordering::Relaxed);
|
||||||
|
conn.connection.connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
super::GameMessage::Motion { user_id, data } => {
|
super::GameMessage::FlippingStarted { user_id } => {
|
||||||
for conn in self.users.read().await.values() {
|
if let Some(user_key) = self.user_key_by_user_id(user_id).await {
|
||||||
if conn.user.user_id() == user_id { continue; } // fun fact: the game hard crashes if you omit this
|
self.rebroadcast(
|
||||||
crate::events::log_lnl_send_failure(conn.connection.sender.send_data(crate::handler::EventData {
|
user_id,
|
||||||
message_ty: crate::data::MessageType::RobotMotion,
|
rlnl::event_code::NetworkEvent::AlignmentRectifierStarted,
|
||||||
variant: 0,
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
data_size: data.len() as _,
|
&rlnl::events::ingame::PlayerId { player: user_key },
|
||||||
data: data.clone(),
|
true,
|
||||||
}, literustlib::packet::Property::Unreliable, &conn.connection.connection).await);
|
).await;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
super::GameMessage::BroadcastRlnl { user_id, event, event_in, property, data } => {
|
||||||
|
if self.custom_logic_handler.on_broadcast(&self, user_id, event, event_in, property, &data, false).await {
|
||||||
|
if let Some(data) = data {
|
||||||
|
self.broadcast(event, property, &*data, true).await;
|
||||||
|
} else {
|
||||||
|
self.broadcast_dataless(event, property, true).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
super::GameMessage::RebroadcastRlnl { skip_user_id, event, event_in, property, data } => {
|
||||||
|
if self.custom_logic_handler.on_broadcast(&self, skip_user_id, event, event_in, property, &data, true).await {
|
||||||
|
if let Some(data) = data {
|
||||||
|
self.rebroadcast(skip_user_id, event, property, &*data, true).await;
|
||||||
|
} else {
|
||||||
|
self.rebroadcast_dataless(skip_user_id, event, property, true).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
super::GameMessage::Motion { user_id, motion } => {
|
||||||
|
if self.custom_logic_handler.on_motion(&self, &motion).await {
|
||||||
|
if let Some(conn) = self.users.read().await.get(&motion.player_id) {
|
||||||
|
let (x, y, z) = motion.rb_state.rb_pos_rot.pos.into();
|
||||||
|
conn.machine.location.x.store(x, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
conn.machine.location.y.store(y, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
conn.machine.location.z.store(z, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
//log::debug!("Player {} is at (x, y, z) ({}, {}, {})", motion.player_id, x, y, z);
|
||||||
|
use byteserde::ser_heap::ByteSerializeHeap;
|
||||||
|
let mut ser = byteserde::ser_heap::ByteSerializerHeap::default();
|
||||||
|
if let Err(e) = motion.byte_serialize_heap(&mut ser) {
|
||||||
|
log::error!("Failed to serialize motion data from user {}: {}", user_id, e);
|
||||||
|
} else {
|
||||||
|
let data = bytes::Bytes::copy_from_slice(ser.as_slice());
|
||||||
|
for conn in self.users.read().await.values() {
|
||||||
|
if conn.user.user_id() == user_id { continue; } // fun fact: the game hard crashes if you omit this
|
||||||
|
crate::events::log_lnl_send_failure(conn.connection.sender.send_data(crate::handler::EventData {
|
||||||
|
message_ty: crate::data::MessageType::RobotMotion,
|
||||||
|
variant: 0,
|
||||||
|
data_size: data.len() as _,
|
||||||
|
data: data.clone(),
|
||||||
|
}, literustlib::packet::Property::Unreliable, &conn.connection.connection).await);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::warn!("Received machine motion with unknown player id {} from user {}", motion.player_id, user_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
super::GameMessage::NoOp => {},
|
super::GameMessage::NoOp => {},
|
||||||
@@ -654,4 +746,8 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
pub(super) fn game_done(&self) {
|
pub(super) fn game_done(&self) {
|
||||||
self.is_complete.store(true, std::sync::atomic::Ordering::SeqCst);
|
self.is_complete.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn is_game_done(&self) -> bool {
|
||||||
|
self.is_complete.load(std::sync::atomic::Ordering::SeqCst)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,21 +38,30 @@ pub enum GameMessage {
|
|||||||
remote_player: u8,
|
remote_player: u8,
|
||||||
killer_player: u8,
|
killer_player: u8,
|
||||||
},
|
},
|
||||||
|
SelfDestruct {
|
||||||
|
user_id: i32,
|
||||||
|
is_classic: bool,
|
||||||
|
},
|
||||||
|
FlippingStarted { // flip yeah!
|
||||||
|
user_id: i32,
|
||||||
|
},
|
||||||
BroadcastRlnl {
|
BroadcastRlnl {
|
||||||
user_id: i32,
|
user_id: i32,
|
||||||
event: rlnl::event_code::NetworkEvent,
|
event: rlnl::event_code::NetworkEvent,
|
||||||
|
event_in: rlnl::event_code::NetworkEvent,
|
||||||
property: literustlib::packet::Property,
|
property: literustlib::packet::Property,
|
||||||
data: Option<Box<dyn crate::Broadcastable>>,
|
data: Option<Box<dyn crate::Broadcastable>>,
|
||||||
},
|
},
|
||||||
RebroadcastRlnl {
|
RebroadcastRlnl {
|
||||||
skip_user_id: i32,
|
skip_user_id: i32,
|
||||||
event: rlnl::event_code::NetworkEvent,
|
event: rlnl::event_code::NetworkEvent,
|
||||||
|
event_in: rlnl::event_code::NetworkEvent,
|
||||||
property: literustlib::packet::Property,
|
property: literustlib::packet::Property,
|
||||||
data: Option<Box<dyn crate::Broadcastable>>,
|
data: Option<Box<dyn crate::Broadcastable>>,
|
||||||
},
|
},
|
||||||
Motion {
|
Motion {
|
||||||
user_id: i32,
|
user_id: i32,
|
||||||
data: bytes::Bytes,
|
motion: rlnl::machine_motion::MachineMotion,
|
||||||
},
|
},
|
||||||
NoOp,
|
NoOp,
|
||||||
}
|
}
|
||||||
@@ -71,6 +80,8 @@ impl GameMessage {
|
|||||||
Self::LoadComplete { user_id, .. } => *user_id,
|
Self::LoadComplete { user_id, .. } => *user_id,
|
||||||
Self::SpotVehicle { user_id, .. } => *user_id,
|
Self::SpotVehicle { user_id, .. } => *user_id,
|
||||||
Self::DestroyVehicle { user_id, .. } => *user_id,
|
Self::DestroyVehicle { user_id, .. } => *user_id,
|
||||||
|
Self::SelfDestruct { user_id, .. } => *user_id,
|
||||||
|
Self::FlippingStarted { user_id, .. } => *user_id,
|
||||||
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::Motion { user_id, .. } => *user_id,
|
Self::Motion { user_id, .. } => *user_id,
|
||||||
|
|||||||
@@ -14,4 +14,6 @@ mod countdown;
|
|||||||
|
|
||||||
pub mod modes;
|
pub mod modes;
|
||||||
|
|
||||||
|
mod timer;
|
||||||
|
|
||||||
pub const CHANNEL_BOUND: usize = 16;
|
pub const CHANNEL_BOUND: usize = 16;
|
||||||
|
|||||||
@@ -40,20 +40,41 @@ impl PlayerTracker {
|
|||||||
}
|
}
|
||||||
only_alive_team
|
only_alive_team
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn teams(&self) -> std::collections::HashSet<u8> {
|
||||||
|
self.alive.lock().await.keys().map(|x| *x).collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EliminationLogic {
|
pub struct EliminationLogic {
|
||||||
tracked: PlayerTracker
|
tracked: PlayerTracker,
|
||||||
|
game_duration: std::time::Duration,
|
||||||
|
game_end: std::sync::atomic::AtomicI64,
|
||||||
|
timer_task: tokio::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EliminationLogic {
|
impl EliminationLogic {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
let dur = std::time::Duration::from_secs(300);
|
||||||
|
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()),
|
||||||
},
|
},
|
||||||
|
game_duration: dur,
|
||||||
|
game_end: std::sync::atomic::AtomicI64::new(fake_end),
|
||||||
|
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_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@@ -64,6 +85,14 @@ impl CustomGameLogic for EliminationLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn on_player_end(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, player: &crate::matches::generic::UserConnection) -> bool {
|
async fn on_player_end(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, player: &crate::matches::generic::UserConnection) -> bool {
|
||||||
|
if generic.is_game_done() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if chrono::Utc::now().timestamp() >= self.game_end.load(std::sync::atomic::Ordering::Relaxed) {
|
||||||
|
generic.game_done();
|
||||||
|
self.abort_timer_sync().await;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
self.tracked.destroy_vehicle(&player.descriptor).await;
|
self.tracked.destroy_vehicle(&player.descriptor).await;
|
||||||
if let Some(winning_team) = self.tracked.winner_team().await {
|
if let Some(winning_team) = self.tracked.winner_team().await {
|
||||||
log::info!("Team {} has won sudden death game {} because player {} left", winning_team, generic.game_guid, player.descriptor.player_id);
|
log::info!("Team {} has won sudden death game {} because player {} left", winning_team, generic.game_guid, player.descriptor.player_id);
|
||||||
@@ -86,6 +115,7 @@ impl CustomGameLogic for EliminationLogic {
|
|||||||
).await);
|
).await);
|
||||||
}
|
}
|
||||||
generic.game_done();
|
generic.game_done();
|
||||||
|
self.abort_timer_sync().await;
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -142,6 +172,15 @@ impl CustomGameLogic for EliminationLogic {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn on_vehicle_self_destruct(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, user: u8, is_classic: bool) -> bool {
|
||||||
|
if is_classic {
|
||||||
|
self.on_vehicle_destroyed(generic, u8::MAX, user).await
|
||||||
|
} else {
|
||||||
|
log::warn!("Received non-elimination self destruct in elimination game mode");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@@ -155,8 +194,48 @@ impl CustomGameLogic for EliminationLogic {
|
|||||||
crate::matches::RlnlPacket {
|
crate::matches::RlnlPacket {
|
||||||
event: rlnl::event_code::NetworkEvent::CurrentGameTime,
|
event: rlnl::event_code::NetworkEvent::CurrentGameTime,
|
||||||
property: literustlib::packet::Property::ReliableOrdered,
|
property: literustlib::packet::Property::ReliableOrdered,
|
||||||
data: Box::new(rlnl::events::GameTime(300.0)), // FIXME use value from config
|
data: Box::new(rlnl::events::GameTime(self.game_duration.as_millis() as f32 / 1000.0)), // FIXME use value from config
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn on_countdown_start(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, game_start: chrono::DateTime<chrono::Utc>) -> bool {
|
||||||
|
let mut senders = Vec::new();
|
||||||
|
for conn in generic.users.read().await.values() {
|
||||||
|
senders.push((conn.connection.clone(), conn.state.clone()));
|
||||||
|
}
|
||||||
|
let game_end = game_start + self.game_duration;
|
||||||
|
let teams = self.tracked.teams().await;
|
||||||
|
let extra_packets = teams.iter().map(|team| crate::matches::RlnlPacket {
|
||||||
|
event: rlnl::event_code::NetworkEvent::TeamBaseInitialise,
|
||||||
|
property: literustlib::packet::Property::ReliableOrdered,
|
||||||
|
data: Box::new(rlnl::events::ingame::TeamBaseState {
|
||||||
|
base_team_or_mining_point_index: *team,
|
||||||
|
current_progress: rlnl::types::ByteFloat::from(0.0),
|
||||||
|
max_progress: rlnl::types::ByteFloat::from(4.0),
|
||||||
|
}),
|
||||||
|
}).collect();
|
||||||
|
let new_timer_task = crate::matches::timer::match_time_syncer(senders, game_start, game_end, extra_packets);
|
||||||
|
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 elimination mode suggests an assumption was wrong");
|
||||||
|
timer_t.abort();
|
||||||
|
}
|
||||||
|
*timer_lock = Some(new_timer_task);
|
||||||
|
self.game_end.store(game_end.timestamp(), std::sync::atomic::Ordering::Relaxed);
|
||||||
|
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) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,27 @@ impl CustomGameLogic for NoOpLogic {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn on_vehicle_self_destruct(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user: u8, _is_classic: bool) -> 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn on_countdown_start(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _game_start: chrono::DateTime<chrono::Utc>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_game_completed(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>) -> bool {
|
||||||
|
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) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
71
rc_multiplayer/src/matches/timer.rs
Normal file
71
rc_multiplayer/src/matches/timer.rs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
const SLEEP_PERIOD: std::time::Duration = std::time::Duration::from_millis(250);
|
||||||
|
|
||||||
|
pub fn match_time_syncer(players: Vec<(super::generic::UserSender, std::sync::Arc<super::generic::UserState>)>, game_start: chrono::DateTime<chrono::Utc>, game_end: chrono::DateTime<chrono::Utc>, extra_packets: Vec<super::RlnlPacket>) -> tokio::task::JoinHandle<()> {
|
||||||
|
tokio::spawn(do_match_timer_async(players, game_start, game_end, extra_packets))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn time_to_game_end_payload(game_end: chrono::DateTime<chrono::Utc>) -> rlnl::events::GameTime {
|
||||||
|
let now = chrono::Utc::now();
|
||||||
|
let time_until_end = game_end.signed_duration_since(now);
|
||||||
|
let time_until_end_f32 = (time_until_end.num_milliseconds().clamp(0, i64::MAX) as f32) / 1000.0;
|
||||||
|
rlnl::events::GameTime(time_until_end_f32 * 4.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_match_timer_async(players: Vec<(super::generic::UserSender, std::sync::Arc<super::generic::UserState>)>, game_start: chrono::DateTime<chrono::Utc>, game_end: chrono::DateTime<chrono::Utc>, extra_packets: Vec<super::RlnlPacket>) {
|
||||||
|
let now = chrono::Utc::now();
|
||||||
|
let time_until_start_ms = game_start.signed_duration_since(now).num_milliseconds().clamp(0, i64::MAX) + SLEEP_PERIOD.as_millis() as i64;
|
||||||
|
tokio::time::sleep(std::time::Duration::from_millis(time_until_start_ms as u64)).await;
|
||||||
|
let extras_count = extra_packets.len();
|
||||||
|
for packet in extra_packets {
|
||||||
|
for player in players.iter() {
|
||||||
|
let mode = super::generic::ConnectionMode::from_u8(player.1.mode.load(std::sync::atomic::Ordering::Relaxed));
|
||||||
|
if matches!(mode, super::generic::ConnectionMode::InGame) {
|
||||||
|
let sender = player.0.rlnl();
|
||||||
|
crate::events::log_lnl_send_failure(sender.send_data(
|
||||||
|
&*packet.data,
|
||||||
|
packet.event,
|
||||||
|
packet.property,
|
||||||
|
&player.0.connection
|
||||||
|
).await);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if extras_count != 0 {
|
||||||
|
log::info!("Broadcast {} custom packets for game start", extras_count);
|
||||||
|
}
|
||||||
|
'timer_loop: loop {
|
||||||
|
let payload = time_to_game_end_payload(game_end);
|
||||||
|
'player_loop: for player in players.iter() {
|
||||||
|
if !player.0.connection.is_connected() { continue 'player_loop; }
|
||||||
|
let sender = player.0.rlnl();
|
||||||
|
if let Err(e) = sender.send_data(
|
||||||
|
&payload,
|
||||||
|
rlnl::event_code::NetworkEvent::CurrentGameTime,
|
||||||
|
literustlib::packet::Property::Unreliable,
|
||||||
|
&player.0.connection)
|
||||||
|
.await {
|
||||||
|
log::error!("Failed to send CurrentGameTime event to a user: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if payload.0 > f32::EPSILON {
|
||||||
|
tokio::time::sleep(SLEEP_PERIOD).await;
|
||||||
|
} else {
|
||||||
|
break 'timer_loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let payload = rlnl::events::ingame::GameEnd {
|
||||||
|
reason: rlnl::types::GameEndReason::TimeOut,
|
||||||
|
};
|
||||||
|
for player in players.iter() {
|
||||||
|
let sender = player.0.rlnl();
|
||||||
|
if let Err(e) = sender.send_data(
|
||||||
|
&payload,
|
||||||
|
rlnl::event_code::NetworkEvent::EndGame,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&player.0.connection)
|
||||||
|
.await {
|
||||||
|
log::error!("Failed to send EndGame event to a user: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::debug!("Game timer (a)sync thread has completed");
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use byteserde::prelude::ByteDeserializeSlice;
|
||||||
|
|
||||||
pub struct VehicleMotionHandler {
|
pub struct VehicleMotionHandler {
|
||||||
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
||||||
}
|
}
|
||||||
@@ -18,10 +20,19 @@ impl VehicleMotionHandler {
|
|||||||
impl crate::RobotMotionHandler for VehicleMotionHandler {
|
impl crate::RobotMotionHandler for VehicleMotionHandler {
|
||||||
async fn handle(&self, data: &bytes::Bytes, user: &crate::UserData) {
|
async fn handle(&self, data: &bytes::Bytes, user: &crate::UserData) {
|
||||||
if let Some(user_info) = user.user().await {
|
if let Some(user_info) = user.user().await {
|
||||||
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::Motion {
|
let mut des = byteserde::des_slice::ByteDeserializerSlice::new(&data);
|
||||||
user_id: user_info.user_id(),
|
match rlnl::machine_motion::MachineMotion::byte_deserialize(&mut des) {
|
||||||
data: data.to_owned(),
|
Ok(motion_data) => {
|
||||||
}).await);
|
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::Motion {
|
||||||
|
user_id: user_info.user_id(),
|
||||||
|
motion: motion_data,
|
||||||
|
}).await);
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Bad deserialization for machine motion, bytes {:?}: {}", &data[..], e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log::error!("Failed to handle motion unknown user");
|
log::error!("Failed to handle motion unknown user");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ mod garage_slot_controls;
|
|||||||
mod garage_slot_set_customisations;
|
mod garage_slot_set_customisations;
|
||||||
mod garage_slot_name;
|
mod garage_slot_name;
|
||||||
mod garage_slot_copy;
|
mod garage_slot_copy;
|
||||||
|
mod steam_promo;
|
||||||
|
|
||||||
use polariton_server::operations::OperationsHandler;
|
use polariton_server::operations::OperationsHandler;
|
||||||
|
|
||||||
@@ -219,4 +220,5 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
|
|||||||
.add(garage_slot_copy::garage_slot_copy_provider())
|
.add(garage_slot_copy::garage_slot_copy_provider())
|
||||||
.add(polariton_server::operations::Ack::<12, _>::default()) // TODO handle UpdatePlayerDailyQuestProgressRequest instead of ignoring it
|
.add(polariton_server::operations::Ack::<12, _>::default()) // TODO handle UpdatePlayerDailyQuestProgressRequest instead of ignoring it
|
||||||
.add(polariton_server::operations::Ack::<90, _>::default()) // TODO handle SubmitCRFRatingRequest instead of ignoring it
|
.add(polariton_server::operations::Ack::<90, _>::default()) // TODO handle SubmitCRFRatingRequest instead of ignoring it
|
||||||
|
.add(steam_promo::steam_promos_provider())
|
||||||
}
|
}
|
||||||
|
|||||||
22
rc_services_room/src/operations/steam_promo.rs
Normal file
22
rc_services_room/src/operations/steam_promo.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
use polariton_server::operations::Immediate;
|
||||||
|
use polariton::operation::Typed;
|
||||||
|
|
||||||
|
const CODE: u8 = 52;
|
||||||
|
const PROMO_LIST_PARAM_KEY: u8 = 63; // list of str
|
||||||
|
const STEAM_PROMOS_PARAM_KEY: u8 = 121; // string (json)
|
||||||
|
const CUBES_AWARDED_PARAM_KEY: u8 = 128; // string (json)
|
||||||
|
const PASS_AWARDED_PARAM_KEY: u8 = 4; // bool
|
||||||
|
|
||||||
|
pub(super) fn steam_promos_provider() -> Immediate<CODE, crate::UserTy> {
|
||||||
|
Immediate::new(|| {
|
||||||
|
let mut params = std::collections::HashMap::with_capacity(4);
|
||||||
|
params.insert(PROMO_LIST_PARAM_KEY, Typed::Arr(polariton::operation::Arr {
|
||||||
|
ty: polariton::serdes::TypePrefix::Str,
|
||||||
|
items: Vec::default(),
|
||||||
|
}));
|
||||||
|
params.insert(STEAM_PROMOS_PARAM_KEY, Typed::Str("{}".into()));
|
||||||
|
params.insert(CUBES_AWARDED_PARAM_KEY, Typed::Str("{}".into()));
|
||||||
|
params.insert(PASS_AWARDED_PARAM_KEY, Typed::Bool(false));
|
||||||
|
params.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
13
rc_social_room/src/operations/calculate_mmr.rs
Normal file
13
rc_social_room/src/operations/calculate_mmr.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
use polariton_server::operations::Immediate;
|
||||||
|
use polariton::operation::Typed;
|
||||||
|
|
||||||
|
const CODE: u8 = 60;
|
||||||
|
const MMR_PARAM_KEY: u8 = 77; // f64
|
||||||
|
|
||||||
|
pub(super) fn mmr_provider<C: Clone + Send + Sync>() -> Immediate<CODE, crate::UserTy, C> {
|
||||||
|
Immediate::new(|| {
|
||||||
|
let mut params = std::collections::HashMap::with_capacity(1);
|
||||||
|
params.insert(MMR_PARAM_KEY, Typed::Double(1.0));
|
||||||
|
params.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ mod search_clan;
|
|||||||
mod season_rewards;
|
mod season_rewards;
|
||||||
mod previous_battle_rewards;
|
mod previous_battle_rewards;
|
||||||
mod platoon_data;
|
mod platoon_data;
|
||||||
|
mod calculate_mmr;
|
||||||
|
|
||||||
use polariton_server::operations::OperationsHandler;
|
use polariton_server::operations::OperationsHandler;
|
||||||
|
|
||||||
@@ -27,4 +28,5 @@ pub fn handler() -> OperationsHandler<crate::UserTy, crate::data::custom::Custom
|
|||||||
.add(previous_battle_rewards::pending_battle_rewards_provider())
|
.add(previous_battle_rewards::pending_battle_rewards_provider())
|
||||||
.add(platoon_data::platoon_provider())
|
.add(platoon_data::platoon_provider())
|
||||||
.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())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user