diff --git a/src/database/models/player.rs b/src/database/models/player.rs index bdfc5fc..6a89839 100644 --- a/src/database/models/player.rs +++ b/src/database/models/player.rs @@ -5,8 +5,10 @@ use mars_api_rs_derive::IdentifiableDocument; use mongodb::Collection; use serde::{Serialize, Deserialize}; use std::collections::HashMap; +use num_traits::ToPrimitive; use crate::{database::CollectionOwner, socket::{leaderboard::ScoreType, player::{player_xp_listener::PlayerXPListener, player_events::PlayerXPGainData}, server::server_context::ServerContext, event_type::EventType}}; +use crate::database::models::server::{ServerEvents, XPMultiplier}; use super::{punishment::StaffNote, level::LevelGamemode, r#match::Match}; @@ -60,13 +62,34 @@ impl Player { } // TODO: Multipliers - pub async fn add_xp(&mut self, server_context: &mut ServerContext, raw_xp: u32, reason: &String, notify: bool, raw_only: bool) { + pub async fn add_xp( + &mut self, + server_context: &mut ServerContext, + raw_xp: u32, + reason: &String, + notify: bool, + raw_only: bool + ) { let use_exponential = server_context.api_state.config.options.use_exponential_exp; let original_level = self.stats.get_level(use_exponential); - let target_xp_increment = if raw_only { raw_xp } else { u32::max(PlayerXPListener::gain(raw_xp, original_level), raw_xp) }; + let multiplier = match server_context.get_server_events().await { + Some(events) => { + match events.xp_multiplier { + Some(multiplier) => multiplier.value, + None => 1.0f32 + } + } + None => 1.0f32 + }; + let multiplied = ((raw_xp as f32) * multiplier).to_u32().unwrap_or(raw_xp); + let target_xp_increment = if raw_only { multiplied } else { u32::max(PlayerXPListener::gain(raw_xp, original_level), multiplied) }; self.stats.xp += target_xp_increment; + let used_multiplier = target_xp_increment == multiplied; - server_context.call(&EventType::PlayerXpGain, PlayerXPGainData { player_id: self.id.clone(), gain: target_xp_increment, reason: reason.clone(), notify }).await; + server_context.call(&EventType::PlayerXpGain, PlayerXPGainData { + player_id: self.id.clone(), gain: target_xp_increment, + reason: reason.clone(), notify, multiplier: if used_multiplier { Some(multiplier) } else { None } + }).await; server_context.api_state.leaderboards.xp.increment(&self.id_name(), Some(target_xp_increment)).await; } diff --git a/src/socket/player/player_events.rs b/src/socket/player/player_events.rs index ef45ebd..3591e26 100644 --- a/src/socket/player/player_events.rs +++ b/src/socket/player/player_events.rs @@ -81,7 +81,8 @@ pub struct PlayerXPGainData { pub player_id: String, pub gain: u32, pub reason: String, - pub notify: bool + pub notify: bool, + pub multiplier: Option } #[derive(Serialize, Deserialize)] diff --git a/src/socket/server/server_context.rs b/src/socket/server/server_context.rs index 4492adb..52f93db 100644 --- a/src/socket/server/server_context.rs +++ b/src/socket/server/server_context.rs @@ -7,6 +7,7 @@ use tokio::net::TcpStream; use tokio_tungstenite::{WebSocketStream, tungstenite::Message}; use crate::{database::models::r#match::Match, socket::event_type::EventType, util::string::deflate_string, MarsAPIState}; +use crate::database::models::server::ServerEvents; pub struct ServerContext { pub id: String, @@ -27,6 +28,10 @@ impl ServerContext { self.api_state.redis.get(&self.get_current_match_id_key()).await.ok() } + pub async fn get_server_events(&self) -> Option { + self.api_state.redis.get(&self.get_server_events_key()).await.ok() + } + pub async fn get_match(&self) -> Option { self.api_state.redis.get(&format!("match:{}", self.get_current_match_id().await.unwrap_or_else(|| "null".to_owned()))).await.ok() } @@ -34,7 +39,6 @@ impl ServerContext { pub async fn call(&mut self, event_type: &EventType, data: T) { let packet = Packet { event: event_type.clone(), data }; let body = serde_json::to_string(&packet).unwrap(); - debug!("Sending {} to {}", &body, &self.id); let binary = Message::Binary(deflate_string(body.as_bytes()).unwrap()); let _ = self.stream.send(binary).await; } @@ -46,6 +50,10 @@ impl ServerContext { fn get_last_alive_time_key(&self) -> String { format!("server:{}:last_alive_time", self.id) } + + fn get_server_events_key(&self) -> String { + format!("server:{}:events", self.id) + } } #[derive(Serialize, Deserialize)] diff --git a/src/socket/socket_router.rs b/src/socket/socket_router.rs index 8068340..2e2aaa2 100644 --- a/src/socket/socket_router.rs +++ b/src/socket/socket_router.rs @@ -381,7 +381,10 @@ impl SocketRouter { async fn on_destroyable_destroy(&mut self, data: DestroyableDestroyData) -> Result<(), SocketError> { let mut current_match = unwrap_helper::return_default!(self.server.get_match().await, Err(SocketError::InvalidMatchState)); for contribution in data.contributions.iter() { - let mut participant = current_match.participants.get(&contribution.player_id).unwrap().clone(); + let mut participant = match current_match.participants.get(&contribution.player_id) { + None => continue, + Some(participant) => participant.clone() + }; for participant_listener in self.participant_listeners.iter() { participant_listener.on_destroyable_destroy( &mut self.server, @@ -490,7 +493,10 @@ impl SocketRouter { async fn on_flag_drop(&mut self, data: FlagDropData) -> Result<(), SocketError> { let mut current_match = unwrap_helper::return_default!(self.server.get_match().await, Err(SocketError::InvalidMatchState)); - let mut participant = current_match.participants.get(&data.player_id).unwrap().clone(); + let mut participant = match current_match.participants.get(&data.player_id) { + None => { return Ok(()) } + Some(participant) => participant.clone() + }; for participant_listener in self.participant_listeners.iter() { participant_listener.on_flag_drop(&mut self.server, &mut current_match, &mut participant, data.held_time).await; };