mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add basic currency support and plumb multiplayer rewards screen #70
This commit is contained in:
@@ -121,3 +121,47 @@ impl AuthErrorCode {
|
|||||||
(self as u16).to_string()
|
(self as u16).to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(u16)] // doesn't really matter
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SocialErrorCode {
|
||||||
|
None = 0,
|
||||||
|
UnexpectedError = 1,
|
||||||
|
UserDoesNotExist = 2,
|
||||||
|
UserAlreadyFriends = 3,
|
||||||
|
MaxFriends = 4,
|
||||||
|
TargetMaxFriends = 5,
|
||||||
|
UserNotFriend = 6,
|
||||||
|
InviteAlreadySent = 7,
|
||||||
|
UserIsSelf = 8,
|
||||||
|
DatabaseError = 9,
|
||||||
|
UserNotOnline = 10,
|
||||||
|
AutoDeclinedFriendOrClan = 11,
|
||||||
|
UserAcceptsPlatoonInvitesFromFriendsAndClansOnly = 12,
|
||||||
|
PlatoonIsFull = 13,
|
||||||
|
UserNotInPlatoon = 14,
|
||||||
|
UserNotPlatoonFound = 15,
|
||||||
|
NoInvite = 16,
|
||||||
|
AlreadyInvited = 17,
|
||||||
|
TheyNotInPlatoon = 18,
|
||||||
|
TheyNotPlatoonLeader = 19,
|
||||||
|
NoClansFound = 20,
|
||||||
|
UserNotInClan = 21,
|
||||||
|
AlreadyInClan = 22,
|
||||||
|
ClanRankTooLow = 23,
|
||||||
|
ClanFull = 24,
|
||||||
|
ClanNotFound = 25,
|
||||||
|
ClanClosed = 26,
|
||||||
|
UserNotFoundInClan = 28,
|
||||||
|
NotClanLeader = 29,
|
||||||
|
TargetAlreadyInClan = 30,
|
||||||
|
ClanAlreadyExists = 31,
|
||||||
|
InvalidClanName = 32,
|
||||||
|
InvalidUsername = 33,
|
||||||
|
WaitingForInviteResponse = 34,
|
||||||
|
AlreadyInPlatoon = 35,
|
||||||
|
NotInPlatoon = 36,
|
||||||
|
NotPlatoonLeader = 37,
|
||||||
|
UserBlockedYou = 38,
|
||||||
|
NoConnection = 39
|
||||||
|
}
|
||||||
|
|||||||
@@ -624,6 +624,57 @@ impl UserData {
|
|||||||
}
|
}
|
||||||
Ok(players)
|
Ok(players)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) async fn currency_op(&self, ty: super::CurrencyType, op: super::CurrencyOp) -> Result<u64, oj_rc_database::sea_orm::DbErr> {
|
||||||
|
let desc = match ty {
|
||||||
|
super::CurrencyType::Free => oj_rc_database::schema::user_aux::Descriptor::UserFreeCurrency,
|
||||||
|
super::CurrencyType::Paid => oj_rc_database::schema::user_aux::Descriptor::UserPaidCurrency,
|
||||||
|
super::CurrencyType::TechPoints => oj_rc_database::schema::user_aux::Descriptor::TechPoints,
|
||||||
|
super::CurrencyType::Experience => oj_rc_database::schema::user_aux::Descriptor::UserXP,
|
||||||
|
};
|
||||||
|
let model_opt = match op {
|
||||||
|
super::CurrencyOp::Get => {
|
||||||
|
self.db.update_user_aux_by_user_id_and_descriptor_custom(
|
||||||
|
self.account.id,
|
||||||
|
desc.clone(),
|
||||||
|
|_model| None
|
||||||
|
).await?
|
||||||
|
},
|
||||||
|
super::CurrencyOp::Add(to_add) => {
|
||||||
|
self.db.update_user_aux_by_user_id_and_descriptor_custom(
|
||||||
|
self.account.id,
|
||||||
|
desc.clone(),
|
||||||
|
move |model| {
|
||||||
|
use oj_rc_database::sea_orm::IntoActiveModel;
|
||||||
|
let new_currency = model.data.parse::<u64>().unwrap_or_default() + to_add;
|
||||||
|
let mut am = model.to_owned().into_active_model();
|
||||||
|
am.data = oj_rc_database::sea_orm::ActiveValue::Set(new_currency.to_string());
|
||||||
|
Some(am)
|
||||||
|
}
|
||||||
|
).await?
|
||||||
|
},
|
||||||
|
super::CurrencyOp::Sub(to_sub) => {
|
||||||
|
self.db.update_user_aux_by_user_id_and_descriptor_custom(
|
||||||
|
self.account.id,
|
||||||
|
desc.clone(),
|
||||||
|
move |model| {
|
||||||
|
use oj_rc_database::sea_orm::IntoActiveModel;
|
||||||
|
let new_currency = model.data.parse::<u64>().unwrap_or_default() - to_sub;
|
||||||
|
let mut am = model.to_owned().into_active_model();
|
||||||
|
am.data = oj_rc_database::sea_orm::ActiveValue::Set(new_currency.to_string());
|
||||||
|
Some(am)
|
||||||
|
}
|
||||||
|
).await?
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let num: u64 = if let Some(model) = model_opt {
|
||||||
|
model.data.parse().unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
log::warn!("No {:?} user_aux found for user {}", desc, self.account.id);
|
||||||
|
0
|
||||||
|
};
|
||||||
|
Ok(num)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const INVALID_ROBOT_ERR: i16 = crate::data::error_codes::WebServicesError::InvalidRobot as i16; // 140
|
const INVALID_ROBOT_ERR: i16 = crate::data::error_codes::WebServicesError::InvalidRobot as i16; // 140
|
||||||
|
|||||||
@@ -33,4 +33,11 @@ impl super::CommonUser for UserData {
|
|||||||
async fn db_metrics(&self) -> oj_rc_database::DatabaseMetrics {
|
async fn db_metrics(&self) -> oj_rc_database::DatabaseMetrics {
|
||||||
self.db.metrics().await
|
self.db.metrics().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn currency(&self, ty: super::CurrencyType, op: super::CurrencyOp) -> Result<u64, polariton_server::operations::SimpleOpError> {
|
||||||
|
self.currency_op(ty, op).await.map_err(|e| polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::WebServicesError::DatabaseError as i16,
|
||||||
|
format!("Currency operation failed for user {}: {}", self.account.id, e),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ mod inventory;
|
|||||||
pub use inventory::UnlockedParts;
|
pub use inventory::UnlockedParts;
|
||||||
|
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers, ResolvedVehicle, CommonUser, IntercomListener, UserRole};
|
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, PlayerScore, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers, ResolvedVehicle, CommonUser, IntercomListener, UserRole, SocialUser, CurrencyType, CurrencyOp, MatchRewards};
|
||||||
|
|
||||||
pub mod intercom;
|
pub mod intercom;
|
||||||
pub use intercom::generate_token as generate_intercom_token;
|
pub use intercom::generate_token as generate_intercom_token;
|
||||||
@@ -21,6 +21,7 @@ mod lobby;
|
|||||||
pub use lobby::TeamChooser;
|
pub use lobby::TeamChooser;
|
||||||
mod common;
|
mod common;
|
||||||
mod chat;
|
mod chat;
|
||||||
|
mod social;
|
||||||
|
|
||||||
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
||||||
|
|
||||||
|
|||||||
@@ -167,4 +167,117 @@ impl super::MultiplayerUser for UserData {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_game_score(&self, guid: &str, score: super::PlayerScore) -> Result<i32, super::MultiplayerError> {
|
||||||
|
if let Some(guid) = crate::persist::user::str_to_i64(guid) {
|
||||||
|
if let Some(score_id) = score.id {
|
||||||
|
let model = oj_rc_database::schema::multiplayer_game_score::ActiveModel {
|
||||||
|
id: oj_rc_database::sea_orm::Set(score_id),
|
||||||
|
player_id: oj_rc_database::sea_orm::NotSet,
|
||||||
|
creation_time: oj_rc_database::sea_orm::NotSet,
|
||||||
|
is_claimed: oj_rc_database::sea_orm::NotSet,
|
||||||
|
kills: oj_rc_database::sea_orm::Set(score.kills as i32),
|
||||||
|
deaths: oj_rc_database::sea_orm::Set(score.deaths as i32),
|
||||||
|
assists: oj_rc_database::sea_orm::Set(score.assists as i32),
|
||||||
|
heal_assists: oj_rc_database::sea_orm::Set(score.heal_assists as i32),
|
||||||
|
healed: oj_rc_database::sea_orm::Set(score.healed as i32),
|
||||||
|
received_healed: oj_rc_database::sea_orm::Set(score.received_healed as i32),
|
||||||
|
damaged: oj_rc_database::sea_orm::Set(score.damaged as i32),
|
||||||
|
received_damaged: oj_rc_database::sea_orm::Set(score.received_damaged as i32),
|
||||||
|
crystals: oj_rc_database::sea_orm::Set(score.crystals as i32),
|
||||||
|
total: oj_rc_database::sea_orm::Set(score.total as i32),
|
||||||
|
};
|
||||||
|
let persisted_model = self.db.update_score(model).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to update player score for user {} in game {}: {}", self.account.id, guid, e);
|
||||||
|
super::MultiplayerError {
|
||||||
|
code: super::MultiplayerErrorCode::CustomString,
|
||||||
|
message: format!("Failed to update player score for user {} in game {}: {}", self.account.id, guid, e),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
Ok(persisted_model.id)
|
||||||
|
} else {
|
||||||
|
let player_opt = self.db.player_by_user_id_and_game_guid(self.account.id, guid).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve player for user {} in game {}: {}", self.account.id, guid, e);
|
||||||
|
super::MultiplayerError {
|
||||||
|
code: super::MultiplayerErrorCode::CustomString,
|
||||||
|
message: format!("Failed to retrieve player for user {} in game {}: {}", self.account.id, guid, e),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
if let Some(player) = player_opt {
|
||||||
|
let model = oj_rc_database::schema::multiplayer_game_score::ActiveModel {
|
||||||
|
id: oj_rc_database::sea_orm::NotSet,
|
||||||
|
player_id: oj_rc_database::sea_orm::Set(player.id),
|
||||||
|
creation_time: oj_rc_database::sea_orm::Set(chrono::Utc::now().timestamp()),
|
||||||
|
is_claimed: oj_rc_database::sea_orm::Set(false),
|
||||||
|
kills: oj_rc_database::sea_orm::Set(score.kills as i32),
|
||||||
|
deaths: oj_rc_database::sea_orm::Set(score.deaths as i32),
|
||||||
|
assists: oj_rc_database::sea_orm::Set(score.assists as i32),
|
||||||
|
heal_assists: oj_rc_database::sea_orm::Set(score.heal_assists as i32),
|
||||||
|
healed: oj_rc_database::sea_orm::Set(score.healed as i32),
|
||||||
|
received_healed: oj_rc_database::sea_orm::Set(score.received_healed as i32),
|
||||||
|
damaged: oj_rc_database::sea_orm::Set(score.damaged as i32),
|
||||||
|
received_damaged: oj_rc_database::sea_orm::Set(score.received_damaged as i32),
|
||||||
|
crystals: oj_rc_database::sea_orm::Set(score.crystals as i32),
|
||||||
|
total: oj_rc_database::sea_orm::Set(score.total as i32),
|
||||||
|
};
|
||||||
|
let persisted_model = self.db.insert_score(model).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to insert player score for user {} in game {}: {}", self.account.id, guid, e);
|
||||||
|
super::MultiplayerError {
|
||||||
|
code: super::MultiplayerErrorCode::CustomString,
|
||||||
|
message: format!("Failed to insert player score for user {} in game {}: {}", self.account.id, guid, e),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
Ok(persisted_model.id)
|
||||||
|
} else {
|
||||||
|
Err(super::MultiplayerError {
|
||||||
|
code: super::MultiplayerErrorCode::IncorrectGameGuid,
|
||||||
|
message: format!("Failed to find user {} in game {}", self.account.id, guid),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(super::MultiplayerError {
|
||||||
|
code: super::MultiplayerErrorCode::IncorrectGameGuid,
|
||||||
|
message: format!("Failed to parse game GUID {}", guid),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn save_player_connected_status(&self, guid: &str, is_connected: bool) -> Result<(), super::MultiplayerError> {
|
||||||
|
if let Some(guid) = crate::persist::user::str_to_i64(guid) {
|
||||||
|
let player_opt = self.db.player_by_user_id_and_game_guid(self.account.id, guid).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve player for game {} and user {}: {}", guid, self.account.id, e);
|
||||||
|
super::MultiplayerError {
|
||||||
|
code: super::MultiplayerErrorCode::CustomString,
|
||||||
|
message: format!("Failed to retrieve user's player for game {}: {}", guid, e),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
if let Some(player) = player_opt {
|
||||||
|
self.db.player_claim(player.id, is_connected).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to claim player for game {} and user {}: {}", guid, self.account.id, e);
|
||||||
|
super::MultiplayerError {
|
||||||
|
code: super::MultiplayerErrorCode::CustomString,
|
||||||
|
message: format!("Failed to claim user's player for game {}: {}", guid, e),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
log::warn!("Failed to find to-be-claimed player for user {} in game {}", self.account.id, guid);
|
||||||
|
Err(super::MultiplayerError {
|
||||||
|
code: super::MultiplayerErrorCode::CustomString,
|
||||||
|
message: "Failed to find user's player to update connected status".to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(super::MultiplayerError {
|
||||||
|
code: super::MultiplayerErrorCode::IncorrectGameGuid,
|
||||||
|
message: format!("Failed to parse game GUID {}", guid),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
91
rc_core/src/persist/user/social.rs
Normal file
91
rc_core/src/persist/user/social.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
use super::account_json::UserData;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl super::SocialUser for UserData {
|
||||||
|
async fn has_unclaimed_match_rewards(&self) -> Result<bool, polariton_server::operations::SimpleOpError> {
|
||||||
|
let count = self.db.count_score_by_user_id_and_claimed(self.account.id, false).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to count unclaimed matches for user {} : {}", self.account.id, e);
|
||||||
|
polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::SocialErrorCode::DatabaseError as i16,
|
||||||
|
format!("Failed to count unclaimed matches: {}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(count > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_unclaimed_match_rewards(&self) -> Result<super::MatchRewards, polariton_server::operations::SimpleOpError> {
|
||||||
|
let unclaimed_score_opt = self.db.score_by_user_id_and_claimed_oldest(self.account.id, false).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve unclaimed player score for user {} : {}", self.account.id, e);
|
||||||
|
polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::SocialErrorCode::DatabaseError as i16,
|
||||||
|
format!("Failed to retrieve unclaimed player score: {}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if let Some(unclaimed_score) = unclaimed_score_opt {
|
||||||
|
// TODO calculate these values
|
||||||
|
return Ok(super::MatchRewards {
|
||||||
|
season_experience: unclaimed_score.total,
|
||||||
|
experience_award_base: unclaimed_score.total,
|
||||||
|
experience_award_premium: unclaimed_score.total, // FIXME actually figure out if player has premium
|
||||||
|
experience_award_party: 0,
|
||||||
|
experience_award_tier: 0,
|
||||||
|
robits_total: unclaimed_score.total,
|
||||||
|
average_experience: unclaimed_score.total,
|
||||||
|
clan_experience: unclaimed_score.total,
|
||||||
|
robits_earned: unclaimed_score.total,
|
||||||
|
premium_robits_earned: unclaimed_score.total,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
log::warn!("No unclaimed match rewards found for user {}", self.account.id);
|
||||||
|
Err(polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::SocialErrorCode::UnexpectedError as i16,
|
||||||
|
"No remaining unclaimed match rewards for current user".to_owned(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn claim_match_rewards(&self) -> Result<bool, polariton_server::operations::SimpleOpError> {
|
||||||
|
let unclaimed_score_opt = self.db.score_by_user_id_and_claimed_oldest(self.account.id, false).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve to-be-claimed player score for user {}: {}", self.account.id, e);
|
||||||
|
polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::SocialErrorCode::DatabaseError as i16,
|
||||||
|
format!("Failed to retrieve unclaimed player score: {}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
if let Some(unclaimed_score) = unclaimed_score_opt {
|
||||||
|
self.db.score_claim(unclaimed_score.id).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to claim player score {} for user {} : {}", unclaimed_score.id, self.account.id, e);
|
||||||
|
polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::SocialErrorCode::DatabaseError as i16,
|
||||||
|
format!("Failed to claim player score: {}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
// TODO calculate currency rewards
|
||||||
|
let reward = unclaimed_score.total as u64;
|
||||||
|
self.currency_op(super::CurrencyType::Free, super::CurrencyOp::Add(reward)).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to save currency reward for user {}: {}", self.account.id, e);
|
||||||
|
polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::SocialErrorCode::DatabaseError as i16,
|
||||||
|
format!("Failed to save currency reward: {}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let experience = unclaimed_score.total as u64 * 4;
|
||||||
|
self.currency_op(super::CurrencyType::Experience, super::CurrencyOp::Add(experience)).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to save experience reward for user {}: {}", self.account.id, e);
|
||||||
|
polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::SocialErrorCode::DatabaseError as i16,
|
||||||
|
format!("Failed to save experience reward: {}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(self.db.count_score_by_user_id_and_claimed(self.account.id, false).await.is_ok_and(|x| x > 0))
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,7 +61,7 @@ pub trait UserAuthenticator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait User<C>: ChatUser + LobbyUser + MultiplayerUser + IntercomUser + CommonUser {
|
pub trait User<C>: ChatUser + SocialUser + LobbyUser + MultiplayerUser + IntercomUser + CommonUser {
|
||||||
async fn unlocked_parts(&self) -> Vec<u32>;
|
async fn unlocked_parts(&self) -> Vec<u32>;
|
||||||
async fn selected_garage(&self) -> (String, u32);
|
async fn selected_garage(&self) -> (String, u32);
|
||||||
async fn select_garage(&self, slot: i32) -> Result<(), i16>;
|
async fn select_garage(&self, slot: i32) -> Result<(), i16>;
|
||||||
@@ -339,6 +339,22 @@ pub trait MultiplayerUser: IntercomUser + CommonUser {
|
|||||||
async fn game_players(&self, guid: &str) -> Result<Vec<PlayerDescriptor>, MultiplayerError>;
|
async fn game_players(&self, guid: &str) -> Result<Vec<PlayerDescriptor>, MultiplayerError>;
|
||||||
async fn complete_game(&self, guid: &str) -> Result<(), MultiplayerError>;
|
async fn complete_game(&self, guid: &str) -> Result<(), MultiplayerError>;
|
||||||
async fn game_info(&self, guid: &str) -> Result<Option<GameDescriptor>, MultiplayerError>;
|
async fn game_info(&self, guid: &str) -> Result<Option<GameDescriptor>, MultiplayerError>;
|
||||||
|
async fn update_game_score(&self, guid: &str, score: PlayerScore) -> Result<i32, MultiplayerError>;
|
||||||
|
async fn save_player_connected_status(&self, guid: &str, is_connected: bool) -> Result<(), MultiplayerError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PlayerScore {
|
||||||
|
pub id: Option<i32>,
|
||||||
|
pub kills: u32,
|
||||||
|
pub deaths: u32,
|
||||||
|
pub assists: u32,
|
||||||
|
pub heal_assists: u32,
|
||||||
|
pub healed: u32,
|
||||||
|
pub received_healed: u32,
|
||||||
|
pub damaged: u32,
|
||||||
|
pub received_damaged: u32,
|
||||||
|
pub crystals: u32, // crystals destroyed
|
||||||
|
pub total: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@@ -387,4 +403,38 @@ pub trait CommonUser: Send + Sync {
|
|||||||
fn is_royal(&self) -> bool;
|
fn is_royal(&self) -> bool;
|
||||||
fn is_banned(&self) -> bool;
|
fn is_banned(&self) -> bool;
|
||||||
async fn db_metrics(&self) -> oj_rc_database::DatabaseMetrics;
|
async fn db_metrics(&self) -> oj_rc_database::DatabaseMetrics;
|
||||||
|
async fn currency(&self, ty: CurrencyType, op: CurrencyOp) -> Result<u64, polariton_server::operations::SimpleOpError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum CurrencyType {
|
||||||
|
Free,
|
||||||
|
Paid,
|
||||||
|
TechPoints,
|
||||||
|
Experience,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum CurrencyOp {
|
||||||
|
Get,
|
||||||
|
Add(u64),
|
||||||
|
Sub(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait SocialUser: Send + Sync {
|
||||||
|
async fn has_unclaimed_match_rewards(&self) -> Result<bool, polariton_server::operations::SimpleOpError>;
|
||||||
|
async fn get_unclaimed_match_rewards(&self) -> Result<MatchRewards, polariton_server::operations::SimpleOpError>;
|
||||||
|
async fn claim_match_rewards(&self) -> Result<bool, polariton_server::operations::SimpleOpError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MatchRewards {
|
||||||
|
pub season_experience: i32,
|
||||||
|
pub experience_award_base: i32,
|
||||||
|
pub experience_award_premium: i32,
|
||||||
|
pub experience_award_party: i32,
|
||||||
|
pub experience_award_tier: i32,
|
||||||
|
pub robits_total: i32,
|
||||||
|
pub average_experience: i32,
|
||||||
|
pub clan_experience: i32,
|
||||||
|
pub robits_earned: i32,
|
||||||
|
pub premium_robits_earned: i32,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
impl MigrationName for Migration {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"m20251228_000001_create_score_table"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
// Define how to apply this migration: Create the Scores table.
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(crate::schema::multiplayer_game_score::Entity)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(crate::schema::multiplayer_game_score::Column::Id)
|
||||||
|
.integer()
|
||||||
|
.not_null()
|
||||||
|
.auto_increment()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::PlayerId).integer().not_null())
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk-scores-player_id")
|
||||||
|
.from(crate::schema::multiplayer_game_score::Entity, crate::schema::multiplayer_game_score::Column::PlayerId)
|
||||||
|
.to(crate::schema::multiplayer_game_player::Entity, crate::schema::multiplayer_game_player::Column::Id),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::CreationTime).big_integer().not_null())
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::IsClaimed).boolean().default(false))
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::Kills).integer().default(0))
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::Deaths).integer().default(0))
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::Assists).integer().default(0))
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::HealAssists).integer().default(0))
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::Healed).integer().default(0))
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::ReceivedHealed).integer().default(0))
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::Damaged).integer().default(0))
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::ReceivedDamaged).integer().default(0))
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::Crystals).integer().default(0))
|
||||||
|
.col(ColumnDef::new(crate::schema::multiplayer_game_score::Column::Total).integer().default(0))
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define how to rollback this migration: Drop the Scores table.
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(crate::schema::multiplayer_game_score::Entity).to_owned())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ mod m20250713_000002_create_player_table;
|
|||||||
mod m20250722_000001_create_game_event_table;
|
mod m20250722_000001_create_game_event_table;
|
||||||
mod m20250816_000001_add_fake_players;
|
mod m20250816_000001_add_fake_players;
|
||||||
mod m20250918_000001_add_player_variant;
|
mod m20250918_000001_add_player_variant;
|
||||||
|
mod m20251228_000001_create_score_table;
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ impl MigratorTrait for Migrator {
|
|||||||
Box::new(m20250722_000001_create_game_event_table::Migration),
|
Box::new(m20250722_000001_create_game_event_table::Migration),
|
||||||
Box::new(m20250816_000001_add_fake_players::Migration),
|
Box::new(m20250816_000001_add_fake_players::Migration),
|
||||||
Box::new(m20250918_000001_add_player_variant::Migration),
|
Box::new(m20250918_000001_add_player_variant::Migration),
|
||||||
|
Box::new(m20251228_000001_create_score_table::Migration),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pub mod sanction;
|
|||||||
pub mod multiplayer_game;
|
pub mod multiplayer_game;
|
||||||
pub mod multiplayer_game_player;
|
pub mod multiplayer_game_player;
|
||||||
pub mod game_event;
|
pub mod game_event;
|
||||||
|
pub mod multiplayer_game_score;
|
||||||
|
|
||||||
pub fn parse_int_csv(s: &str) -> Vec<u32> {
|
pub fn parse_int_csv(s: &str) -> Vec<u32> {
|
||||||
s.split(',').filter_map(|i_as_s| {
|
s.split(',').filter_map(|i_as_s| {
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ pub enum Relation {
|
|||||||
to = "super::user::Column::Id"
|
to = "super::user::Column::Id"
|
||||||
)]
|
)]
|
||||||
User,
|
User,
|
||||||
|
#[sea_orm(has_one = "super::multiplayer_game_score::Entity")]
|
||||||
|
Score,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Related<super::multiplayer_game::Entity> for Entity {
|
impl Related<super::multiplayer_game::Entity> for Entity {
|
||||||
@@ -45,6 +47,12 @@ impl Related<super::user::Entity> for Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::multiplayer_game_score::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Score.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
|
|||||||
39
rc_database/src/schema/multiplayer_game_score.rs
Normal file
39
rc_database/src/schema/multiplayer_game_score.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "scores")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i32,
|
||||||
|
pub player_id: i32,
|
||||||
|
pub creation_time: i64, // seconds since unix epoch
|
||||||
|
pub is_claimed: bool,
|
||||||
|
pub kills: i32,
|
||||||
|
pub deaths: i32,
|
||||||
|
pub assists: i32,
|
||||||
|
pub heal_assists: i32,
|
||||||
|
pub healed: i32,
|
||||||
|
pub received_healed: i32,
|
||||||
|
pub damaged: i32,
|
||||||
|
pub received_damaged: i32,
|
||||||
|
pub crystals: i32,
|
||||||
|
pub total: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::multiplayer_game_player::Entity",
|
||||||
|
from = "Column::PlayerId",
|
||||||
|
to = "super::multiplayer_game_player::Column::Id"
|
||||||
|
)]
|
||||||
|
Player,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::multiplayer_game_player::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Player.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
@@ -110,6 +110,35 @@ impl Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn update_user_aux_by_user_id_and_descriptor_custom(&self, user_id: i32, descriptor: crate::schema::user_aux::Descriptor, custom: impl (FnOnce(&crate::schema::user_aux::Model) -> Option<crate::schema::user_aux::ActiveModel>) + Send + 'static) -> Result<Option<crate::schema::user_aux::Model>, sea_orm::DbErr> {
|
||||||
|
self.orm.transaction(|txn| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let opt = crate::schema::user_aux::Entity::find()
|
||||||
|
.filter(crate::schema::user_aux::Column::UserId.eq(user_id))
|
||||||
|
.filter(crate::schema::user_aux::Column::Descriptor.eq(descriptor))
|
||||||
|
.one(txn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Some(model) = opt {
|
||||||
|
if let Some(updated_model) = custom(&model) {
|
||||||
|
Ok(Some(crate::schema::user_aux::Entity::update(updated_model)
|
||||||
|
.exec(txn)
|
||||||
|
.await?))
|
||||||
|
} else {
|
||||||
|
Ok(Some(model))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).await.map_err(|e| {
|
||||||
|
match e {
|
||||||
|
sea_orm::TransactionError::Connection(db) => db,
|
||||||
|
sea_orm::TransactionError::Transaction(txn) => txn,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn perms_by_user_id(&self, user_id: i32) -> Result<Option<crate::schema::permissions::Model>, sea_orm::DbErr> {
|
pub async fn perms_by_user_id(&self, user_id: i32) -> Result<Option<crate::schema::permissions::Model>, sea_orm::DbErr> {
|
||||||
crate::schema::permissions::Entity::find()
|
crate::schema::permissions::Entity::find()
|
||||||
.filter(crate::schema::permissions::Column::UserId.eq(user_id))
|
.filter(crate::schema::permissions::Column::UserId.eq(user_id))
|
||||||
@@ -404,7 +433,7 @@ impl Database {
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn players_by_game_id_and_completion(&self, game_id: i32) -> Result<Vec<crate::schema::multiplayer_game_player::Model>, sea_orm::DbErr> {
|
pub async fn players_by_game_id(&self, game_id: i32) -> Result<Vec<crate::schema::multiplayer_game_player::Model>, sea_orm::DbErr> {
|
||||||
Ok(crate::schema::multiplayer_game_player::Entity::find()
|
Ok(crate::schema::multiplayer_game_player::Entity::find()
|
||||||
.find_also_related(crate::schema::multiplayer_game::Entity)
|
.find_also_related(crate::schema::multiplayer_game::Entity)
|
||||||
.filter(crate::schema::multiplayer_game::Column::Id.eq(game_id))
|
.filter(crate::schema::multiplayer_game::Column::Id.eq(game_id))
|
||||||
@@ -415,11 +444,33 @@ impl Database {
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn player_by_user_id_and_game_guid(&self, user_id: i32, game_guid: i64) -> Result<Option<crate::schema::multiplayer_game_player::Model>, sea_orm::DbErr> {
|
||||||
|
Ok(crate::schema::multiplayer_game::Entity::find()
|
||||||
|
.find_also_related(crate::schema::multiplayer_game_player::Entity)
|
||||||
|
.filter(crate::schema::multiplayer_game::Column::Guid.eq(game_guid))
|
||||||
|
.filter(crate::schema::multiplayer_game_player::Column::UserId.eq(user_id))
|
||||||
|
//.order_by_asc(crate::schema::multiplayer_game::Column::CreationTime)
|
||||||
|
.one(&self.orm)
|
||||||
|
.await?
|
||||||
|
.and_then(|(_, player)| player))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn insert_players(&self, entities: Vec<crate::schema::multiplayer_game_player::ActiveModel>) -> Result<(), sea_orm::DbErr> {
|
pub async fn insert_players(&self, entities: Vec<crate::schema::multiplayer_game_player::ActiveModel>) -> Result<(), sea_orm::DbErr> {
|
||||||
crate::schema::multiplayer_game_player::Entity::insert_many(entities.into_iter()).exec(&self.orm).await?;
|
crate::schema::multiplayer_game_player::Entity::insert_many(entities.into_iter()).exec(&self.orm).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn player_claim(&self, player_id: i32, is_claimed: bool) -> Result<(), sea_orm::DbErr> {
|
||||||
|
crate::schema::multiplayer_game_player::Entity::update(crate::schema::multiplayer_game_player::ActiveModel {
|
||||||
|
id: sea_orm::ActiveValue::Set(player_id),
|
||||||
|
is_claimed: sea_orm::Set(is_claimed),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.exec(&self.orm)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn game_event_at_time(&self, time: i64, variant: crate::schema::game_event::EventVariant) -> Result<Option<crate::schema::game_event::Model>, sea_orm::DbErr> {
|
pub async fn game_event_at_time(&self, time: i64, variant: crate::schema::game_event::EventVariant) -> Result<Option<crate::schema::game_event::Model>, sea_orm::DbErr> {
|
||||||
crate::schema::game_event::Entity::find()
|
crate::schema::game_event::Entity::find()
|
||||||
.filter(crate::schema::game_event::Column::Start.lte(time))
|
.filter(crate::schema::game_event::Column::Start.lte(time))
|
||||||
@@ -434,6 +485,53 @@ impl Database {
|
|||||||
entity.insert(&self.orm).await
|
entity.insert(&self.orm).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn score_by_player_id(&self, player_id: i32) -> Result<Option<crate::schema::multiplayer_game_score::Model>, sea_orm::DbErr> {
|
||||||
|
crate::schema::multiplayer_game_score::Entity::find()
|
||||||
|
.filter(crate::schema::multiplayer_game_score::Column::PlayerId.eq(player_id))
|
||||||
|
.one(&self.orm)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn score_by_user_id_and_claimed_oldest(&self, user_id: i32, is_claimed: bool) -> Result<Option<crate::schema::multiplayer_game_score::Model>, sea_orm::DbErr> {
|
||||||
|
crate::schema::multiplayer_game_player::Entity::find()
|
||||||
|
.find_also_related(crate::schema::multiplayer_game_score::Entity)
|
||||||
|
.filter(crate::schema::multiplayer_game_player::Column::UserId.eq(user_id))
|
||||||
|
.filter(crate::schema::multiplayer_game_score::Column::IsClaimed.eq(is_claimed))
|
||||||
|
.order_by_asc(crate::schema::multiplayer_game_player::Column::CreationTime)
|
||||||
|
.one(&self.orm)
|
||||||
|
.await
|
||||||
|
.map(|opt| opt.and_then(|(_player, score)| score))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn insert_score(&self, entity: crate::schema::multiplayer_game_score::ActiveModel) -> Result<crate::schema::multiplayer_game_score::Model, sea_orm::DbErr> {
|
||||||
|
entity.insert(&self.orm).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_score(&self, entity: crate::schema::multiplayer_game_score::ActiveModel) -> Result<crate::schema::multiplayer_game_score::Model, sea_orm::DbErr> {
|
||||||
|
crate::schema::multiplayer_game_score::Entity::update(entity).exec(&self.orm).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn score_claim(&self, score_id: i32) -> Result<(), sea_orm::DbErr> {
|
||||||
|
crate::schema::multiplayer_game_score::Entity::update(crate::schema::multiplayer_game_score::ActiveModel {
|
||||||
|
id: sea_orm::ActiveValue::Set(score_id),
|
||||||
|
is_claimed: sea_orm::Set(true),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.exec(&self.orm)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn count_score_by_user_id_and_claimed(&self, user_id: i32, is_claimed: bool) -> Result<u64, sea_orm::DbErr> {
|
||||||
|
crate::schema::multiplayer_game_player::Entity::find()
|
||||||
|
.find_also_related(crate::schema::multiplayer_game_score::Entity)
|
||||||
|
.filter(crate::schema::multiplayer_game_player::Column::UserId.eq(user_id))
|
||||||
|
.filter(crate::schema::multiplayer_game_score::Column::IsClaimed.eq(is_claimed))
|
||||||
|
.order_by_asc(crate::schema::multiplayer_game_player::Column::CreationTime)
|
||||||
|
.count(&self.orm)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn metrics(&self) -> super::DatabaseMetrics {
|
pub async fn metrics(&self) -> super::DatabaseMetrics {
|
||||||
self.metrics.lock().unwrap().snapshot()
|
self.metrics.lock().unwrap().snapshot()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
let start_time = chrono::Utc::now();
|
let start_time = chrono::Utc::now();
|
||||||
START_TIMESTAMP_S.store(start_time.timestamp(), std::sync::atomic::Ordering::Relaxed);
|
START_TIMESTAMP_S.store(start_time.timestamp(), std::sync::atomic::Ordering::Relaxed);
|
||||||
|
|
||||||
|
log::info!("lobby_room ready");
|
||||||
if args.once {
|
if args.once {
|
||||||
log::warn!("Handling first connection and then exiting");
|
log::warn!("Handling first connection and then exiting");
|
||||||
let (socket, address) = listener.accept().await?;
|
let (socket, address) = listener.accept().await?;
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ impl Location {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct UserData {
|
pub(super) struct UserData {
|
||||||
|
pub score_id: std::sync::atomic::AtomicI64,
|
||||||
pub kills: std::sync::atomic::AtomicU32,
|
pub kills: std::sync::atomic::AtomicU32,
|
||||||
pub deaths: std::sync::atomic::AtomicU32,
|
pub deaths: std::sync::atomic::AtomicU32,
|
||||||
pub assists: std::sync::atomic::AtomicU32,
|
pub assists: std::sync::atomic::AtomicU32,
|
||||||
@@ -130,6 +131,7 @@ pub(super) struct UserData {
|
|||||||
impl UserData {
|
impl UserData {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
score_id: std::sync::atomic::AtomicI64::new(i64::MIN),
|
||||||
kills: std::sync::atomic::AtomicU32::new(0),
|
kills: std::sync::atomic::AtomicU32::new(0),
|
||||||
deaths: std::sync::atomic::AtomicU32::new(0),
|
deaths: std::sync::atomic::AtomicU32::new(0),
|
||||||
assists: std::sync::atomic::AtomicU32::new(0),
|
assists: std::sync::atomic::AtomicU32::new(0),
|
||||||
@@ -173,6 +175,23 @@ impl UserData {
|
|||||||
delta_score: delta.unwrap_or(backup_delta),
|
delta_score: delta.unwrap_or(backup_delta),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_core(&self) -> oj_rc_core::persist::user::PlayerScore {
|
||||||
|
let id_maybe = self.score_id.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
oj_rc_core::persist::user::PlayerScore {
|
||||||
|
id: id_maybe.try_into().ok(),
|
||||||
|
kills: self.kills.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
deaths: self.deaths.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
assists: self.assists.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
heal_assists: self.heal_assists.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
healed: self.healed.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
received_healed: self.received_healed.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
damaged: self.cubes.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
received_damaged: self.received_cubes.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
crystals: self.crystals.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
total: self.generic_score(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
@@ -521,6 +540,9 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
).await);
|
).await);
|
||||||
log::debug!("User {} is validated to play game {}", new_user.user.user_id(), game_guid);
|
log::debug!("User {} is validated to play game {}", new_user.user.user_id(), game_guid);
|
||||||
let new_user = std::sync::Arc::new(new_user);
|
let new_user = std::sync::Arc::new(new_user);
|
||||||
|
if let Err(e) = new_user.user.save_player_connected_status(self.game_guid(), true).await {
|
||||||
|
log::error!("Failed to mark player {} (user {}) as connected to game {}: {}", id, user_id, self.game_guid(), e);
|
||||||
|
}
|
||||||
let mut users = self.users.write().await;
|
let mut users = self.users.write().await;
|
||||||
users.insert(id, new_user.clone());
|
users.insert(id, new_user.clone());
|
||||||
for fake_id in new_user.aliases.iter() {
|
for fake_id in new_user.aliases.iter() {
|
||||||
@@ -558,17 +580,16 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let is_game_complete = self.is_complete.load(std::sync::atomic::Ordering::Relaxed);
|
let is_game_complete = self.is_complete.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
for disconnecter in disconnecting_players {
|
|
||||||
if !is_game_complete {
|
|
||||||
self.broadcast(
|
|
||||||
rlnl::event_code::NetworkEvent::OnAnotherClientDisconnected,
|
|
||||||
literustlib::packet::Property::ReliableOrdered,
|
|
||||||
&rlnl::events::ingame::PlayerId { player: disconnecter },
|
|
||||||
true,
|
|
||||||
).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if is_game_complete {
|
if is_game_complete {
|
||||||
|
// save score to database as they disconnect
|
||||||
|
// this happens before they return to the main menu
|
||||||
|
let scores = user_info.counters.as_core();
|
||||||
|
match conn.user.update_game_score(self.game_guid(), scores).await {
|
||||||
|
Ok(score_id) => user_info.counters.score_id.store(score_id as i64, std::sync::atomic::Ordering::Relaxed),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Failed to save score for player {} (user {}) after end of game {}: {}", player_id, user_id, self.game_guid(), e);
|
||||||
|
},
|
||||||
|
}
|
||||||
// in every other case this packet would've already been sent
|
// in every other case this packet would've already been sent
|
||||||
// this makes the end-of-match "continue" button send you back to the main menu a bit sooner
|
// this makes the end-of-match "continue" button send you back to the main menu a bit sooner
|
||||||
// (otherwise it waits for the multiplayer server to disconnect via timeout)
|
// (otherwise it waits for the multiplayer server to disconnect via timeout)
|
||||||
@@ -577,6 +598,15 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
literustlib::packet::Property::ReliableOrdered,
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
&conn.connection.connection,
|
&conn.connection.connection,
|
||||||
).await);
|
).await);
|
||||||
|
} else {
|
||||||
|
for disconnecter in disconnecting_players {
|
||||||
|
self.broadcast(
|
||||||
|
rlnl::event_code::NetworkEvent::OnAnotherClientDisconnected,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&rlnl::events::ingame::PlayerId { player: disconnecter },
|
||||||
|
true,
|
||||||
|
).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut has_active_connections = false;
|
let mut has_active_connections = false;
|
||||||
@@ -594,6 +624,9 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Err(e) = conn.user.save_player_connected_status(self.game_guid(), false).await {
|
||||||
|
log::error!("Failed to mark player {} (user {}) as disconnected in game {}: {}", player_id, user_id, self.game_guid(), e);
|
||||||
|
}
|
||||||
conn.connection.connection.goodbye(&conn.connection.sender).await;
|
conn.connection.connection.goodbye(&conn.connection.sender).await;
|
||||||
return has_active_connections;
|
return has_active_connections;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
let start_time = chrono::Utc::now();
|
let start_time = chrono::Utc::now();
|
||||||
START_TIMESTAMP_S.store(start_time.timestamp(), std::sync::atomic::Ordering::Relaxed);
|
START_TIMESTAMP_S.store(start_time.timestamp(), std::sync::atomic::Ordering::Relaxed);
|
||||||
|
|
||||||
|
log::info!("services_room ready");
|
||||||
if args.once {
|
if args.once {
|
||||||
log::warn!("Handling first connection and then exiting");
|
log::warn!("Handling first connection and then exiting");
|
||||||
let (socket, address) = listener.accept().await?;
|
let (socket, address) = listener.accept().await?;
|
||||||
|
|||||||
@@ -1,14 +1,36 @@
|
|||||||
use polariton_server::operations::SimpleFunc;
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
use polariton::operation::{ParameterTable, Typed};
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
const FREE_BALANCE_PARAM_KEY: u8 = 74;
|
const FREE_BALANCE_PARAM_KEY: u8 = 74;
|
||||||
const PAID_BALANCE_PARAM_KEY: u8 = 87;
|
const PAID_BALANCE_PARAM_KEY: u8 = 87;
|
||||||
|
|
||||||
pub(super) fn balance_wallet_provider() -> SimpleFunc<66, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
const CODE: u8 = 66;
|
||||||
SimpleFunc::new(|params, _| {
|
|
||||||
let mut params = params.to_dict();
|
pub(super) struct WalletBallancer;
|
||||||
params.insert(FREE_BALANCE_PARAM_KEY, Typed::Long(31_337_000));
|
|
||||||
params.insert(PAID_BALANCE_PARAM_KEY, Typed::Long(1));
|
#[async_trait::async_trait]
|
||||||
Ok(params.into())
|
impl <C: Send + 'static> SimpleOperation<C> for WalletBallancer {
|
||||||
})
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, _params: ParameterTable<C>, user: &Self::User) -> Result<ParameterTable<C>, SimpleOpError> {
|
||||||
|
let mut params = ParameterTable::with_capacity(3);
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let free = user_info.currency(
|
||||||
|
oj_rc_core::persist::user::CurrencyType::Free,
|
||||||
|
oj_rc_core::persist::user::CurrencyOp::Get,
|
||||||
|
).await?;
|
||||||
|
let paid = user_info.currency(
|
||||||
|
oj_rc_core::persist::user::CurrencyType::Paid,
|
||||||
|
oj_rc_core::persist::user::CurrencyOp::Get,
|
||||||
|
).await?;
|
||||||
|
params.insert(FREE_BALANCE_PARAM_KEY, Typed::Long(free as _));
|
||||||
|
params.insert(PAID_BALANCE_PARAM_KEY, Typed::Long(paid as _));
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn balance_wallet_provider<C: Send + 'static>() -> SimpleOpImpl<C, crate::UserTy, WalletBallancer> {
|
||||||
|
SimpleOpImpl::new(WalletBallancer)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,12 @@ pub(super) fn player_level_info_provider() -> SimpleFunc<3, crate::UserTy, impl
|
|||||||
key_ty: TypePrefix::Int, // int
|
key_ty: TypePrefix::Int, // int
|
||||||
val_ty: TypePrefix::Int, // int
|
val_ty: TypePrefix::Int, // int
|
||||||
items: vec![
|
items: vec![
|
||||||
(Typed::Int(0), Typed::Int(99)),
|
// FIXME load this from config
|
||||||
(Typed::Int(10_000), Typed::Int(99_000)),
|
// these are interpolated by the client
|
||||||
|
(Typed::Int(0), Typed::Int(0)),
|
||||||
|
// this should interpolate to display a level of 1337
|
||||||
|
(Typed::Int(2674), Typed::Int(1)),
|
||||||
|
(Typed::Int(10_000), Typed::Int(i32::MAX / 2)),
|
||||||
] }));
|
] }));
|
||||||
Ok(params.into())
|
Ok(params.into())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,16 +1,34 @@
|
|||||||
use polariton_server::operations::SimpleFunc;
|
use polariton_server::operations::SimpleFunc;
|
||||||
use polariton::operation::{ParameterTable, Typed};
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
|
|
||||||
|
const CURRENT_CODE: u8 = 187;
|
||||||
const CURRENT_PARAM_KEY: u8 = 214;
|
const CURRENT_PARAM_KEY: u8 = 214;
|
||||||
|
|
||||||
pub(super) fn tech_points_provider() -> SimpleFunc<187, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
pub(super) struct CurrentTechPointers;
|
||||||
SimpleFunc::new(|params, _| {
|
|
||||||
let mut params = params.to_dict();
|
#[async_trait::async_trait]
|
||||||
params.insert(CURRENT_PARAM_KEY, Typed::Int(1337));
|
impl <C: Send + 'static> SimpleOperation<C> for CurrentTechPointers {
|
||||||
Ok(params.into())
|
type User = crate::UserTy;
|
||||||
})
|
const CODE: u8 = CURRENT_CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, _params: ParameterTable<C>, user: &Self::User) -> Result<ParameterTable<C>, SimpleOpError> {
|
||||||
|
let mut table = ParameterTable::with_capacity(2);
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let exp = user_info.currency(
|
||||||
|
oj_rc_core::persist::user::CurrencyType::TechPoints,
|
||||||
|
oj_rc_core::persist::user::CurrencyOp::Get,
|
||||||
|
).await?;
|
||||||
|
table.insert(CURRENT_PARAM_KEY, Typed::Int(exp as _));
|
||||||
|
Ok(table)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn tech_points_provider<C: Send + 'static>() -> SimpleOpImpl<C, crate::UserTy, CurrentTechPointers> {
|
||||||
|
SimpleOpImpl::new(CurrentTechPointers)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const UNCLAIMED_PARAM_KEY: u8 = 212;
|
const UNCLAIMED_PARAM_KEY: u8 = 212;
|
||||||
|
|
||||||
pub(super) fn tech_points_awards_provider() -> SimpleFunc<185, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
pub(super) fn tech_points_awards_provider() -> SimpleFunc<185, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
||||||
|
|||||||
@@ -1,12 +1,29 @@
|
|||||||
use polariton_server::operations::SimpleFunc;
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
use polariton::operation::{ParameterTable, Typed};
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
|
const CODE: u8 = 83;
|
||||||
|
|
||||||
const PARAM_KEY: u8 = 8;
|
const PARAM_KEY: u8 = 8;
|
||||||
|
|
||||||
pub(super) fn get_user_xp_provider() -> SimpleFunc<83, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
pub(super) struct UserTotalExperiencer;
|
||||||
SimpleFunc::new(|params, _| {
|
|
||||||
let mut params = params.to_dict();
|
#[async_trait::async_trait]
|
||||||
params.insert(PARAM_KEY, Typed::Int(31337));
|
impl <C: Send + 'static> SimpleOperation<C> for UserTotalExperiencer {
|
||||||
Ok(params.into())
|
type User = crate::UserTy;
|
||||||
})
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, _params: ParameterTable<C>, user: &Self::User) -> Result<ParameterTable<C>, SimpleOpError> {
|
||||||
|
let mut table = ParameterTable::with_capacity(2);
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let exp = user_info.currency(
|
||||||
|
oj_rc_core::persist::user::CurrencyType::Experience,
|
||||||
|
oj_rc_core::persist::user::CurrencyOp::Get,
|
||||||
|
).await?;
|
||||||
|
table.insert(PARAM_KEY, Typed::Int(exp as _));
|
||||||
|
Ok(table)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_user_xp_provider<C: Send + 'static>() -> SimpleOpImpl<C, crate::UserTy, UserTotalExperiencer> {
|
||||||
|
SimpleOpImpl::new(UserTotalExperiencer)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![allow(unused)]
|
||||||
use polariton::operation::Typed;
|
use polariton::operation::Typed;
|
||||||
|
|
||||||
pub struct ClanInviteInfo {
|
pub struct ClanInviteInfo {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use polariton_server::operations::SimpleFunc;
|
use polariton_server::operations::SimpleFunc;
|
||||||
use polariton::operation::{ParameterTable, Typed, Arr};
|
use polariton::operation::{ParameterTable, Typed, Arr};
|
||||||
|
|
||||||
use crate::data::clan_invite::*;
|
//use crate::data::clan_invite::*;
|
||||||
|
|
||||||
const PARAM_KEY: u8 = 42;
|
const PARAM_KEY: u8 = 42;
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ pub(super) fn clan_invites_provider<C: Send + Sync>() -> SimpleFunc<39, crate::U
|
|||||||
let mut params = params.to_dict();
|
let mut params = params.to_dict();
|
||||||
params.insert(PARAM_KEY, Typed::<C>::Arr(Arr {
|
params.insert(PARAM_KEY, Typed::<C>::Arr(Arr {
|
||||||
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
||||||
items: vec![
|
/*items: vec![
|
||||||
ClanInviteInfo {
|
ClanInviteInfo {
|
||||||
username: "RE_user1".to_owned(),
|
username: "RE_user1".to_owned(),
|
||||||
display_name: "RE_user1".to_owned(),
|
display_name: "RE_user1".to_owned(),
|
||||||
@@ -19,7 +19,8 @@ pub(super) fn clan_invites_provider<C: Send + Sync>() -> SimpleFunc<39, crate::U
|
|||||||
use_custom_avatar: false,
|
use_custom_avatar: false,
|
||||||
avatar_id: 0,
|
avatar_id: 0,
|
||||||
}.as_transmissible()
|
}.as_transmissible()
|
||||||
],
|
],*/
|
||||||
|
items: vec![],
|
||||||
}));
|
}));
|
||||||
Ok(params.into())
|
Ok(params.into())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ mod season_rewards;
|
|||||||
mod previous_battle_rewards;
|
mod previous_battle_rewards;
|
||||||
mod platoon_data;
|
mod platoon_data;
|
||||||
mod calculate_mmr;
|
mod calculate_mmr;
|
||||||
|
mod previous_battle_rewards_get;
|
||||||
|
mod previous_battle_rewards_claim;
|
||||||
|
|
||||||
use polariton_server::operations::OperationsHandler;
|
use polariton_server::operations::OperationsHandler;
|
||||||
|
|
||||||
@@ -31,4 +33,6 @@ pub fn handler() -> OperationsHandler<crate::UserTy, crate::data::custom::Custom
|
|||||||
.add(calculate_mmr::mmr_provider())
|
.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...)
|
.add(polariton_server::operations::Ack::<25, _>::default()) // save social settings, sent on escape menu settings save (should probably be saved someday...)
|
||||||
.add(polariton_server::operations::Ack::<0, _>::default()) // send friend request, can be sent from match leaderboard
|
.add(polariton_server::operations::Ack::<0, _>::default()) // send friend request, can be sent from match leaderboard
|
||||||
|
.add(previous_battle_rewards_get::get_battle_rewards_provider())
|
||||||
|
.add(previous_battle_rewards_claim::claim_battle_rewards_provider())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,27 @@
|
|||||||
use polariton_server::operations::SimpleFunc;
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
use polariton::operation::{ParameterTable, Typed};
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
const PARAM_KEY: u8 = 60;
|
const CODE: u8 = 54;
|
||||||
//const USER_PARAM_KEY: u8 = 1; // str (username)
|
|
||||||
|
|
||||||
pub(super) fn pending_battle_rewards_provider<C: Send + Sync>() -> SimpleFunc<54, crate::UserTy, impl (Fn(ParameterTable<C>, &crate::UserTy) -> Result<ParameterTable<C>, i16>) + Sync + Sync, C> {
|
// const USERNAME_PARAM_KEY: u8 = 1; // in; string
|
||||||
SimpleFunc::new(|params, _| {
|
const PARAM_KEY: u8 = 60;
|
||||||
let mut params = params.to_dict();
|
|
||||||
params.insert(PARAM_KEY, Typed::Bool(false));
|
pub(super) struct PreviousBattleRewarder;
|
||||||
Ok(params.into())
|
|
||||||
})
|
#[async_trait::async_trait]
|
||||||
|
impl <C: Send + 'static> SimpleOperation<C> for PreviousBattleRewarder {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, _params: ParameterTable<C>, user: &Self::User) -> Result<ParameterTable<C>, SimpleOpError> {
|
||||||
|
let mut table = ParameterTable::with_capacity(2);
|
||||||
|
let user_info = user.user()?;
|
||||||
|
table.insert(PARAM_KEY, Typed::Bool(user_info.has_unclaimed_match_rewards().await?));
|
||||||
|
Ok(table)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn pending_battle_rewards_provider<C: Send + 'static>() -> SimpleOpImpl<C, crate::UserTy, PreviousBattleRewarder> {
|
||||||
|
SimpleOpImpl::new(PreviousBattleRewarder)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
|
const CODE: u8 = 55;
|
||||||
|
|
||||||
|
// const USERNAME_PARAM_KEY: u8 = 1; // in; string
|
||||||
|
const PARAM_KEY: u8 = 60;
|
||||||
|
|
||||||
|
pub(super) struct ClaimPreviousBattleRewards;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl <C: Send + 'static> SimpleOperation<C> for ClaimPreviousBattleRewards {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, _params: ParameterTable<C>, user: &Self::User) -> Result<ParameterTable<C>, SimpleOpError> {
|
||||||
|
let mut table = ParameterTable::with_capacity(2);
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let is_success = user_info.claim_match_rewards().await?;
|
||||||
|
table.insert(PARAM_KEY, Typed::Bool(!is_success || user_info.has_unclaimed_match_rewards().await?));
|
||||||
|
Ok(table)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn claim_battle_rewards_provider<C: Send + 'static>() -> SimpleOpImpl<C, crate::UserTy, ClaimPreviousBattleRewards> {
|
||||||
|
SimpleOpImpl::new(ClaimPreviousBattleRewards)
|
||||||
|
}
|
||||||
|
|
||||||
49
rc_social_room/src/operations/previous_battle_rewards_get.rs
Normal file
49
rc_social_room/src/operations/previous_battle_rewards_get.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
|
const CODE: u8 = 53;
|
||||||
|
|
||||||
|
// const USERNAME_PARAM_KEY: u8 = 1; // in; string
|
||||||
|
//const PARAM_KEY: u8 = 60;
|
||||||
|
const NEW_SEASON_XP_PARAM_KEY: u8 = 57; // int; out
|
||||||
|
const XP_AWARD_BASE_PARAM_KEY: u8 = 58; // int; out
|
||||||
|
const XP_AWARD_PREMIUM_PARAM_KEY: u8 = 59; // int; out
|
||||||
|
const XP_AWARD_PARTY_PARAM_KEY: u8 = 61; // int; out
|
||||||
|
const XP_AWARD_TIER_PARAM_KEY: u8 = 52; // int; out
|
||||||
|
const ROBITS_TOTAL_PARAM_KEY: u8 = 50; // int; out
|
||||||
|
const AVERAGE_XP_PARAM_KEY: u8 = 54; // int; out
|
||||||
|
const CLAN_TOTAL_XP_PARAM_KEY: u8 = 55; // int; out
|
||||||
|
const ROBITS_PARAM_KEY: u8 = 68; // int; out
|
||||||
|
const PREMIUM_ROBITS_PARAM_KEY: u8 = 69; // int; out
|
||||||
|
const LONG_PLAY_MULTIPLIER_PARAM_KEY: u8 = 72; // float; out
|
||||||
|
|
||||||
|
pub(super) struct GetPreviousBattleRewards;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl <C: Send + 'static> SimpleOperation<C> for GetPreviousBattleRewards {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, _params: ParameterTable<C>, user: &Self::User) -> Result<ParameterTable<C>, SimpleOpError> {
|
||||||
|
let mut table = ParameterTable::with_capacity(12);
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let next_rewards = user_info.get_unclaimed_match_rewards().await?;
|
||||||
|
table.insert(NEW_SEASON_XP_PARAM_KEY, Typed::Int(next_rewards.season_experience));
|
||||||
|
table.insert(XP_AWARD_BASE_PARAM_KEY, Typed::Int(next_rewards.experience_award_base));
|
||||||
|
table.insert(XP_AWARD_PREMIUM_PARAM_KEY, Typed::Int(next_rewards.experience_award_premium));
|
||||||
|
table.insert(XP_AWARD_PARTY_PARAM_KEY, Typed::Int(next_rewards.experience_award_party));
|
||||||
|
table.insert(XP_AWARD_TIER_PARAM_KEY, Typed::Int(next_rewards.experience_award_tier));
|
||||||
|
table.insert(ROBITS_TOTAL_PARAM_KEY, Typed::Int(next_rewards.robits_total));
|
||||||
|
table.insert(AVERAGE_XP_PARAM_KEY, Typed::Int(next_rewards.average_experience));
|
||||||
|
table.insert(CLAN_TOTAL_XP_PARAM_KEY, Typed::Int(next_rewards.clan_experience));
|
||||||
|
table.insert(ROBITS_PARAM_KEY, Typed::Int(next_rewards.robits_earned));
|
||||||
|
table.insert(PREMIUM_ROBITS_PARAM_KEY, Typed::Int(next_rewards.premium_robits_earned));
|
||||||
|
table.insert(LONG_PLAY_MULTIPLIER_PARAM_KEY, Typed::Float(1.0));
|
||||||
|
Ok(table)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_battle_rewards_provider<C: Send + 'static>() -> SimpleOpImpl<C, crate::UserTy, GetPreviousBattleRewards> {
|
||||||
|
SimpleOpImpl::new(GetPreviousBattleRewards)
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user