mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add basic stats tracking #30
This commit is contained in:
31
rc_multiplayer/src/events/assist_bonus.rs
Normal file
31
rc_multiplayer/src/events/assist_bonus.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
pub struct AssistBonus {
|
||||
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
||||
}
|
||||
|
||||
pub(super) fn handler(init_ctx: &crate::InitConfig) -> crate::handlers::SimpleRlnl<rlnl::events::ingame::AssistBonus, AssistBonus> {
|
||||
crate::handlers::SimpleRlnl::new(AssistBonus::new(init_ctx))
|
||||
}
|
||||
|
||||
impl AssistBonus {
|
||||
fn new(init_ctx: &crate::InitConfig) -> Self {
|
||||
Self {
|
||||
msg_router: init_ctx.matches_chann.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl crate::handlers::RlnlEventCodeHandler for AssistBonus {
|
||||
type In = rlnl::events::ingame::AssistBonus;
|
||||
const CODE: rlnl::event_code::NetworkEvent = rlnl::event_code::NetworkEvent::AssistBonusRequest;
|
||||
|
||||
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 {
|
||||
super::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::AssistBonus {
|
||||
user_id: user_info.user_id(),
|
||||
shootee: data.requester_player_id as u8,
|
||||
shooters: data.player_ids.into_iter().map(|x| x.player).collect(),
|
||||
}).await);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
rc_multiplayer/src/events/damage_bonus.rs
Normal file
30
rc_multiplayer/src/events/damage_bonus.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
pub struct DamageBonus {
|
||||
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
||||
}
|
||||
|
||||
pub(super) fn handler(init_ctx: &crate::InitConfig) -> crate::handlers::SimpleRlnl<rlnl::events::ingame::DestroyedHealedCubesBonus, DamageBonus> {
|
||||
crate::handlers::SimpleRlnl::new(DamageBonus::new(init_ctx))
|
||||
}
|
||||
|
||||
impl DamageBonus {
|
||||
fn new(init_ctx: &crate::InitConfig) -> Self {
|
||||
Self {
|
||||
msg_router: init_ctx.matches_chann.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl crate::handlers::RlnlEventCodeHandler for DamageBonus {
|
||||
type In = rlnl::events::ingame::DestroyedHealedCubesBonus;
|
||||
const CODE: rlnl::event_code::NetworkEvent = rlnl::event_code::NetworkEvent::DestroyCubesBonusRequest;
|
||||
|
||||
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 {
|
||||
super::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::DestroyCubesBonus {
|
||||
user_id: user_info.user_id(),
|
||||
info: data,
|
||||
}).await);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
rc_multiplayer/src/events/heal_bonus.rs
Normal file
30
rc_multiplayer/src/events/heal_bonus.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
pub struct HealBonus {
|
||||
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
||||
}
|
||||
|
||||
pub(super) fn handler(init_ctx: &crate::InitConfig) -> crate::handlers::SimpleRlnl<rlnl::events::ingame::DestroyedHealedCubesBonus, HealBonus> {
|
||||
crate::handlers::SimpleRlnl::new(HealBonus::new(init_ctx))
|
||||
}
|
||||
|
||||
impl HealBonus {
|
||||
fn new(init_ctx: &crate::InitConfig) -> Self {
|
||||
Self {
|
||||
msg_router: init_ctx.matches_chann.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl crate::handlers::RlnlEventCodeHandler for HealBonus {
|
||||
type In = rlnl::events::ingame::DestroyedHealedCubesBonus;
|
||||
const CODE: rlnl::event_code::NetworkEvent = rlnl::event_code::NetworkEvent::HealCubesBonusRequest;
|
||||
|
||||
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 {
|
||||
super::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::DestroyCubesBonus {
|
||||
user_id: user_info.user_id(),
|
||||
info: data,
|
||||
}).await);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
rc_multiplayer/src/events/kill_bonus.rs
Normal file
31
rc_multiplayer/src/events/kill_bonus.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
pub struct KillEnemyBonus {
|
||||
msg_router: tokio::sync::mpsc::Sender<crate::matches::GameMessage>,
|
||||
}
|
||||
|
||||
pub(super) fn handler(init_ctx: &crate::InitConfig) -> crate::handlers::SimpleRlnl<rlnl::events::ingame::Kill, KillEnemyBonus> {
|
||||
crate::handlers::SimpleRlnl::new(KillEnemyBonus::new(init_ctx))
|
||||
}
|
||||
|
||||
impl KillEnemyBonus {
|
||||
fn new(init_ctx: &crate::InitConfig) -> Self {
|
||||
Self {
|
||||
msg_router: init_ctx.matches_chann.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl crate::handlers::RlnlEventCodeHandler for KillEnemyBonus {
|
||||
type In = rlnl::events::ingame::Kill;
|
||||
const CODE: rlnl::event_code::NetworkEvent = rlnl::event_code::NetworkEvent::KillBonusRequest;
|
||||
|
||||
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 {
|
||||
super::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::KillBonus {
|
||||
user_id: user_info.user_id(),
|
||||
shootee: data.killee_player_id,
|
||||
shooter: data.killer_player_id,
|
||||
}).await);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,10 @@ mod client_unregister;
|
||||
mod flipper_start;
|
||||
mod self_destruct_elimination;
|
||||
mod map_ping;
|
||||
mod kill_bonus;
|
||||
mod assist_bonus;
|
||||
mod damage_bonus;
|
||||
mod heal_bonus;
|
||||
|
||||
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))
|
||||
@@ -79,10 +83,8 @@ pub async fn handler(init_ctx: &crate::InitConfig) -> crate::handler::LnlEventHa
|
||||
{literustlib::packet::Property::Unreliable as u8},
|
||||
rlnl::events::ingame::DestroyCubeEffectOnly,
|
||||
>::handler(init_ctx))
|
||||
.add(crate::handlers::Stub::<
|
||||
{rlnl::event_code::NetworkEvent::DestroyCubesBonusRequest as i16},
|
||||
rlnl::events::ingame::DestroyedHealedCubesBonus,
|
||||
>::handler(init_ctx))
|
||||
.add(damage_bonus::handler(init_ctx))
|
||||
.add(heal_bonus::handler(init_ctx))
|
||||
.add(crate::handlers::Broadcaster::<
|
||||
false,
|
||||
{rlnl::event_code::NetworkEvent::HealSelf as i16},
|
||||
@@ -90,10 +92,8 @@ pub async fn handler(init_ctx: &crate::InitConfig) -> crate::handler::LnlEventHa
|
||||
{literustlib::packet::Property::ReliableOrdered as u8},
|
||||
rlnl::events::HealedCubes,
|
||||
>::handler(init_ctx))
|
||||
.add(crate::handlers::Stub::<
|
||||
{rlnl::event_code::NetworkEvent::KillBonusRequest as i16},
|
||||
rlnl::events::ingame::Kill, // FIXME this is not a player ID -- it's actually sending 2 bytes not 1
|
||||
>::handler(init_ctx))
|
||||
.add(kill_bonus::handler(init_ctx))
|
||||
.add(assist_bonus::handler(init_ctx))
|
||||
.add(kill_player::handler(init_ctx))
|
||||
.add(crate::handlers::Broadcaster::<
|
||||
true,
|
||||
|
||||
@@ -12,4 +12,5 @@ mod ingame_broadcast_dataless;
|
||||
pub use ingame_broadcast_dataless::DatalessBroadcaster;
|
||||
|
||||
mod stub;
|
||||
#[allow(unused_imports)]
|
||||
pub use stub::Stub;
|
||||
|
||||
@@ -3,6 +3,7 @@ pub struct Stub<const CODE_IN: i16, In: byteserde::des_slice::ByteDeserializeSli
|
||||
}
|
||||
|
||||
impl <const CODE_IN: i16, In: byteserde::des_slice::ByteDeserializeSlice<In> + Send + Sync + 'static> Stub<CODE_IN, In> {
|
||||
#[allow(dead_code)]
|
||||
pub fn handler(init_ctx: &crate::InitConfig) -> crate::handlers::simple_typed::SimpleRlnl<In, Self> {
|
||||
crate::handlers::simple_typed::SimpleRlnl::new(Stub::new(init_ctx))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ pub(super) struct UserConnection {
|
||||
pub(super) state: std::sync::Arc<UserState>,
|
||||
pub(super) machine: MachineState,
|
||||
pub(super) descriptor: oj_rc_core::persist::user::PlayerDescriptor,
|
||||
pub(super) counters: UserData,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -76,6 +77,57 @@ impl Location {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct UserData {
|
||||
pub kills: std::sync::atomic::AtomicU32,
|
||||
pub deaths: std::sync::atomic::AtomicU32,
|
||||
pub assists: std::sync::atomic::AtomicU32,
|
||||
pub healed: std::sync::atomic::AtomicU32,
|
||||
pub received_healed: std::sync::atomic::AtomicU32,
|
||||
pub cubes: std::sync::atomic::AtomicU32,
|
||||
pub received_cubes: std::sync::atomic::AtomicU32, // damage taken
|
||||
}
|
||||
|
||||
impl UserData {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
kills: std::sync::atomic::AtomicU32::new(0),
|
||||
deaths: std::sync::atomic::AtomicU32::new(0),
|
||||
assists: std::sync::atomic::AtomicU32::new(0),
|
||||
healed: std::sync::atomic::AtomicU32::new(0),
|
||||
received_healed: std::sync::atomic::AtomicU32::new(0),
|
||||
cubes: std::sync::atomic::AtomicU32::new(0),
|
||||
received_cubes: std::sync::atomic::AtomicU32::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn generic_score(&self) -> u32 {
|
||||
self.kills.load(std::sync::atomic::Ordering::Relaxed) * 1_000
|
||||
+ self.assists.load(std::sync::atomic::Ordering::Relaxed) * 100
|
||||
+ self.healed.load(std::sync::atomic::Ordering::Relaxed)
|
||||
+ self.cubes.load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub(super) fn get_generic_packet(&self, player_id: u8, stat: rlnl::types::IngameStatId, delta: Option<u32>) -> rlnl::events::ingame::UpdateGameStats {
|
||||
let (stat_amount, backup_delta) = match stat {
|
||||
rlnl::types::IngameStatId::DestroyedCubes
|
||||
| rlnl::types::IngameStatId::DestroyedCubesInProtection
|
||||
| rlnl::types::IngameStatId::DestroyedCubesDefendingTheBase => (self.cubes.load(std::sync::atomic::Ordering::SeqCst), 1),
|
||||
rlnl::types::IngameStatId::Kill => (self.kills.load(std::sync::atomic::Ordering::Relaxed), 1_000),
|
||||
rlnl::types::IngameStatId::KillAssist => (self.assists.load(std::sync::atomic::Ordering::Relaxed), 100),
|
||||
rlnl::types::IngameStatId::HealCubes => (self.assists.load(std::sync::atomic::Ordering::SeqCst), 1),
|
||||
rlnl::types::IngameStatId::RobotDestroyed => (self.deaths.load(std::sync::atomic::Ordering::Relaxed), 0),
|
||||
s => panic!("Cannot generate game stat {:?}", s)
|
||||
};
|
||||
rlnl::events::ingame::UpdateGameStats {
|
||||
player_id,
|
||||
stat_id: stat,
|
||||
amount: stat_amount,
|
||||
score: self.generic_score(),
|
||||
delta_score: delta.unwrap_or(backup_delta),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(super) enum ConnectionMode {
|
||||
@@ -252,6 +304,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
state: std::sync::Arc::new(UserState::new()),
|
||||
machine: MachineState::new(),
|
||||
descriptor: player_info.to_owned(),
|
||||
counters: UserData::new(),
|
||||
};
|
||||
if self.custom_logic_handler.on_player_join(&self, &new_user, &self.players_info).await {
|
||||
self.spawn_send_loading_events(&new_user, id, self.players_info.clone());
|
||||
@@ -489,7 +542,19 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
true,
|
||||
).await;
|
||||
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;
|
||||
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
|
||||
if let Some(killed) = self.users.read().await.get(&remote_player) {
|
||||
killed.counters.deaths.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
let data = killed.counters.get_generic_packet(remote_player, rlnl::types::IngameStatId::RobotDestroyed, None);
|
||||
self.broadcast(
|
||||
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&data,
|
||||
true,
|
||||
).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
super::GameMessage::SelfDestruct { user_id, is_classic } => {
|
||||
if let Some(player_id) = self.user_key_by_user_id(user_id).await {
|
||||
@@ -520,6 +585,16 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
conn.connection.connection.disconnect();
|
||||
}
|
||||
}
|
||||
if let Some(killed) = self.users.read().await.get(&player_id) {
|
||||
killed.counters.deaths.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
let data = killed.counters.get_generic_packet(player_id, rlnl::types::IngameStatId::RobotDestroyed, None);
|
||||
self.broadcast(
|
||||
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&data,
|
||||
true,
|
||||
).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,7 +621,106 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
).await);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
super::GameMessage::KillBonus { user_id: _, shootee, shooter } => {
|
||||
if let Some(to_reward) = self.users.read().await.get(&shooter) {
|
||||
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: shootee,
|
||||
killer_player_id: shooter,
|
||||
},
|
||||
rlnl::event_code::NetworkEvent::ConfirmedKill,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&to_reward.connection.connection
|
||||
).await);
|
||||
let data = to_reward.counters.get_generic_packet(shooter, rlnl::types::IngameStatId::Kill, None);
|
||||
self.broadcast(
|
||||
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&data,
|
||||
true,
|
||||
).await;
|
||||
}
|
||||
},
|
||||
super::GameMessage::AssistBonus { user_id: _, shootee, shooters } => {
|
||||
let lock = self.users.read().await;
|
||||
for shooter in shooters {
|
||||
if let Some(to_reward) = lock.get(&shooter) {
|
||||
to_reward.counters.assists.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: shootee,
|
||||
killer_player_id: shooter,
|
||||
},
|
||||
rlnl::event_code::NetworkEvent::ConfirmedAssist,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&to_reward.connection.connection
|
||||
).await);
|
||||
let data = to_reward.counters.get_generic_packet(shooter, rlnl::types::IngameStatId::KillAssist, None);
|
||||
self.broadcast(
|
||||
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&data,
|
||||
true,
|
||||
).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
super::GameMessage::DestroyCubesBonus { user_id: _, info } => {
|
||||
let lock = self.users.read().await;
|
||||
for shooter in info.shooters {
|
||||
if let Some(to_reward) = lock.get(&shooter.shooting_player_id) {
|
||||
let mut total_cubes = 0;
|
||||
for target in shooter.shooter_targets {
|
||||
if let Some(to_punish) = lock.get(&target.target_player_id) {
|
||||
let mut total_cubes_received = 0;
|
||||
for cubes in target.cube_amounts {
|
||||
// TODO use cube_id for something!?
|
||||
total_cubes += cubes.cube_count;
|
||||
total_cubes_received += cubes.cube_count;
|
||||
}
|
||||
to_punish.counters.received_cubes.fetch_add(total_cubes_received, std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
to_reward.counters.cubes.fetch_add(total_cubes, std::sync::atomic::Ordering::SeqCst);
|
||||
let data = to_reward.counters.get_generic_packet(shooter.shooting_player_id, rlnl::types::IngameStatId::DestroyedCubes, Some(total_cubes));
|
||||
self.broadcast(
|
||||
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
||||
literustlib::packet::Property::Unreliable,
|
||||
&data,
|
||||
true,
|
||||
).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
super::GameMessage::HealCubesBonus { user_id: _, info } => {
|
||||
let lock = self.users.read().await;
|
||||
for shooter in info.shooters {
|
||||
if let Some(to_reward) = lock.get(&shooter.shooting_player_id) {
|
||||
let mut total_cubes = 0;
|
||||
for target in shooter.shooter_targets {
|
||||
if let Some(to_punish) = lock.get(&target.target_player_id) {
|
||||
let mut total_cubes_received = 0;
|
||||
for cubes in target.cube_amounts {
|
||||
// TODO use cube_id for something!?
|
||||
total_cubes += cubes.cube_count;
|
||||
total_cubes_received += cubes.cube_count;
|
||||
}
|
||||
to_punish.counters.received_healed.fetch_add(total_cubes_received, std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
to_reward.counters.healed.fetch_add(total_cubes, std::sync::atomic::Ordering::SeqCst);
|
||||
let data = to_reward.counters.get_generic_packet(shooter.shooting_player_id, rlnl::types::IngameStatId::HealCubes, Some(total_cubes));
|
||||
self.broadcast(
|
||||
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
||||
literustlib::packet::Property::Unreliable,
|
||||
&data,
|
||||
true,
|
||||
).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 {
|
||||
|
||||
@@ -49,6 +49,24 @@ pub enum GameMessage {
|
||||
user_id: i32,
|
||||
ping: rlnl::events::ingame::MapPing,
|
||||
},
|
||||
KillBonus {
|
||||
user_id: i32,
|
||||
shootee: u8,
|
||||
shooter: u8,
|
||||
},
|
||||
AssistBonus {
|
||||
user_id: i32,
|
||||
shootee: u8,
|
||||
shooters: Vec<u8>,
|
||||
},
|
||||
DestroyCubesBonus {
|
||||
user_id: i32,
|
||||
info: rlnl::events::ingame::DestroyedHealedCubesBonus,
|
||||
},
|
||||
HealCubesBonus {
|
||||
user_id: i32,
|
||||
info: rlnl::events::ingame::DestroyedHealedCubesBonus,
|
||||
},
|
||||
BroadcastRlnl {
|
||||
user_id: i32,
|
||||
event: rlnl::event_code::NetworkEvent,
|
||||
@@ -87,6 +105,10 @@ impl GameMessage {
|
||||
Self::SelfDestruct { user_id, .. } => *user_id,
|
||||
Self::FlippingStarted { user_id, .. } => *user_id,
|
||||
Self::MapPing { user_id, .. } => *user_id,
|
||||
Self::KillBonus { user_id, .. } => *user_id,
|
||||
Self::AssistBonus { user_id, .. } => *user_id,
|
||||
Self::DestroyCubesBonus { user_id, .. } => *user_id,
|
||||
Self::HealCubesBonus { user_id, .. } => *user_id,
|
||||
Self::BroadcastRlnl { user_id, .. } => *user_id,
|
||||
Self::RebroadcastRlnl { skip_user_id, .. } => *skip_user_id,
|
||||
Self::Motion { user_id, .. } => *user_id,
|
||||
|
||||
Reference in New Issue
Block a user