mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Refactor multiplayer message handling into smaller functions
This commit is contained in:
@@ -323,13 +323,95 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
if let Some(msg) = recv.recv().await {
|
||||
match msg {
|
||||
super::GameMessage::NewConnection { user, game_guid, connection, response, sender } => {
|
||||
self.on_new_connection(user, game_guid, connection, response, sender).await;
|
||||
},
|
||||
super::GameMessage::EndConnection { user_id } => {
|
||||
is_engaged = self.on_end_connection(user_id).await;
|
||||
},
|
||||
super::GameMessage::RequestLeave { user_id } => {
|
||||
self.on_request_leave(user_id).await;
|
||||
}
|
||||
super::GameMessage::LoadingProgress { user_id, user_name, progress } => {
|
||||
self.on_loading_progress(user_id, user_name, progress).await;
|
||||
}
|
||||
super::GameMessage::RequestLoadingProgress { user_id } => {
|
||||
self.on_request_loading_progress(user_id).await;
|
||||
},
|
||||
super::GameMessage::WeaponSelect { user_id, machine_id, category, size } => {
|
||||
self.on_weapon_select(user_id, machine_id, category, size).await;
|
||||
},
|
||||
super::GameMessage::RequestLoadingSync { user_id } => {
|
||||
self.on_request_loading_sync(user_id).await;
|
||||
},
|
||||
super::GameMessage::LoadComplete { user_id } => {
|
||||
self.on_load_complete(user_id).await;
|
||||
},
|
||||
super::GameMessage::SpotVehicle { user_id, remote_player } => {
|
||||
self.rebroadcast(
|
||||
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 } => {
|
||||
self.on_destroy_vehicle(user_id, remote_player, killer_player).await;
|
||||
},
|
||||
super::GameMessage::SelfDestruct { user_id, is_classic } => {
|
||||
self.on_self_destruct(user_id, is_classic).await;
|
||||
},
|
||||
super::GameMessage::FlippingStarted { user_id } => {
|
||||
self.on_flipping_started(user_id).await;
|
||||
},
|
||||
super::GameMessage::MapPing { user_id, ping } => {
|
||||
self.on_map_ping(user_id, ping).await;
|
||||
},
|
||||
super::GameMessage::KillBonus { user_id, shootee, shooter } => {
|
||||
self.on_kill_bonus(user_id, shootee, shooter).await;
|
||||
},
|
||||
super::GameMessage::AssistBonus { user_id, shootee, shooters } => {
|
||||
self.on_assist_bonus(user_id, shootee, shooters).await;
|
||||
},
|
||||
super::GameMessage::DestroyCubesBonus { user_id, info } => {
|
||||
self.on_destroy_cubes_bonus(user_id, info).await;
|
||||
},
|
||||
super::GameMessage::HealCubesBonus { user_id, info } => {
|
||||
self.on_heal_cubes_bonus(user_id, info).await;
|
||||
},
|
||||
super::GameMessage::BroadcastRlnl { user_id, event, event_in, property, data } => {
|
||||
self.on_broadcast(user_id, event, event_in, property, data, false).await;
|
||||
},
|
||||
super::GameMessage::RebroadcastRlnl { skip_user_id, event, event_in, property, data } => {
|
||||
self.on_broadcast(skip_user_id, event, event_in, property, data, true).await;
|
||||
},
|
||||
super::GameMessage::CustomLogicRlnl { user_id, event, property, data } => {
|
||||
self.custom_logic_handler.on_custom(&self, user_id, event, property, data).await;
|
||||
},
|
||||
super::GameMessage::Motion { user_id, motion } => {
|
||||
self.on_motion(user_id, motion).await;
|
||||
},
|
||||
super::GameMessage::NoOp => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
self.fakes_handler.stop();
|
||||
log::info!("Game {} has exited", self.game_guid());
|
||||
}
|
||||
|
||||
async fn on_new_connection(&self,
|
||||
user: std::sync::Arc<Box<dyn oj_rc_core::persist::user::MultiplayerUser + Send + Sync + 'static>>,
|
||||
game_guid: String,
|
||||
connection: std::sync::Arc<literustlib_server::Connection<crate::PacketData>>,
|
||||
response: tokio::sync::oneshot::Sender<Option<super::messages::ErrorMessage>>,
|
||||
sender: std::sync::Arc<literustlib_server::DataSender<crate::PacketData>>
|
||||
) {
|
||||
if self.game_guid() != game_guid {
|
||||
log::error!("Game guid does not match (got: {}, expected: {})", game_guid, self.game_guid());
|
||||
response.send(Some(super::messages::ErrorMessage {
|
||||
message: format!("Game guid does not match (got: {}, expected: {})", game_guid, self.game_guid()),
|
||||
inner: None,
|
||||
})).unwrap_or_default();
|
||||
return;
|
||||
} else {
|
||||
let mut users = self.users.write().await;
|
||||
//tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
@@ -348,7 +430,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
descriptor: player_info.to_owned(),
|
||||
counters: UserData::new(),
|
||||
};
|
||||
if self.custom_logic_handler.on_player_join(&self, &new_user, &self.players_info).await {
|
||||
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());
|
||||
crate::events::log_lnl_send_failure(new_user.connection.rlnl().send_data(
|
||||
&rlnl::events::ingame::PlayerId { player: id },
|
||||
@@ -362,12 +444,13 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
}
|
||||
response.send(None).unwrap_or_default();
|
||||
}
|
||||
},
|
||||
super::GameMessage::EndConnection { user_id } => {
|
||||
}
|
||||
|
||||
async fn on_end_connection(&self, user_id: i32) -> bool {
|
||||
if let Some(player_id) = self.user_key_by_user_id(user_id).await {
|
||||
let conn_opt = self.users.write().await.remove(&player_id);
|
||||
if let Some(conn) = conn_opt {
|
||||
if self.custom_logic_handler.on_player_end(&self, &conn).await {
|
||||
if self.custom_logic_handler.on_player_end(self, &conn).await {
|
||||
conn.state.mode.store(ConnectionMode::Disconnected.to_u8(), std::sync::atomic::Ordering::Relaxed);
|
||||
if !self.is_complete.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
self.rebroadcast(
|
||||
@@ -392,22 +475,24 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
let mode = ConnectionMode::from_u8(user.state.mode.load(std::sync::atomic::Ordering::Relaxed));
|
||||
has_active_connections |= !matches!(mode, ConnectionMode::Disconnected);
|
||||
}
|
||||
is_engaged = has_active_connections;
|
||||
//is_engaged = has_active_connections;
|
||||
if !has_active_connections {
|
||||
self.is_complete.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
if self.custom_logic_handler.on_game_completed(&self).await {
|
||||
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;
|
||||
return has_active_connections;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
super::GameMessage::RequestLeave { user_id } => {
|
||||
async fn on_request_leave(&self, user_id: i32) {
|
||||
log::info!("User {} wants to leave game {}", user_id, self.game_guid());
|
||||
if let Some(player_id) = self.user_key_by_user_id(user_id).await {
|
||||
self.rebroadcast(
|
||||
@@ -429,7 +514,8 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
}
|
||||
}
|
||||
}
|
||||
super::GameMessage::LoadingProgress { user_id, user_name, progress } => {
|
||||
|
||||
async fn on_loading_progress(&self, user_id: i32, user_name: String, progress: f32) {
|
||||
let progress_data = rlnl::events::loading::LoadingProgress {
|
||||
user_name: rlnl::types::BinaryWriterString(user_name),
|
||||
progress,
|
||||
@@ -458,7 +544,8 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
}
|
||||
}
|
||||
}
|
||||
super::GameMessage::RequestLoadingProgress { user_id } => {
|
||||
|
||||
async fn on_request_loading_progress(&self, user_id: i32) {
|
||||
log::info!("Got request loading progress");
|
||||
if let Some(user_key) = self.user_key_by_user_id(user_id).await {
|
||||
if let Some(user_info) = self.users.read().await.get(&user_key) {
|
||||
@@ -501,9 +588,14 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
} else {
|
||||
log::error!("Failed to find user {} in connected users for match {}", user_id, self.game_guid());
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
super::GameMessage::WeaponSelect { user_id, machine_id, category, size } => {
|
||||
async fn on_weapon_select(&self,
|
||||
user_id: i32,
|
||||
machine_id: u8,
|
||||
category: oj_rc_core::data::weapon_list::ItemCategory,
|
||||
size: oj_rc_core::data::cube_list::ItemTier
|
||||
) {
|
||||
if let Some(conn) = self.users.read().await.get(&machine_id) {
|
||||
let category_u32 = category as u32;
|
||||
let size_u32 = size as u32;
|
||||
@@ -522,8 +614,9 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
true
|
||||
).await;
|
||||
}
|
||||
},
|
||||
super::GameMessage::RequestLoadingSync { user_id } => {
|
||||
}
|
||||
|
||||
async fn on_request_loading_sync(&self, user_id: i32) {
|
||||
// wait for all users to be ready before transitioning to loading sync
|
||||
let mut ready_count = 0;
|
||||
for user in self.users.read().await.values() {
|
||||
@@ -543,12 +636,13 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
if ready_count == player_count {
|
||||
log::info!("All players ({}) awaiting sync for game {}", player_count, self.game_guid());
|
||||
for (user_key, conn) in self.users.read().await.iter() {
|
||||
let extra_packets = self.custom_logic_handler.extra_sync_events(&self, conn).await;
|
||||
let extra_packets = self.custom_logic_handler.extra_sync_events(self, conn).await;
|
||||
self.spawn_send_sync_events(conn, conn.user.user_id(), *user_key, self.players_info.clone(), extra_packets, self.map_config.clone());
|
||||
}
|
||||
}
|
||||
},
|
||||
super::GameMessage::LoadComplete { user_id } => {
|
||||
}
|
||||
|
||||
async fn on_load_complete(&self, user_id: i32) {
|
||||
if let Some(user_key) = self.user_key_by_user_id(user_id).await {
|
||||
if let Some(conn) = self.users.read().await.get(&user_key) {
|
||||
log::info!("Loading complete for game {}, user {} (player {})", self.game_guid(), user_id, user_key);
|
||||
@@ -561,11 +655,11 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
self.spawn_initial_ingame_events(conn, user_id);
|
||||
} else {
|
||||
log::warn!("Invalid LoadComplete user key {} for game {}", user_key, self.game_guid());
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log::warn!("Unknown LoadComplete user id {} for game {}", user_id, self.game_guid());
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
// wait for all users to be ready for starting game start countdown
|
||||
let mut all_users_loading_complete = true;
|
||||
@@ -584,7 +678,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
.collect()
|
||||
);
|
||||
let game_start = chrono::Utc::now() + Self::COUNTDOWN_DURATION;
|
||||
if self.custom_logic_handler.on_countdown_start(&self, game_start).await {
|
||||
if self.custom_logic_handler.on_countdown_start(self, game_start).await {
|
||||
let mut senders = Vec::new();
|
||||
for conn in self.users.read().await.values() {
|
||||
senders.push((conn.connection.clone(), conn.state.clone()));
|
||||
@@ -593,17 +687,13 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
super::countdown::match_countdown(senders, game_start);
|
||||
}
|
||||
}
|
||||
},
|
||||
super::GameMessage::SpotVehicle { user_id, remote_player } => {
|
||||
self.rebroadcast(
|
||||
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 } => {
|
||||
}
|
||||
|
||||
async fn on_destroy_vehicle(&self,
|
||||
user_id: i32,
|
||||
remote_player: u8,
|
||||
killer_player: u8,
|
||||
) {
|
||||
// FIXME allow custom_logic_handler to override the MachineDestroyedConfirmed send
|
||||
self.rebroadcast(
|
||||
user_id,
|
||||
@@ -613,7 +703,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
true,
|
||||
).await;
|
||||
log::info!("Player {} was destroyed by player {} (user {}) in game {}", remote_player, killer_player, user_id, self.game_guid());
|
||||
if self.custom_logic_handler.on_vehicle_destroyed(&self, killer_player, remote_player).await {
|
||||
if self.custom_logic_handler.on_vehicle_destroyed(self, killer_player, remote_player).await {
|
||||
// the kill tracking is initiated separately by the client with kill bonus event
|
||||
if let Some(killed) = self.users.read().await.get(&remote_player) {
|
||||
killed.counters.deaths.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
@@ -626,8 +716,9 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
super::GameMessage::SelfDestruct { user_id, is_classic } => {
|
||||
}
|
||||
|
||||
async fn on_self_destruct(&self, user_id: i32, is_classic: bool) {
|
||||
if let Some(player_id) = self.user_key_by_user_id(user_id).await {
|
||||
self.rebroadcast(
|
||||
user_id,
|
||||
@@ -637,7 +728,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
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 self.custom_logic_handler.on_vehicle_self_destruct(self, player_id, is_classic).await {
|
||||
if is_classic {
|
||||
self.rebroadcast(
|
||||
user_id,
|
||||
@@ -658,9 +749,9 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
super::GameMessage::FlippingStarted { user_id } => {
|
||||
async fn on_flipping_started(&self, user_id: i32) {
|
||||
if let Some(user_key) = self.user_key_by_user_id(user_id).await {
|
||||
self.rebroadcast(
|
||||
user_id,
|
||||
@@ -670,8 +761,9 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
true,
|
||||
).await;
|
||||
}
|
||||
},
|
||||
super::GameMessage::MapPing { user_id: _, ping } => {
|
||||
}
|
||||
|
||||
async fn on_map_ping(&self, _user_id: i32, ping: rlnl::events::ingame::MapPing) {
|
||||
for (id, conn) in self.users.read().await.iter() {
|
||||
if (*id as i32) != ping.sender && conn.descriptor.team == ping.team_id {
|
||||
crate::events::log_lnl_send_failure(conn.connection.rlnl().send_data(
|
||||
@@ -682,8 +774,13 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
).await);
|
||||
}
|
||||
}
|
||||
},
|
||||
super::GameMessage::KillBonus { user_id: _, shootee, shooter } => {
|
||||
}
|
||||
|
||||
async fn on_kill_bonus(&self,
|
||||
_user_id: i32,
|
||||
shootee: u8,
|
||||
shooter: u8,
|
||||
) {
|
||||
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(
|
||||
@@ -703,8 +800,13 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
true,
|
||||
).await;
|
||||
}
|
||||
},
|
||||
super::GameMessage::AssistBonus { user_id: _, shootee, shooters } => {
|
||||
}
|
||||
|
||||
async fn on_assist_bonus(&self,
|
||||
_user_id: i32,
|
||||
shootee: u8,
|
||||
shooters: Vec<u8>,
|
||||
) {
|
||||
let lock = self.users.read().await;
|
||||
for shooter in shooters {
|
||||
if let Some(to_reward) = lock.get(&shooter) {
|
||||
@@ -727,8 +829,12 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
super::GameMessage::DestroyCubesBonus { user_id: _, info } => {
|
||||
}
|
||||
|
||||
async fn on_destroy_cubes_bonus(&self,
|
||||
_user_id: i32,
|
||||
info: rlnl::events::ingame::DestroyedHealedCubesBonus
|
||||
) {
|
||||
let lock = self.users.read().await;
|
||||
for shooter in info.shooters {
|
||||
if let Some(to_reward) = lock.get(&shooter.shooting_player_id) {
|
||||
@@ -754,8 +860,12 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
super::GameMessage::HealCubesBonus { user_id: _, info } => {
|
||||
}
|
||||
|
||||
async fn on_heal_cubes_bonus(&self,
|
||||
_user_id: i32,
|
||||
info: rlnl::events::ingame::DestroyedHealedCubesBonus,
|
||||
) {
|
||||
let lock = self.users.read().await;
|
||||
for shooter in info.shooters {
|
||||
if let Some(to_reward) = lock.get(&shooter.shooting_player_id) {
|
||||
@@ -781,29 +891,35 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
).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 {
|
||||
}
|
||||
|
||||
async fn on_broadcast(&self,
|
||||
user_id: i32,
|
||||
event: rlnl::event_code::NetworkEvent,
|
||||
event_in: rlnl::event_code::NetworkEvent,
|
||||
property: literustlib::packet::Property,
|
||||
data: Option<Box<dyn crate::Broadcastable>>,
|
||||
skip_user: bool,
|
||||
) {
|
||||
if self.custom_logic_handler.on_broadcast(self, user_id, event, event_in, property, &data, skip_user).await {
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
if let Some(data) = data {
|
||||
if skip_user {
|
||||
self.rebroadcast(user_id, event, property, &*data, true).await;
|
||||
} else {
|
||||
self.broadcast(event, property, &*data, true).await;
|
||||
}
|
||||
} else {
|
||||
if skip_user {
|
||||
self.rebroadcast_dataless(user_id, event, property, 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::CustomLogicRlnl { user_id, event, property, data } => {
|
||||
self.custom_logic_handler.on_custom(&self, user_id, event, property, data).await;
|
||||
},
|
||||
super::GameMessage::Motion { user_id, motion } => {
|
||||
|
||||
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();
|
||||
//log::info!("Player {} looking at ({}, {}, {})", motion.player_id, looking_at_x, looking_at_y, looking_at_z);
|
||||
let (x, y, z) = motion.rb_state.rb_pos_rot.pos.into();
|
||||
@@ -814,7 +930,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
let coords = unit_quat.rotate_vector([x2, y2, z2]);
|
||||
let (x4, y4, z4) = (x + coords[0], y + coords[1], z + coords[2]);
|
||||
//log::debug!("Player {} world CoM is at (x, y, z) ({}, {}, {})", motion.player_id, x4, y4, z4);
|
||||
if self.custom_logic_handler.on_motion(&self, &motion, (x4, y4, z4)).await {
|
||||
if self.custom_logic_handler.on_motion(self, &motion, (x4, y4, z4)).await {
|
||||
if let Some(conn) = self.users.read().await.get(&motion.player_id) {
|
||||
conn.machine.location.x.store(x4, std::sync::atomic::Ordering::Relaxed);
|
||||
conn.machine.location.y.store(y4, std::sync::atomic::Ordering::Relaxed);
|
||||
@@ -840,13 +956,6 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
super::GameMessage::NoOp => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
self.fakes_handler.stop();
|
||||
log::info!("Game {} has exited", self.game_guid());
|
||||
}
|
||||
|
||||
fn spawn_send_loading_events(&self, user: &UserConnection, player_id: u8, players: std::sync::Arc<Vec<oj_rc_core::persist::user::PlayerDescriptor>>) {
|
||||
|
||||
@@ -29,4 +29,5 @@ pub fn handler() -> OperationsHandler<crate::UserTy, crate::data::custom::Custom
|
||||
.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(calculate_mmr::mmr_provider())
|
||||
.add(polariton_server::operations::Ack::<25, _>::default()) // save social settings, sent on escape menu settings save (should probably be saved someday...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user