mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Platoons social functionality (#91)
### Description This implements and completes #87 (to the best of my knowledge) ### Game Robocraft ### Please confirm - [x] I am the legal owner or represent the owner of all work submitted - [x] I consent to my submission being added to this FOSS project - [ ] This PR used LLMs to generate some or all of the code changes Reviewed-on: https://git.ngram.ca/OpenJam/rc-servers/pulls/91 Co-authored-by: NG (Graham) <ngniusness@gmail.com> Co-committed-by: NG (Graham) <ngniusness@gmail.com>
This commit is contained in:
@@ -8,6 +8,7 @@ pub(super) fn ignores_provider() -> SimpleFunc<8, crate::UserTy, impl (Fn(Parame
|
|||||||
let mut params = params.to_dict();
|
let mut params = params.to_dict();
|
||||||
params.insert(PARAM_KEY, Typed::Arr(Arr {
|
params.insert(PARAM_KEY, Typed::Arr(Arr {
|
||||||
ty: polariton::serdes::TypePrefix::Str,
|
ty: polariton::serdes::TypePrefix::Str,
|
||||||
|
custom_ty: None,
|
||||||
items: vec![
|
items: vec![
|
||||||
Typed::Str("Pluto".into()),
|
Typed::Str("Pluto".into()),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ impl ChatChannelInfo {
|
|||||||
(Typed::Str("channelName".into()), Typed::Str(self.channel_name.clone().into())),
|
(Typed::Str("channelName".into()), Typed::Str(self.channel_name.clone().into())),
|
||||||
(Typed::Str("members".into()), Typed::Arr(Arr {
|
(Typed::Str("members".into()), Typed::Arr(Arr {
|
||||||
ty: polariton::serdes::TypePrefix::HashMap, // hashtable
|
ty: polariton::serdes::TypePrefix::HashMap, // hashtable
|
||||||
|
custom_ty: None,
|
||||||
items: self.members.iter().map(|x| x.as_transmissible()).collect(),
|
items: self.members.iter().map(|x| x.as_transmissible()).collect(),
|
||||||
})),
|
})),
|
||||||
(Typed::Str("channelType".into()), Typed::Int(self.channel_ty as _)),
|
(Typed::Str("channelType".into()), Typed::Int(self.channel_ty as _)),
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ impl GarageSlotInfo {
|
|||||||
(Typed::Str("wasRated".into()), Typed::Bool(self.was_rated)),
|
(Typed::Str("wasRated".into()), Typed::Bool(self.was_rated)),
|
||||||
(Typed::Str("movementCategories".into()), Typed::Arr(Arr {
|
(Typed::Str("movementCategories".into()), Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Int, // int
|
ty: TypePrefix::Int, // int
|
||||||
|
custom_ty: None,
|
||||||
items: self.movement_categories.iter().map(|x| Typed::Int(x.but_bigger())).collect(),
|
items: self.movement_categories.iter().map(|x| Typed::Int(x.but_bigger())).collect(),
|
||||||
})),
|
})),
|
||||||
(Typed::Str("uniqueId1".into()), Typed::Int(self.uuid.0 as i32)),
|
(Typed::Str("uniqueId1".into()), Typed::Int(self.uuid.0 as i32)),
|
||||||
@@ -49,6 +50,7 @@ impl GarageSlotInfo {
|
|||||||
(Typed::Str("baySkinId".into()), Typed::Str(self.bay_skin_id.clone().into())),
|
(Typed::Str("baySkinId".into()), Typed::Str(self.bay_skin_id.clone().into())),
|
||||||
(Typed::Str("weaponOrder".into()), Typed::Arr(Arr {
|
(Typed::Str("weaponOrder".into()), Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Int, // int
|
ty: TypePrefix::Int, // int
|
||||||
|
custom_ty: None,
|
||||||
items: self.weapon_order.iter().map(|x| Typed::Int(*x)).collect(),
|
items: self.weapon_order.iter().map(|x| Typed::Int(*x)).collect(),
|
||||||
})),
|
})),
|
||||||
].into())
|
].into())
|
||||||
@@ -85,6 +87,7 @@ impl ControlOptions {
|
|||||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||||
Typed::Arr(Arr {
|
Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Bool, // bool
|
ty: TypePrefix::Bool, // bool
|
||||||
|
custom_ty: None,
|
||||||
items: vec![
|
items: vec![
|
||||||
Typed::Bool(self.vertical_strafing),
|
Typed::Bool(self.vertical_strafing),
|
||||||
Typed::Bool(self.sideways_driving),
|
Typed::Bool(self.sideways_driving),
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ impl TechTreeNode {
|
|||||||
(Typed::Str("tp".into()), Typed::Int(self.tech_points as i32)),
|
(Typed::Str("tp".into()), Typed::Int(self.tech_points as i32)),
|
||||||
(Typed::Str("neighbours".into()), Typed::Arr(Arr {
|
(Typed::Str("neighbours".into()), Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Str, // str
|
ty: TypePrefix::Str, // str
|
||||||
|
custom_ty: None,
|
||||||
items: self.neighbours.iter().map(|cube_id| Typed::Str(hex::encode(cube_id.to_be_bytes()).into())).collect(),
|
items: self.neighbours.iter().map(|cube_id| Typed::Str(hex::encode(cube_id.to_be_bytes()).into())).collect(),
|
||||||
})),
|
})),
|
||||||
].into())
|
].into())
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ impl WeaponData {
|
|||||||
let typed_arr: Vec<Typed<C>> = self.group_fire_scales.iter().map(|x| Typed::Float(*x)).collect();
|
let typed_arr: Vec<Typed<C>> = self.group_fire_scales.iter().map(|x| Typed::Float(*x)).collect();
|
||||||
out.push((Typed::Str("groupFireScales".into()), Typed::Arr(polariton::operation::Arr {
|
out.push((Typed::Str("groupFireScales".into()), Typed::Arr(polariton::operation::Arr {
|
||||||
ty: polariton::serdes::TypePrefix::Float,
|
ty: polariton::serdes::TypePrefix::Float,
|
||||||
|
custom_ty: None,
|
||||||
items: typed_arr,
|
items: typed_arr,
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ impl <C: Clone + Send> super::ConfigProvider<C> for CubeConfig {
|
|||||||
fn public_channels(&self) -> Typed<C> {
|
fn public_channels(&self) -> Typed<C> {
|
||||||
Typed::Arr(polariton::operation::Arr {
|
Typed::Arr(polariton::operation::Arr {
|
||||||
ty: TypePrefix::Str,
|
ty: TypePrefix::Str,
|
||||||
|
custom_ty: None,
|
||||||
items: self.chat.public_channels.iter().map(|s| Typed::Str(s.into())).collect(),
|
items: self.chat.public_channels.iter().map(|s| Typed::Str(s.into())).collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ impl GameEventSequence {
|
|||||||
GameEventTransmissible {
|
GameEventTransmissible {
|
||||||
maps: Typed::Arr(polariton::operation::Arr {
|
maps: Typed::Arr(polariton::operation::Arr {
|
||||||
ty: polariton::serdes::TypePrefix::Str,
|
ty: polariton::serdes::TypePrefix::Str,
|
||||||
|
custom_ty: None,
|
||||||
items: vec![
|
items: vec![
|
||||||
Typed::Str(crate::data::game_mode::GameMap::from_persist(item_now.singleplayer.map).as_str().into()),
|
Typed::Str(crate::data::game_mode::GameMap::from_persist(item_now.singleplayer.map).as_str().into()),
|
||||||
Typed::Str(crate::data::game_mode::GameMap::from_persist(item_now.multiplayer.map).as_str().into()),
|
Typed::Str(crate::data::game_mode::GameMap::from_persist(item_now.multiplayer.map).as_str().into()),
|
||||||
@@ -200,6 +201,7 @@ impl GameEventSequence {
|
|||||||
}),
|
}),
|
||||||
visibilities: Typed::Arr(polariton::operation::Arr {
|
visibilities: Typed::Arr(polariton::operation::Arr {
|
||||||
ty: polariton::serdes::TypePrefix::Int,
|
ty: polariton::serdes::TypePrefix::Int,
|
||||||
|
custom_ty: None,
|
||||||
items: vec![
|
items: vec![
|
||||||
Typed::Int(crate::data::game_mode::MapVisibility::from_persist(item_now.singleplayer.visibility) as _),
|
Typed::Int(crate::data::game_mode::MapVisibility::from_persist(item_now.singleplayer.visibility) as _),
|
||||||
Typed::Int(crate::data::game_mode::MapVisibility::from_persist(item_now.multiplayer.visibility) as _),
|
Typed::Int(crate::data::game_mode::MapVisibility::from_persist(item_now.multiplayer.visibility) as _),
|
||||||
@@ -207,6 +209,7 @@ impl GameEventSequence {
|
|||||||
}),
|
}),
|
||||||
modes: Typed::Arr(polariton::operation::Arr {
|
modes: Typed::Arr(polariton::operation::Arr {
|
||||||
ty: polariton::serdes::TypePrefix::Int,
|
ty: polariton::serdes::TypePrefix::Int,
|
||||||
|
custom_ty: None,
|
||||||
items: vec![
|
items: vec![
|
||||||
Typed::Int(crate::data::game_mode::GameMode::from_persist(item_now.singleplayer.mode) as _),
|
Typed::Int(crate::data::game_mode::GameMode::from_persist(item_now.singleplayer.mode) as _),
|
||||||
Typed::Int(crate::data::game_mode::GameMode::from_persist(item_now.multiplayer.mode) as _),
|
Typed::Int(crate::data::game_mode::GameMode::from_persist(item_now.multiplayer.mode) as _),
|
||||||
@@ -214,6 +217,7 @@ impl GameEventSequence {
|
|||||||
}),
|
}),
|
||||||
auto_heals: Typed::Arr(polariton::operation::Arr {
|
auto_heals: Typed::Arr(polariton::operation::Arr {
|
||||||
ty: polariton::serdes::TypePrefix::Bool,
|
ty: polariton::serdes::TypePrefix::Bool,
|
||||||
|
custom_ty: None,
|
||||||
items: vec![
|
items: vec![
|
||||||
Typed::Bool(item_now.singleplayer.auto_heal),
|
Typed::Bool(item_now.singleplayer.auto_heal),
|
||||||
Typed::Bool(item_now.multiplayer.auto_heal),
|
Typed::Bool(item_now.multiplayer.auto_heal),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ impl super::ChatUser for UserData {
|
|||||||
log::info!("User is subscribed to channels {:?}", channels);
|
log::info!("User is subscribed to channels {:?}", channels);
|
||||||
Ok(polariton::operation::Typed::Arr(polariton::operation::Arr {
|
Ok(polariton::operation::Typed::Arr(polariton::operation::Arr {
|
||||||
ty: polariton::serdes::TypePrefix::HashMap, // hashtable
|
ty: polariton::serdes::TypePrefix::HashMap, // hashtable
|
||||||
|
custom_ty: None,
|
||||||
items: channels.into_iter().map(|name| crate::data::channel::ChatChannelInfo {
|
items: channels.into_iter().map(|name| crate::data::channel::ChatChannelInfo {
|
||||||
channel_name: name,
|
channel_name: name,
|
||||||
members: vec![
|
members: vec![
|
||||||
@@ -103,6 +104,7 @@ impl super::ChatUser for UserData {
|
|||||||
})?;
|
})?;
|
||||||
Ok(polariton::operation::Typed::Arr(polariton::operation::Arr {
|
Ok(polariton::operation::Typed::Arr(polariton::operation::Arr {
|
||||||
ty: polariton::serdes::TypePrefix::Str,
|
ty: polariton::serdes::TypePrefix::Str,
|
||||||
|
custom_ty: None,
|
||||||
items: sanctions.into_iter().map(|x| {
|
items: sanctions.into_iter().map(|x| {
|
||||||
let data = crate::data::sanction::SanctionJson {
|
let data = crate::data::sanction::SanctionJson {
|
||||||
type_: crate::data::sanction::SanctionType::from_db(x.descriptor),
|
type_: crate::data::sanction::SanctionType::from_db(x.descriptor),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ mod inventory;
|
|||||||
pub use inventory::{UnlockedParts, UnlockOverride};
|
pub use inventory::{UnlockedParts, UnlockOverride};
|
||||||
|
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserAuthInfo, UserLoginInfo, 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, SocialUserC, CurrencyType, CurrencyOp, MatchRewards, SingleplayerUser, PurchaseResult, FactoryUser, FriendInviteReturn, FriendData, FriendInviteStatus};
|
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserAuthInfo, UserLoginInfo, 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, SocialUserC, CurrencyType, CurrencyOp, MatchRewards, SingleplayerUser, PurchaseResult, FactoryUser, FriendInviteReturn, FriendData, FriendInviteStatus, SocialInfo};
|
||||||
|
|
||||||
pub mod intercom;
|
pub mod intercom;
|
||||||
pub use intercom::generate_token as generate_intercom_token;
|
pub use intercom::generate_token as generate_intercom_token;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ impl super::SocialUser for UserData {
|
|||||||
format!("Failed to retrieve friends: {}", e),
|
format!("Failed to retrieve friends: {}", e),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let friend_ids: Vec<i32> = friends.iter().map(|(_, user)| user.id).collect();
|
let friend_ids = friends.iter().map(|(_, user)| user.id);
|
||||||
let friend_avatars = self.db.user_auxs_by_user_ids_and_descriptor(friend_ids, oj_rc_database::schema::user_aux::Descriptor::AvatarId).await
|
let friend_avatars = self.db.user_auxs_by_user_ids_and_descriptor(friend_ids, oj_rc_database::schema::user_aux::Descriptor::AvatarId).await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("Failed to retrieve friend avatars for user {} : {}", self.account.id, e);
|
log::error!("Failed to retrieve friend avatars for user {} : {}", self.account.id, e);
|
||||||
@@ -81,6 +81,37 @@ impl super::SocialUser for UserData {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn list_social_info(&self, public_ids: &[String]) -> Result<Vec<super::SocialInfo>, polariton_server::operations::SimpleOpError> {
|
||||||
|
let users = self.db.users_by_public_id(public_ids.iter()).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve friend avatars for user {} : {}", self.account.id, e);
|
||||||
|
polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::SocialErrorCode::DatabaseError as i16,
|
||||||
|
format!("Failed to retrieve friend avatars: {}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let user_ids = users.iter().map(|user| user.id);
|
||||||
|
let user_avatars = self.db.user_auxs_by_user_ids_and_descriptor(user_ids, oj_rc_database::schema::user_aux::Descriptor::AvatarId).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve friend avatars for user {} : {}", self.account.id, e);
|
||||||
|
polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::SocialErrorCode::DatabaseError as i16,
|
||||||
|
format!("Failed to retrieve friend avatars: {}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let avatar_map: std::collections::HashMap<i32, u32> = user_avatars.iter()
|
||||||
|
.filter_map(|avatar| avatar.data.parse().ok().map(|avatar_id| (avatar.user_id, avatar_id)))
|
||||||
|
.collect();
|
||||||
|
Ok(users.iter()
|
||||||
|
.map(|user| super::SocialInfo {
|
||||||
|
public_id: user.public_id.clone(),
|
||||||
|
display_name: user.display_name.clone(),
|
||||||
|
avatar_id: avatar_map.get(&user.id).and_then(|&avatar_id| if avatar_id == u32::MAX { None } else { Some(avatar_id as i32) }),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async fn has_unclaimed_match_rewards(&self) -> Result<bool, polariton_server::operations::SimpleOpError> {
|
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
|
let count = self.db.count_score_by_user_id_and_claimed(self.account.id, false).await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
|
|||||||
@@ -458,6 +458,7 @@ pub trait SocialUser: Send + Sync {
|
|||||||
async fn cancel_friend(&self, username: String) -> Result<bool, polariton_server::operations::SimpleOpError>;
|
async fn cancel_friend(&self, username: String) -> Result<bool, polariton_server::operations::SimpleOpError>;
|
||||||
async fn remove_friend(&self, username: String) -> Result<bool, polariton_server::operations::SimpleOpError>;
|
async fn remove_friend(&self, username: String) -> Result<bool, polariton_server::operations::SimpleOpError>;
|
||||||
async fn list_friends(&self) -> Result<Vec<FriendData>, polariton_server::operations::SimpleOpError>;
|
async fn list_friends(&self) -> Result<Vec<FriendData>, polariton_server::operations::SimpleOpError>;
|
||||||
|
async fn list_social_info(&self, public_ids: &[String]) -> Result<Vec<SocialInfo>, polariton_server::operations::SimpleOpError>;
|
||||||
async fn has_unclaimed_match_rewards(&self) -> Result<bool, polariton_server::operations::SimpleOpError>;
|
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 get_unclaimed_match_rewards(&self) -> Result<MatchRewards, polariton_server::operations::SimpleOpError>;
|
||||||
async fn claim_match_rewards(&self) -> Result<bool, polariton_server::operations::SimpleOpError>;
|
async fn claim_match_rewards(&self) -> Result<bool, polariton_server::operations::SimpleOpError>;
|
||||||
@@ -498,6 +499,13 @@ pub struct FriendData {
|
|||||||
pub avatar_id: u32,
|
pub avatar_id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SocialInfo {
|
||||||
|
pub public_id: String,
|
||||||
|
pub display_name: String,
|
||||||
|
pub avatar_id: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum FriendInviteStatus {
|
pub enum FriendInviteStatus {
|
||||||
InviteSent,
|
InviteSent,
|
||||||
InvitePending,
|
InvitePending,
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ impl Database {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn users_by_public_id<'a>(&self, public_ids: impl std::iter::Iterator<Item=&'a String>) -> Result<Vec<crate::schema::user::Model>, sea_orm::DbErr> {
|
||||||
|
crate::schema::user::Entity::find()
|
||||||
|
.filter(crate::schema::user::Column::PublicId.is_in(public_ids))
|
||||||
|
.all(self.orm.as_ref())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn user_by_some_social_id(&self, public_id: String) -> Result<Option<crate::schema::user::Model>, sea_orm::DbErr> {
|
pub async fn user_by_some_social_id(&self, public_id: String) -> Result<Option<crate::schema::user::Model>, sea_orm::DbErr> {
|
||||||
let lower_public_id = public_id.to_lowercase();
|
let lower_public_id = public_id.to_lowercase();
|
||||||
crate::schema::user::Entity::find()
|
crate::schema::user::Entity::find()
|
||||||
@@ -113,7 +120,7 @@ impl Database {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn user_auxs_by_user_ids_and_descriptor(&self, user_ids: impl std::iter::IntoIterator<Item=i32>, descriptor: crate::schema::user_aux::Descriptor) -> Result<Vec<crate::schema::user_aux::Model>, sea_orm::DbErr> {
|
pub async fn user_auxs_by_user_ids_and_descriptor(&self, user_ids: impl std::iter::Iterator<Item=i32>, descriptor: crate::schema::user_aux::Descriptor) -> Result<Vec<crate::schema::user_aux::Model>, sea_orm::DbErr> {
|
||||||
crate::schema::user_aux::Entity::find()
|
crate::schema::user_aux::Entity::find()
|
||||||
.filter(crate::schema::user_aux::Column::UserId.is_in(user_ids))
|
.filter(crate::schema::user_aux::Column::UserId.is_in(user_ids))
|
||||||
.filter(crate::schema::user_aux::Column::Descriptor.eq(descriptor))
|
.filter(crate::schema::user_aux::Column::Descriptor.eq(descriptor))
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ impl BattleEnter {
|
|||||||
fn as_transmissible<C>(&self) -> Vec<(u8, polariton::operation::Typed<C>)> {
|
fn as_transmissible<C>(&self) -> Vec<(u8, polariton::operation::Typed<C>)> {
|
||||||
let player_datas = polariton::operation::Typed::Arr(polariton::operation::Arr {
|
let player_datas = polariton::operation::Typed::Arr(polariton::operation::Arr {
|
||||||
ty: polariton::serdes::TypePrefix::HashMap,
|
ty: polariton::serdes::TypePrefix::HashMap,
|
||||||
|
custom_ty: None,
|
||||||
items: self.player_datas.iter().map(|x| x.as_transmissible()).collect()
|
items: self.player_datas.iter().map(|x| x.as_transmissible()).collect()
|
||||||
});
|
});
|
||||||
let mut vec = Vec::with_capacity(11);
|
let mut vec = Vec::with_capacity(11);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ impl PlayerRankStaticInfo {
|
|||||||
(Typed::Str("subRankCount".into()), Typed::Int(self.sub_rank_count)),
|
(Typed::Str("subRankCount".into()), Typed::Int(self.sub_rank_count)),
|
||||||
(Typed::Str("subRankThresholds".into()), Typed::Arr(Arr {
|
(Typed::Str("subRankThresholds".into()), Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Int, // int
|
ty: TypePrefix::Int, // int
|
||||||
|
custom_ty: None,
|
||||||
items: self.sub_rank_thresholds.iter().map(|x| Typed::Int(*x)).collect(),
|
items: self.sub_rank_thresholds.iter().map(|x| Typed::Int(*x)).collect(),
|
||||||
})),
|
})),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -192,14 +192,17 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im
|
|||||||
let mut params = params.to_dict();
|
let mut params = params.to_dict();
|
||||||
params.insert(SKINS_KEY, Typed::Arr(Arr {
|
params.insert(SKINS_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::HashMap, // hashtable
|
ty: TypePrefix::HashMap, // hashtable
|
||||||
|
custom_ty: None,
|
||||||
items: all_skins().into_iter().map(|x| x.as_transmissible()).collect(),
|
items: all_skins().into_iter().map(|x| x.as_transmissible()).collect(),
|
||||||
}));
|
}));
|
||||||
params.insert(SPAWNS_KEY, Typed::Arr(Arr {
|
params.insert(SPAWNS_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::HashMap, // hashtable
|
ty: TypePrefix::HashMap, // hashtable
|
||||||
|
custom_ty: None,
|
||||||
items: all_spawns().into_iter().map(|x| x.as_transmissible()).collect(),
|
items: all_spawns().into_iter().map(|x| x.as_transmissible()).collect(),
|
||||||
}));
|
}));
|
||||||
params.insert(DEATHS_KEY, Typed::Arr(Arr {
|
params.insert(DEATHS_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::HashMap, // hashtable
|
ty: TypePrefix::HashMap, // hashtable
|
||||||
|
custom_ty: None,
|
||||||
items: all_deaths().into_iter().map(|x| x.as_transmissible()).collect(),
|
items: all_deaths().into_iter().map(|x| x.as_transmissible()).collect(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ pub(super) fn cube_awards_provider() -> SimpleFunc<206, crate::UserTy, impl (Fn(
|
|||||||
let mut params = params.to_dict();
|
let mut params = params.to_dict();
|
||||||
params.insert(PARAM_KEY, Typed::Arr(Arr {
|
params.insert(PARAM_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Str, // str
|
ty: TypePrefix::Str, // str
|
||||||
|
custom_ty: None,
|
||||||
items: Vec::default(),
|
items: Vec::default(),
|
||||||
}));
|
}));
|
||||||
Ok(params.into())
|
Ok(params.into())
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ impl SimpleOperation<()> for ItemBundleBuyer {
|
|||||||
let purchase_result = user_info.apply_purchase(transaction).await?;
|
let purchase_result = user_info.apply_purchase(transaction).await?;
|
||||||
let new_cubes = polariton::operation::Typed::Arr(polariton::operation::Arr {
|
let new_cubes = polariton::operation::Typed::Arr(polariton::operation::Arr {
|
||||||
ty: polariton::serdes::TypePrefix::Str,
|
ty: polariton::serdes::TypePrefix::Str,
|
||||||
|
custom_ty: None,
|
||||||
items: purchase_result.cube_awards.into_keys()
|
items: purchase_result.cube_awards.into_keys()
|
||||||
.map(|k| polariton::operation::Typed::Str(k.into()))
|
.map(|k| polariton::operation::Typed::Str(k.into()))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ pub(super) fn owned_cosmetics_provider() -> SimpleFunc<23, crate::UserTy, impl (
|
|||||||
let mut params = params.to_dict();
|
let mut params = params.to_dict();
|
||||||
params.insert(PARAM_KEY, Typed::Arr(Arr {
|
params.insert(PARAM_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Str, // str
|
ty: TypePrefix::Str, // str
|
||||||
|
custom_ty: None,
|
||||||
items: vec![Typed::Str("1".into())],
|
items: vec![Typed::Str("1".into())],
|
||||||
}));
|
}));
|
||||||
Ok(params.into())
|
Ok(params.into())
|
||||||
@@ -19,6 +20,7 @@ pub(super) fn selected_cosmetics_provider() -> SimpleFunc<21, crate::UserTy, imp
|
|||||||
let mut params = params.to_dict();
|
let mut params = params.to_dict();
|
||||||
params.insert(PARAM_KEY, Typed::Arr(Arr {
|
params.insert(PARAM_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Str, // str
|
ty: TypePrefix::Str, // str
|
||||||
|
custom_ty: None,
|
||||||
items: vec![Typed::Str("1".into())],
|
items: vec![Typed::Str("1".into())],
|
||||||
}));
|
}));
|
||||||
Ok(params.into())
|
Ok(params.into())
|
||||||
|
|||||||
@@ -23,12 +23,14 @@ pub(super) fn player_data_provider() -> SimpleFunc<61, crate::UserTy, impl (Fn(P
|
|||||||
params.insert(CONTROL_OPTIONS_KEY, ControlOptions { vertical_strafing: false, sideways_driving: false, tracks_turn_on_spot: false, }.as_transmissible());
|
params.insert(CONTROL_OPTIONS_KEY, ControlOptions { vertical_strafing: false, sideways_driving: false, tracks_turn_on_spot: false, }.as_transmissible());
|
||||||
params.insert(WEAPON_ORDER_KEY, Typed::Arr(Arr {
|
params.insert(WEAPON_ORDER_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Int, // int
|
ty: TypePrefix::Int, // int
|
||||||
|
custom_ty: None,
|
||||||
items: vec![
|
items: vec![
|
||||||
Typed::Int(0),
|
Typed::Int(0),
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
params.insert(ITEM_CATEGORY_KEY, Typed::Arr(Arr {
|
params.insert(ITEM_CATEGORY_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Int, // int
|
ty: TypePrefix::Int, // int
|
||||||
|
custom_ty: None,
|
||||||
items: vec![
|
items: vec![
|
||||||
Typed::Int(ItemCategory::Wheel.but_bigger()),
|
Typed::Int(ItemCategory::Wheel.but_bigger()),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub(super) fn robot_sanction_provider() -> SimpleFunc<174, crate::UserTy, impl (
|
|||||||
}
|
}
|
||||||
params.insert(SANCTION_JSONS_PARAM_KEY, Typed::Arr(Arr {
|
params.insert(SANCTION_JSONS_PARAM_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Str, // str
|
ty: TypePrefix::Str, // str
|
||||||
|
custom_ty: None,
|
||||||
items: Vec::default(),
|
items: Vec::default(),
|
||||||
}));
|
}));
|
||||||
Ok(params.into())
|
Ok(params.into())
|
||||||
@@ -23,6 +24,7 @@ pub(super) fn all_robot_sanctions_provider() -> SimpleFunc<176, crate::UserTy, i
|
|||||||
let mut params = params.to_dict();
|
let mut params = params.to_dict();
|
||||||
params.insert(SANCTION_JSONS_PARAM_KEY, Typed::Arr(Arr {
|
params.insert(SANCTION_JSONS_PARAM_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::Str, // str
|
ty: TypePrefix::Str, // str
|
||||||
|
custom_ty: None,
|
||||||
items: Vec::default(),
|
items: Vec::default(),
|
||||||
}));
|
}));
|
||||||
Ok(params.into())
|
Ok(params.into())
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub(super) fn steam_promos_provider() -> Immediate<CODE, crate::UserTy> {
|
|||||||
let mut params = std::collections::HashMap::with_capacity(4);
|
let mut params = std::collections::HashMap::with_capacity(4);
|
||||||
params.insert(PROMO_LIST_PARAM_KEY, Typed::Arr(polariton::operation::Arr {
|
params.insert(PROMO_LIST_PARAM_KEY, Typed::Arr(polariton::operation::Arr {
|
||||||
ty: polariton::serdes::TypePrefix::Str,
|
ty: polariton::serdes::TypePrefix::Str,
|
||||||
|
custom_ty: None,
|
||||||
items: Vec::default(),
|
items: Vec::default(),
|
||||||
}));
|
}));
|
||||||
params.insert(STEAM_PROMOS_PARAM_KEY, Typed::Str("{}".into()));
|
params.insert(STEAM_PROMOS_PARAM_KEY, Typed::Str("{}".into()));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum CustomType {
|
pub enum CustomType {
|
||||||
FriendInfo(super::friend::FriendInfo), // TODO actually serialise
|
FriendInfo(super::friend::FriendInfo),
|
||||||
|
PlatoonMember(super::platoon::PlatoonMemberInfo),
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8,7 +9,8 @@ impl CustomType {
|
|||||||
fn custom_ty(&self) -> u8 {
|
fn custom_ty(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
Self::FriendInfo(_) => 0,
|
Self::FriendInfo(_) => 0,
|
||||||
Self::Unknown => 1,
|
Self::PlatoonMember(_) => 1,
|
||||||
|
Self::Unknown => u8::MAX,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,31 +18,27 @@ impl CustomType {
|
|||||||
pub struct CustomTypeSerdes;
|
pub struct CustomTypeSerdes;
|
||||||
|
|
||||||
impl polariton::serdes::CustomSerdes<CustomType> for CustomTypeSerdes {
|
impl polariton::serdes::CustomSerdes<CustomType> for CustomTypeSerdes {
|
||||||
fn dump(c: &CustomType, w: &mut dyn std::io::Write) -> std::io::Result<usize> {
|
fn custom_ty(c: &CustomType) -> u8 {
|
||||||
w.write_all(&[c.custom_ty()])?;
|
c.custom_ty()
|
||||||
let mut buf = Vec::new();
|
}
|
||||||
|
|
||||||
|
fn dump_inner(c: &CustomType, w: &mut dyn std::io::Write) -> std::io::Result<usize> {
|
||||||
let total_written_len = match c {
|
let total_written_len = match c {
|
||||||
CustomType::FriendInfo(friend) => {
|
CustomType::FriendInfo(friend) => {
|
||||||
friend.dump(&mut std::io::Cursor::new(&mut buf))?
|
friend.dump(w)?
|
||||||
|
},
|
||||||
|
CustomType::PlatoonMember(member) => {
|
||||||
|
member.dump(w)?
|
||||||
},
|
},
|
||||||
CustomType::Unknown => 0,
|
CustomType::Unknown => 0,
|
||||||
};
|
};
|
||||||
w.write_all(&(total_written_len as i16).to_be_bytes())?;
|
Ok(total_written_len)
|
||||||
w.write_all(&buf)?;
|
|
||||||
Ok(3 + total_written_len)
|
|
||||||
/*let payload = vec![ // FIXME don't manually serialize
|
|
||||||
0u8, // byte custom type
|
|
||||||
0u8, 5u8, // short custom object size
|
|
||||||
3u8, 0u8, 0u8, 0u8, 0u8, // content
|
|
||||||
];*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(r: &mut dyn std::io::Read) -> std::io::Result<CustomType> {
|
fn parse_inner(ty: u8, r: &mut dyn std::io::Read) -> std::io::Result<CustomType> {
|
||||||
let mut buf = [0u8; 3];
|
match ty {
|
||||||
r.read_exact(&mut buf)?;
|
|
||||||
// TODO only read up up to size
|
|
||||||
match buf[0] {
|
|
||||||
0 => super::friend::FriendInfo::parse(r).map(CustomType::FriendInfo),
|
0 => super::friend::FriendInfo::parse(r).map(CustomType::FriendInfo),
|
||||||
|
1 => super::platoon::PlatoonMemberInfo::parse(r).map(CustomType::PlatoonMember),
|
||||||
_ => Ok(CustomType::Unknown),
|
_ => Ok(CustomType::Unknown),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ pub mod friend;
|
|||||||
pub mod clan_invite;
|
pub mod clan_invite;
|
||||||
pub mod clan;
|
pub mod clan;
|
||||||
pub mod custom;
|
pub mod custom;
|
||||||
|
pub mod platoon;
|
||||||
|
|||||||
72
rc_social_room/src/data/platoon.rs
Normal file
72
rc_social_room/src/data/platoon.rs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PlatoonMemberInfo {
|
||||||
|
pub public_id: String,
|
||||||
|
pub display_name: String,
|
||||||
|
pub status: MemberStatus,
|
||||||
|
pub added: i64, // timestamp
|
||||||
|
pub avatar_id: i32,
|
||||||
|
pub use_custom_avatar: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatoonMemberInfo {
|
||||||
|
pub(super) fn dump(&self, w: &mut dyn std::io::Write) -> std::io::Result<usize> {
|
||||||
|
let mut total = 1 + 8 + 4 + 1;
|
||||||
|
total += oj_rc_core::data::write_str_for_binreader(&self.public_id, w)?;
|
||||||
|
total += oj_rc_core::data::write_str_for_binreader(&self.display_name, w)?;
|
||||||
|
w.write_all(&[self.status.as_u8()])?;
|
||||||
|
w.write_all(&self.added.to_be_bytes())?;
|
||||||
|
w.write_all(&self.avatar_id.to_be_bytes())?;
|
||||||
|
w.write_all(&[self.use_custom_avatar as u8])?;
|
||||||
|
Ok(total)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn parse(r: &mut dyn std::io::Read) -> std::io::Result<Self> {
|
||||||
|
let public_id = oj_rc_core::data::read_str_for_binwriter(r)?;
|
||||||
|
let display_name = oj_rc_core::data::read_str_for_binwriter(r)?;
|
||||||
|
let mut buf = [0u8; 1 + 8 + 4 + 1];
|
||||||
|
r.read_exact(&mut buf)?;
|
||||||
|
let status = MemberStatus::from_u8(buf[0]).ok_or_else(|| std::io::Error::other(format!("Invalid platoon member status {}", buf[0])))?;
|
||||||
|
let added = i64::from_be_bytes([
|
||||||
|
buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8],
|
||||||
|
]);
|
||||||
|
let avatar_id = i32::from_be_bytes([
|
||||||
|
buf[9], buf[10], buf[11], buf[12],
|
||||||
|
]);
|
||||||
|
let use_custom_avatar = buf[13] != 0;
|
||||||
|
Ok(Self {
|
||||||
|
public_id,
|
||||||
|
display_name,
|
||||||
|
status,
|
||||||
|
added,
|
||||||
|
avatar_id,
|
||||||
|
use_custom_avatar,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum MemberStatus {
|
||||||
|
Invited = 0,
|
||||||
|
Ready = 1,
|
||||||
|
InQueue = 2,
|
||||||
|
InBattle = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemberStatus {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_u8(num: u8) -> Option<Self> {
|
||||||
|
match num {
|
||||||
|
0 => Some(Self::Invited),
|
||||||
|
1 => Some(Self::Ready),
|
||||||
|
2 => Some(Self::InQueue),
|
||||||
|
3 => Some(Self::InBattle),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn as_u8(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,3 +4,11 @@ pub mod friend_invite_declined;
|
|||||||
pub mod friend_invite_cancelled;
|
pub mod friend_invite_cancelled;
|
||||||
pub mod friend_removed;
|
pub mod friend_removed;
|
||||||
pub mod friend_status;
|
pub mod friend_status;
|
||||||
|
pub mod platoon_invite_received;
|
||||||
|
pub mod platoon_member_added;
|
||||||
|
pub mod platoon_member_left;
|
||||||
|
pub mod platoon_member_update;
|
||||||
|
pub mod platoon_removed;
|
||||||
|
pub mod platoon_leader_changed;
|
||||||
|
pub mod platoon_member_kick;
|
||||||
|
pub mod platoon_member_avatar_update;
|
||||||
|
|||||||
32
rc_social_room/src/events/platoon_invite_received.rs
Normal file
32
rc_social_room/src/events/platoon_invite_received.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PlatoonInviteReceived {
|
||||||
|
pub inviter_public_id: String,
|
||||||
|
pub inviter_display_name: String,
|
||||||
|
pub avatar_id: Option<i32>, // None when using custom avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatoonInviteReceived {
|
||||||
|
pub const CODE: u8 = 10;
|
||||||
|
|
||||||
|
pub fn as_event_params<C>(&self) -> polariton::operation::ParameterTable<C> {
|
||||||
|
let mut params = std::collections::HashMap::with_capacity(4);
|
||||||
|
params.insert(19, polariton::operation::Typed::Str(self.inviter_public_id.clone().into()));
|
||||||
|
params.insert(75, polariton::operation::Typed::Str(self.inviter_display_name.clone().into()));
|
||||||
|
params.insert(13, polariton::operation::Typed::Bool(self.avatar_id.is_none()));
|
||||||
|
params.insert(14, polariton::operation::Typed::Int(self.avatar_id.unwrap_or_default()));
|
||||||
|
params.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <C: Send + 'static> polariton_server::events::IntoEvent<C> for PlatoonInviteReceived {
|
||||||
|
const CHANNEL: u8 = 0;
|
||||||
|
const ENCRYPT: bool = true;
|
||||||
|
const RELIABLE: bool = true;
|
||||||
|
|
||||||
|
fn into_event(self) -> polariton::operation::Event<C> {
|
||||||
|
polariton::operation::Event {
|
||||||
|
code: Self::CODE,
|
||||||
|
params: self.as_event_params(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
rc_social_room/src/events/platoon_leader_changed.rs
Normal file
29
rc_social_room/src/events/platoon_leader_changed.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PlatoonLeaderChanged {
|
||||||
|
pub leader_public_id: String,
|
||||||
|
pub leader_display_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatoonLeaderChanged {
|
||||||
|
pub const CODE: u8 = 16;
|
||||||
|
|
||||||
|
pub fn as_event_params<C>(&self) -> polariton::operation::ParameterTable<C> {
|
||||||
|
let mut params = std::collections::HashMap::with_capacity(4);
|
||||||
|
params.insert(17, polariton::operation::Typed::Str(self.leader_public_id.clone().into()));
|
||||||
|
params.insert(75, polariton::operation::Typed::Str(self.leader_display_name.clone().into()));
|
||||||
|
params.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <C: Send + 'static> polariton_server::events::IntoEvent<C> for PlatoonLeaderChanged {
|
||||||
|
const CHANNEL: u8 = 0;
|
||||||
|
const ENCRYPT: bool = true;
|
||||||
|
const RELIABLE: bool = true;
|
||||||
|
|
||||||
|
fn into_event(self) -> polariton::operation::Event<C> {
|
||||||
|
polariton::operation::Event {
|
||||||
|
code: Self::CODE,
|
||||||
|
params: self.as_event_params(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
rc_social_room/src/events/platoon_member_added.rs
Normal file
27
rc_social_room/src/events/platoon_member_added.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PlatoonNewMember {
|
||||||
|
pub new_member: crate::data::platoon::PlatoonMemberInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatoonNewMember {
|
||||||
|
pub const CODE: u8 = 17;
|
||||||
|
|
||||||
|
pub fn as_event_params(&self) -> polariton::operation::ParameterTable<crate::data::custom::CustomType> {
|
||||||
|
let mut params = std::collections::HashMap::with_capacity(1);
|
||||||
|
params.insert(15, polariton::operation::Typed::Custom(crate::data::custom::CustomType::PlatoonMember(self.new_member.clone())));
|
||||||
|
params.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl polariton_server::events::IntoEvent<crate::data::custom::CustomType> for PlatoonNewMember {
|
||||||
|
const CHANNEL: u8 = 0;
|
||||||
|
const ENCRYPT: bool = true;
|
||||||
|
const RELIABLE: bool = true;
|
||||||
|
|
||||||
|
fn into_event(self) -> polariton::operation::Event<crate::data::custom::CustomType> {
|
||||||
|
polariton::operation::Event {
|
||||||
|
code: Self::CODE,
|
||||||
|
params: self.as_event_params(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
rc_social_room/src/events/platoon_member_avatar_update.rs
Normal file
30
rc_social_room/src/events/platoon_member_avatar_update.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PlatoonMemberAvatarChanged {
|
||||||
|
pub member_public_id: String,
|
||||||
|
pub avatar_id: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatoonMemberAvatarChanged {
|
||||||
|
pub const CODE: u8 = 14;
|
||||||
|
|
||||||
|
pub fn as_event_params<C>(&self) -> polariton::operation::ParameterTable<C> {
|
||||||
|
let mut params = std::collections::HashMap::with_capacity(2);
|
||||||
|
params.insert(1, polariton::operation::Typed::Str(self.member_public_id.clone().into()));
|
||||||
|
params.insert(13, polariton::operation::Typed::Bool(self.avatar_id.is_none()));
|
||||||
|
params.insert(14, polariton::operation::Typed::Int(self.avatar_id.unwrap_or_default()));
|
||||||
|
params.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <C: Send + 'static> polariton_server::events::IntoEvent<C> for PlatoonMemberAvatarChanged {
|
||||||
|
const CHANNEL: u8 = 0;
|
||||||
|
const ENCRYPT: bool = true;
|
||||||
|
const RELIABLE: bool = true;
|
||||||
|
|
||||||
|
fn into_event(self) -> polariton::operation::Event<C> {
|
||||||
|
polariton::operation::Event {
|
||||||
|
code: Self::CODE,
|
||||||
|
params: self.as_event_params(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
rc_social_room/src/events/platoon_member_kick.rs
Normal file
23
rc_social_room/src/events/platoon_member_kick.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PlatoonMemberKick;
|
||||||
|
|
||||||
|
impl PlatoonMemberKick {
|
||||||
|
pub const CODE: u8 = 12;
|
||||||
|
|
||||||
|
pub fn as_event_params<C>(&self) -> polariton::operation::ParameterTable<C> {
|
||||||
|
polariton::operation::ParameterTable::<C>::with_capacity(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <C: Send + 'static> polariton_server::events::IntoEvent<C> for PlatoonMemberKick {
|
||||||
|
const CHANNEL: u8 = 0;
|
||||||
|
const ENCRYPT: bool = true;
|
||||||
|
const RELIABLE: bool = true;
|
||||||
|
|
||||||
|
fn into_event(self) -> polariton::operation::Event<C> {
|
||||||
|
polariton::operation::Event {
|
||||||
|
code: Self::CODE,
|
||||||
|
params: self.as_event_params(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
rc_social_room/src/events/platoon_member_left.rs
Normal file
29
rc_social_room/src/events/platoon_member_left.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PlatoonMemberLeft {
|
||||||
|
pub member_public_id: String,
|
||||||
|
pub member_display_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatoonMemberLeft {
|
||||||
|
pub const CODE: u8 = 11;
|
||||||
|
|
||||||
|
pub fn as_event_params<C>(&self) -> polariton::operation::ParameterTable<C> {
|
||||||
|
let mut params = std::collections::HashMap::with_capacity(2);
|
||||||
|
params.insert(1, polariton::operation::Typed::Str(self.member_public_id.clone().into()));
|
||||||
|
params.insert(75, polariton::operation::Typed::Str(self.member_display_name.clone().into()));
|
||||||
|
params.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <C: Send + 'static> polariton_server::events::IntoEvent<C> for PlatoonMemberLeft {
|
||||||
|
const CHANNEL: u8 = 0;
|
||||||
|
const ENCRYPT: bool = true;
|
||||||
|
const RELIABLE: bool = true;
|
||||||
|
|
||||||
|
fn into_event(self) -> polariton::operation::Event<C> {
|
||||||
|
polariton::operation::Event {
|
||||||
|
code: Self::CODE,
|
||||||
|
params: self.as_event_params(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
rc_social_room/src/events/platoon_member_update.rs
Normal file
31
rc_social_room/src/events/platoon_member_update.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PlatoonMemberStatusUpdate {
|
||||||
|
pub member_public_id: String,
|
||||||
|
pub member_display_name: String,
|
||||||
|
pub member_status: crate::data::platoon::MemberStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatoonMemberStatusUpdate {
|
||||||
|
pub const CODE: u8 = 18;
|
||||||
|
|
||||||
|
pub fn as_event_params<C>(&self) -> polariton::operation::ParameterTable<C> {
|
||||||
|
let mut params = std::collections::HashMap::with_capacity(2);
|
||||||
|
params.insert(1, polariton::operation::Typed::Str(self.member_public_id.clone().into()));
|
||||||
|
params.insert(75, polariton::operation::Typed::Str(self.member_display_name.clone().into()));
|
||||||
|
params.insert(3, polariton::operation::Typed::Int(self.member_status.as_u8() as i32));
|
||||||
|
params.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <C: Send + 'static> polariton_server::events::IntoEvent<C> for PlatoonMemberStatusUpdate {
|
||||||
|
const CHANNEL: u8 = 0;
|
||||||
|
const ENCRYPT: bool = true;
|
||||||
|
const RELIABLE: bool = true;
|
||||||
|
|
||||||
|
fn into_event(self) -> polariton::operation::Event<C> {
|
||||||
|
polariton::operation::Event {
|
||||||
|
code: Self::CODE,
|
||||||
|
params: self.as_event_params(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
rc_social_room/src/events/platoon_removed.rs
Normal file
27
rc_social_room/src/events/platoon_removed.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PlatoonDisbanded {
|
||||||
|
pub platoon_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatoonDisbanded {
|
||||||
|
pub const CODE: u8 = 13;
|
||||||
|
|
||||||
|
pub fn as_event_params(&self) -> polariton::operation::ParameterTable<crate::data::custom::CustomType> {
|
||||||
|
let mut params = std::collections::HashMap::with_capacity(1);
|
||||||
|
params.insert(16, polariton::operation::Typed::Str(self.platoon_id.clone().into()));
|
||||||
|
params.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl polariton_server::events::IntoEvent<crate::data::custom::CustomType> for PlatoonDisbanded {
|
||||||
|
const CHANNEL: u8 = 0;
|
||||||
|
const ENCRYPT: bool = true;
|
||||||
|
const RELIABLE: bool = true;
|
||||||
|
|
||||||
|
fn into_event(self) -> polariton::operation::Event<crate::data::custom::CustomType> {
|
||||||
|
polariton::operation::Event {
|
||||||
|
code: Self::CODE,
|
||||||
|
params: self.as_event_params(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,6 +85,19 @@ async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAdd
|
|||||||
ONLINE_USERS.store(social.online_count_read().await - 1, std::sync::atomic::Ordering::SeqCst);
|
ONLINE_USERS.store(social.online_count_read().await - 1, std::sync::atomic::Ordering::SeqCst);
|
||||||
if let Ok(user_info) = user_state.user() {
|
if let Ok(user_info) = user_state.user() {
|
||||||
update_status(user_info.as_ref().as_ref()).await;
|
update_status(user_info.as_ref().as_ref()).await;
|
||||||
|
if let Some(platoon_id) = social.platoon_of_user(user_info.public_id()).await {
|
||||||
|
// send platoon leave event to others in platoon
|
||||||
|
social.remove_user_from_platoon(user_info.public_id()).await;
|
||||||
|
let remaining_users = social.users_of_platoon(&platoon_id).await;
|
||||||
|
let event = crate::events::platoon_member_left::PlatoonMemberLeft {
|
||||||
|
member_public_id: user_info.public_id().to_owned(),
|
||||||
|
member_display_name: user_info.display_name().to_owned(),
|
||||||
|
};
|
||||||
|
for rem_user in remaining_users {
|
||||||
|
social.send_event_to(&rem_user.public_id, event.clone()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(friends) = user_info.list_friends().await {
|
if let Ok(friends) = user_info.list_friends().await {
|
||||||
for friend in friends {
|
for friend in friends {
|
||||||
social.send_event_to(&friend.public_id, crate::events::friend_status::FriendStatus {
|
social.send_event_to(&friend.public_id, crate::events::friend_status::FriendStatus {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ pub(super) fn clan_info_provider<C: Send + Sync>() -> SimpleFunc<33, crate::User
|
|||||||
log::debug!("Requested info on clan {}", clan_name.string);
|
log::debug!("Requested info on clan {}", clan_name.string);
|
||||||
params.insert(MEMBERS_PARAM_KEY, Typed::Arr(Arr {
|
params.insert(MEMBERS_PARAM_KEY, Typed::Arr(Arr {
|
||||||
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
||||||
|
custom_ty: None,
|
||||||
items: vec![
|
items: vec![
|
||||||
ClanMember {
|
ClanMember {
|
||||||
username: "RE_clan_user_idk0".to_owned(),
|
username: "RE_clan_user_idk0".to_owned(),
|
||||||
|
|||||||
@@ -10,6 +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
|
||||||
|
custom_ty: None,
|
||||||
/*items: vec![
|
/*items: vec![
|
||||||
ClanInviteInfo {
|
ClanInviteInfo {
|
||||||
username: "RE_user1".to_owned(),
|
username: "RE_user1".to_owned(),
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ impl SimpleOperation<crate::data::custom::CustomType> for FriendsLister {
|
|||||||
// Typed::Custom(crate::data::custom::CustomType::FriendInfo)
|
// Typed::Custom(crate::data::custom::CustomType::FriendInfo)
|
||||||
params.insert(FRIENDS_PARAM_KEY, Typed::Arr(Arr {
|
params.insert(FRIENDS_PARAM_KEY, Typed::Arr(Arr {
|
||||||
ty: polariton::serdes::TypePrefix::Custom, // custom
|
ty: polariton::serdes::TypePrefix::Custom, // custom
|
||||||
|
custom_ty: Some(0),
|
||||||
items: friends.iter().map(|friend|
|
items: friends.iter().map(|friend|
|
||||||
Typed::Custom(crate::data::custom::CustomType::FriendInfo(crate::data::friend::FriendInfo {
|
Typed::Custom(crate::data::custom::CustomType::FriendInfo(crate::data::friend::FriendInfo {
|
||||||
status: crate::data::friend::InviteStatus::from_core(&friend.state),
|
status: crate::data::friend::InviteStatus::from_core(&friend.state),
|
||||||
@@ -56,6 +57,7 @@ impl SimpleOperation<crate::data::custom::CustomType> for FriendsLister {
|
|||||||
}));
|
}));
|
||||||
params.insert(AVATAR_PARAM_KEY, Typed::Arr(Arr {
|
params.insert(AVATAR_PARAM_KEY, Typed::Arr(Arr {
|
||||||
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
||||||
|
custom_ty: None,
|
||||||
items: friends.iter()
|
items: friends.iter()
|
||||||
.map(|friend|
|
.map(|friend|
|
||||||
AvatarInfo {
|
AvatarInfo {
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ mod friend_accept;
|
|||||||
mod friend_decline;
|
mod friend_decline;
|
||||||
mod friend_cancel;
|
mod friend_cancel;
|
||||||
mod friend_remove;
|
mod friend_remove;
|
||||||
|
mod platoon_invite_to;
|
||||||
|
mod user_can_be_platooned;
|
||||||
|
mod platoon_invite_accept;
|
||||||
|
mod platoon_invite_decline;
|
||||||
|
mod platoon_leave;
|
||||||
|
mod platoon_kick;
|
||||||
|
mod platoon_invites;
|
||||||
|
mod platoon_status;
|
||||||
|
|
||||||
use polariton_server::operations::OperationsHandler;
|
use polariton_server::operations::OperationsHandler;
|
||||||
|
|
||||||
@@ -27,7 +35,8 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy,
|
|||||||
.add(settings::settings_provider()) // TODO save settings persistently
|
.add(settings::settings_provider()) // TODO save settings persistently
|
||||||
.add(polariton_server::operations::Ack::<43, _>::default()) // get my clan info (this is equivalent to not being in a clan)
|
.add(polariton_server::operations::Ack::<43, _>::default()) // get my clan info (this is equivalent to not being in a clan)
|
||||||
.add(clan_invite::clan_invites_provider())
|
.add(clan_invite::clan_invites_provider())
|
||||||
.add(polariton_server::operations::Ack::<19, _>::default()) // get pending platoon invite (this is equivalent to having no pending invite)
|
//.add(polariton_server::operations::Ack::<19, _>::default()) // get pending platoon invite (this is equivalent to having no pending invite)
|
||||||
|
.add(platoon_invites::platoon_pending_provider(init_ctx))
|
||||||
.add(clan_info::clan_info_provider())
|
.add(clan_info::clan_info_provider())
|
||||||
.add(search_clan::search_clans_provider())
|
.add(search_clan::search_clans_provider())
|
||||||
.add(polariton_server::operations::Ack::<52, _>::default()) // validate pending season rewards (this just always needs to be ack-ed)
|
.add(polariton_server::operations::Ack::<52, _>::default()) // validate pending season rewards (this just always needs to be ack-ed)
|
||||||
@@ -44,4 +53,11 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy,
|
|||||||
.add(friend_decline::friend_decline_provider(init_ctx))
|
.add(friend_decline::friend_decline_provider(init_ctx))
|
||||||
.add(friend_cancel::friend_cancel_provider(init_ctx))
|
.add(friend_cancel::friend_cancel_provider(init_ctx))
|
||||||
.add(friend_remove::friend_remove_provider(init_ctx))
|
.add(friend_remove::friend_remove_provider(init_ctx))
|
||||||
|
.add(platoon_invite_to::platoon_invite_provider(init_ctx))
|
||||||
|
.add(user_can_be_platooned::can_invite_to_platoon_provider(init_ctx))
|
||||||
|
.add(platoon_invite_accept::platoon_accepter_provider(init_ctx))
|
||||||
|
.add(platoon_invite_decline::platoon_decliner_provider(init_ctx))
|
||||||
|
.add(platoon_leave::platoon_leave_provider(init_ctx))
|
||||||
|
.add(platoon_kick::platoon_kick_provider(init_ctx))
|
||||||
|
.add(platoon_status::platoon_update_provider(init_ctx))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
use polariton_server::operations::SimpleFunc;
|
|
||||||
use polariton::operation::{ParameterTable, Typed, Arr};
|
|
||||||
|
|
||||||
use crate::data::friend::*;
|
|
||||||
|
|
||||||
const INVITER_NAME_PARAM_KEY: u8 = 19;
|
|
||||||
const INVITER_DISPLAY_NAME_PARAM_KEY: u8 = 75;
|
|
||||||
const INVITER_CUSTOM_AVATAR_NAME_PARAM_KEY: u8 = 13;
|
|
||||||
const INVITER_AVATAR_ID_NAME_PARAM_KEY: u8 = 14;
|
|
||||||
|
|
||||||
pub(super) fn platoon_pending_provider<C: Send + Sync>() -> SimpleFunc<19, crate::UserTy, impl (Fn(ParameterTable<C>, &crate::UserTy) -> Result<ParameterTable<C>, i16>) + Sync + Sync, C> {
|
|
||||||
SimpleFunc::new(|params, _| {
|
|
||||||
let mut params = params.to_dict();
|
|
||||||
params.insert(INVITER_NAME_PARAM_KEY, Typed::Str("RE_platoon_inviter".into())));
|
|
||||||
params.insert(INVITER_DISPLAY_NAME_PARAM_KEY, Typed::Str("RE_platoon_inviter_display".into())));
|
|
||||||
params.insert(INVITER_CUSTOM_AVATAR_NAME_PARAM_KEY, Typed::Bool(false.into())));
|
|
||||||
params.insert(INVITER_AVATAR_ID_NAME_PARAM_KEY, Typed::Int(1)));
|
|
||||||
Ok(params.into())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
93
rc_social_room/src/operations/platoon_invite_accept.rs
Normal file
93
rc_social_room/src/operations/platoon_invite_accept.rs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
|
const CODE: u8 = 11;
|
||||||
|
|
||||||
|
const PLATOON_ID_PARAM_KEY: u8 = 16; // str; out
|
||||||
|
const PLATOON_LEADER_PARAM_KEY: u8 = 17; // str; out
|
||||||
|
const PLATOON_MEMBERS_PARAM_KEY: u8 = 7; // array of custom (platoon member); out
|
||||||
|
|
||||||
|
pub(super) struct PlatoonInviteAccepter {
|
||||||
|
social: std::sync::Arc<crate::SocialMesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SimpleOperation<crate::data::custom::CustomType> for PlatoonInviteAccepter {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, mut params: ParameterTable<crate::data::custom::CustomType>, user: &Self::User) -> Result<ParameterTable<crate::data::custom::CustomType>, SimpleOpError> {
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let my_public_id = user_info.public_id();
|
||||||
|
if let Some(platoon_id) = self.social.platoon_of_user(my_public_id).await {
|
||||||
|
log::debug!("User {} accepted invite to platoon {}", my_public_id, platoon_id);
|
||||||
|
let members = self.social.users_of_platoon(&platoon_id).await;
|
||||||
|
let member_pub_ids: Vec<String> = members.iter().map(|m| m.public_id.clone()).collect();
|
||||||
|
let social_infos = user_info.list_social_info(&member_pub_ids).await?;
|
||||||
|
let plat_members: Vec<crate::data::platoon::PlatoonMemberInfo> = members.iter()
|
||||||
|
.map(|member| {
|
||||||
|
let soc = social_infos.iter()
|
||||||
|
.find(|soc| soc.public_id == member.public_id)
|
||||||
|
.unwrap();
|
||||||
|
crate::data::platoon::PlatoonMemberInfo {
|
||||||
|
public_id: soc.public_id.clone(),
|
||||||
|
display_name: soc.display_name.clone(),
|
||||||
|
status: member.status,
|
||||||
|
added: member.timestamp,
|
||||||
|
avatar_id: 0,
|
||||||
|
use_custom_avatar: true, // the game client has a bug that makes it need a custom avatar here
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
params.insert(PLATOON_ID_PARAM_KEY, Typed::Str(platoon_id.into()));
|
||||||
|
params.insert(PLATOON_LEADER_PARAM_KEY, Typed::Str(
|
||||||
|
members.first()
|
||||||
|
.map(|p| p.public_id.clone())
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into()
|
||||||
|
));
|
||||||
|
params.insert(PLATOON_MEMBERS_PARAM_KEY, Typed::Arr(polariton::operation::Arr {
|
||||||
|
ty: polariton::serdes::TypePrefix::Custom,
|
||||||
|
custom_ty: Some(1),
|
||||||
|
items: plat_members.iter()
|
||||||
|
.map(|x| Typed::Custom(crate::data::custom::CustomType::PlatoonMember(x.to_owned())))
|
||||||
|
.collect(),
|
||||||
|
}));
|
||||||
|
self.social.update_user_in_platoon(my_public_id, crate::data::platoon::MemberStatus::Ready).await;
|
||||||
|
let event = crate::events::platoon_member_update::PlatoonMemberStatusUpdate {
|
||||||
|
member_public_id: my_public_id.to_owned(),
|
||||||
|
member_display_name: user_info.display_name().to_owned(),
|
||||||
|
member_status: crate::data::platoon::MemberStatus::Ready,
|
||||||
|
};
|
||||||
|
for member in members.iter() {
|
||||||
|
if member.public_id == my_public_id { continue; }
|
||||||
|
self.social.send_event_to(&member.public_id, event.clone()).await;
|
||||||
|
}
|
||||||
|
// send the correct avatars later (to workaround the game client bug)
|
||||||
|
let social = self.social.clone();
|
||||||
|
let my_id = my_public_id.to_owned();
|
||||||
|
tokio::task::spawn(async move {
|
||||||
|
tokio::time::sleep(std::time::Duration::from_millis(200)).await; // FIXME figure out a better way to run this shortly after the op has returned
|
||||||
|
for member in plat_members.iter() {
|
||||||
|
if member.public_id == my_id { continue; }
|
||||||
|
let social_info = social_infos.iter()
|
||||||
|
.find(|soc| soc.public_id == member.public_id)
|
||||||
|
.unwrap();
|
||||||
|
if social_info.avatar_id.is_none() { continue; }
|
||||||
|
let event = crate::events::platoon_member_avatar_update::PlatoonMemberAvatarChanged {
|
||||||
|
member_public_id: member.public_id.clone(),
|
||||||
|
avatar_id: social_info.avatar_id,
|
||||||
|
};
|
||||||
|
social.send_event_to(&my_id, event).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn platoon_accepter_provider(init_ctx: &crate::InitConfig) -> SimpleOpImpl<crate::data::custom::CustomType, crate::UserTy, PlatoonInviteAccepter> {
|
||||||
|
SimpleOpImpl::new(PlatoonInviteAccepter {
|
||||||
|
social: init_ctx.social.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
52
rc_social_room/src/operations/platoon_invite_decline.rs
Normal file
52
rc_social_room/src/operations/platoon_invite_decline.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
|
use polariton::operation::ParameterTable;
|
||||||
|
|
||||||
|
const CODE: u8 = 12;
|
||||||
|
|
||||||
|
pub(super) struct PlatoonInviteDecliner {
|
||||||
|
social: std::sync::Arc<crate::SocialMesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SimpleOperation<crate::data::custom::CustomType> for PlatoonInviteDecliner {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, params: ParameterTable<crate::data::custom::CustomType>, user: &Self::User) -> Result<ParameterTable<crate::data::custom::CustomType>, SimpleOpError> {
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let my_public_id = user_info.public_id();
|
||||||
|
if let Some(platoon_id) = self.social.platoon_of_user(my_public_id).await {
|
||||||
|
log::debug!("User {} declined invite to platoon {}", my_public_id, platoon_id);
|
||||||
|
let members = self.social.users_of_platoon(&platoon_id).await;
|
||||||
|
if members.len() <= 2 {
|
||||||
|
self.social.remove_platoon(&platoon_id).await;
|
||||||
|
let event = crate::events::platoon_removed::PlatoonDisbanded {
|
||||||
|
platoon_id: platoon_id.to_owned(),
|
||||||
|
};
|
||||||
|
for member in members.iter() {
|
||||||
|
if member.public_id == my_public_id { continue; }
|
||||||
|
self.social.send_event_to(&member.public_id, event.clone()).await;
|
||||||
|
}
|
||||||
|
log::debug!("Platoon {} removed", platoon_id);
|
||||||
|
} else {
|
||||||
|
self.social.remove_user_from_platoon(my_public_id).await;
|
||||||
|
let event = crate::events::platoon_member_left::PlatoonMemberLeft {
|
||||||
|
member_public_id: my_public_id.to_owned(),
|
||||||
|
member_display_name: user_info.display_name().to_owned(),
|
||||||
|
};
|
||||||
|
for member in members.iter() {
|
||||||
|
if member.public_id == my_public_id { continue; }
|
||||||
|
self.social.send_event_to(&member.public_id, event.clone()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn platoon_decliner_provider(init_ctx: &crate::InitConfig) -> SimpleOpImpl<crate::data::custom::CustomType, crate::UserTy, PlatoonInviteDecliner> {
|
||||||
|
SimpleOpImpl::new(PlatoonInviteDecliner {
|
||||||
|
social: init_ctx.social.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
158
rc_social_room/src/operations/platoon_invite_to.rs
Normal file
158
rc_social_room/src/operations/platoon_invite_to.rs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
|
const CODE: u8 = 10;
|
||||||
|
|
||||||
|
const USERNAME_PARAM_KEY: u8 = 1; // str; in
|
||||||
|
const PLATOON_ID_PARAM_KEY: u8 = 16; // str; out
|
||||||
|
const PLATOON_INVITEE_PARAM_KEY: u8 = 15; // custom (PlatoonMember); out
|
||||||
|
const AVATAR_ID_PARAM_KEY: u8 = 14; // int; out
|
||||||
|
const USE_CUSTOM_AVATAR_PARAM_KEY: u8 = 13; // bool; out
|
||||||
|
|
||||||
|
pub(super) struct PlatoonInviter {
|
||||||
|
social: std::sync::Arc<crate::SocialMesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SimpleOperation<crate::data::custom::CustomType> for PlatoonInviter {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, mut params: ParameterTable<crate::data::custom::CustomType>, user: &Self::User) -> Result<ParameterTable<crate::data::custom::CustomType>, SimpleOpError> {
|
||||||
|
if let Some(Typed::Str(username)) = params.remove(&USERNAME_PARAM_KEY) {
|
||||||
|
if let Some(platoon_id) = self.social.platoon_of_user(&username.string).await {
|
||||||
|
log::debug!("User {} is already in platoon {}", username.string, platoon_id);
|
||||||
|
return Err(SimpleOpError::with_message(
|
||||||
|
oj_rc_core::data::error_codes::SocialErrorCode::AlreadyInPlatoon as i16,
|
||||||
|
format!("User is already in platoon {}", platoon_id),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let (platoon_id, social_infos, timestamp) = if let Some(platoon_id) = self.social.platoon_of_user(user_info.public_id()).await {
|
||||||
|
// add to existing platoon
|
||||||
|
log::debug!("Inviting {} to platoon {} by user {}", username.string, platoon_id, user_info.public_id());
|
||||||
|
let members = self.social.users_of_platoon(&platoon_id).await;
|
||||||
|
let member_public_ids: Vec<String> = members.iter()
|
||||||
|
.map(|m| m.public_id.clone())
|
||||||
|
.chain([username.string.clone()])
|
||||||
|
.collect();
|
||||||
|
let social_infos = user_info.list_social_info(&member_public_ids).await?;
|
||||||
|
if social_infos.len() != members.len() + 1 {
|
||||||
|
log::debug!("Platoon members info could not be retrieved while inviting {} to platoon {}", username.string, platoon_id);
|
||||||
|
return Err(SimpleOpError::with_message(
|
||||||
|
oj_rc_core::data::error_codes::SocialErrorCode::UserDoesNotExist as i16,
|
||||||
|
"User's info could not be retrieved".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(timestamp) = self.social.add_user_to_platoon(&username.string, &platoon_id, crate::data::platoon::MemberStatus::Invited).await {
|
||||||
|
if social_infos.len() > 2 {
|
||||||
|
// send PlatoonMemberAdded event to other platoon members
|
||||||
|
let invitee_soc = social_infos.iter()
|
||||||
|
.find(|soc| soc.public_id == username.string)
|
||||||
|
.unwrap();
|
||||||
|
let event = crate::events::platoon_member_added::PlatoonNewMember {
|
||||||
|
new_member: crate::data::platoon::PlatoonMemberInfo {
|
||||||
|
public_id: invitee_soc.public_id.clone(),
|
||||||
|
display_name: invitee_soc.display_name.clone(),
|
||||||
|
status: crate::data::platoon::MemberStatus::Invited,
|
||||||
|
added: timestamp,
|
||||||
|
avatar_id: invitee_soc.avatar_id.unwrap_or_default(),
|
||||||
|
use_custom_avatar: invitee_soc.avatar_id.is_none(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// unlikely to be more than 3, so no need to run this in its own task
|
||||||
|
for social_info in social_infos.iter() {
|
||||||
|
if social_info.public_id == username.string { continue; }
|
||||||
|
if social_info.public_id == user_info.public_id() { continue; }
|
||||||
|
self.social.send_event_to(&social_info.public_id, event.clone()).await;
|
||||||
|
}
|
||||||
|
/*// send PlatoonMemberAdded event to invitee for other members
|
||||||
|
for social_info in social_infos.iter() {
|
||||||
|
if social_info.public_id == username.string { continue; }
|
||||||
|
if social_info.public_id == user_info.public_id() { continue; }
|
||||||
|
let member = members.iter()
|
||||||
|
.find(|mem| mem.public_id == social_info.public_id)
|
||||||
|
.unwrap();
|
||||||
|
let event = crate::events::platoon_member_added::PlatoonNewMember {
|
||||||
|
new_member: crate::data::platoon::PlatoonMemberInfo {
|
||||||
|
public_id: social_info.public_id.clone(),
|
||||||
|
display_name: social_info.display_name.clone(),
|
||||||
|
status: member.status,
|
||||||
|
added: member.timestamp,
|
||||||
|
avatar_id: social_info.avatar_id.unwrap_or_default(),
|
||||||
|
use_custom_avatar: social_info.avatar_id.is_none(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.social.send_event_to(&username.string, event).await;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
(platoon_id, social_infos, timestamp)
|
||||||
|
} else {
|
||||||
|
log::debug!("Failed to add user {} to platoon {} (did the platoon disband?)", username.string, platoon_id);
|
||||||
|
return Err(SimpleOpError::with_message(
|
||||||
|
oj_rc_core::data::error_codes::SocialErrorCode::UserNotPlatoonFound as i16,
|
||||||
|
"Failed to invite user to platoon".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// create new platoon
|
||||||
|
let platoon_id = format!("{}-{}", user_info.public_id(), chrono::Utc::now().timestamp());
|
||||||
|
log::debug!("Creating new platoon {} for user {} to invite {}", platoon_id, user_info.public_id(), username.string);
|
||||||
|
let social_infos = user_info.list_social_info(&[
|
||||||
|
username.string.clone(),
|
||||||
|
user_info.public_id().to_owned(),
|
||||||
|
]).await?;
|
||||||
|
if social_infos.len() != 2 {
|
||||||
|
log::debug!("User {} info could not be retrieved while creating platoon {}", username.string, platoon_id);
|
||||||
|
return Err(SimpleOpError::with_message(
|
||||||
|
oj_rc_core::data::error_codes::SocialErrorCode::UserDoesNotExist as i16,
|
||||||
|
"User's info could not be retrieved".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if self.social.create_platoon(&platoon_id, user_info.public_id()).await.is_none() {
|
||||||
|
return Err(SimpleOpError::with_message(
|
||||||
|
oj_rc_core::data::error_codes::SocialErrorCode::UserNotInPlatoon as i16,
|
||||||
|
"Failed to create platoon".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(timestamp) = self.social.add_user_to_platoon(&username.string, &platoon_id, crate::data::platoon::MemberStatus::Invited).await {
|
||||||
|
(platoon_id, social_infos, timestamp)
|
||||||
|
} else {
|
||||||
|
return Err(SimpleOpError::with_message(
|
||||||
|
oj_rc_core::data::error_codes::SocialErrorCode::UserNotPlatoonFound as i16,
|
||||||
|
"Failed to add user to new platoon".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let invitee = social_infos.iter()
|
||||||
|
.find(|soc| soc.public_id == username.string)
|
||||||
|
.unwrap();
|
||||||
|
let inviter = social_infos.iter()
|
||||||
|
.find(|soc| soc.public_id == user_info.public_id())
|
||||||
|
.unwrap();
|
||||||
|
self.social.send_event_to(&invitee.public_id, crate::events::platoon_invite_received::PlatoonInviteReceived {
|
||||||
|
inviter_public_id: user_info.public_id().to_owned(),
|
||||||
|
inviter_display_name: user_info.display_name().to_owned(),
|
||||||
|
avatar_id: inviter.avatar_id,
|
||||||
|
}).await;
|
||||||
|
params.insert(PLATOON_ID_PARAM_KEY, Typed::Str(platoon_id.into()));
|
||||||
|
params.insert(PLATOON_INVITEE_PARAM_KEY, Typed::Custom(crate::data::custom::CustomType::PlatoonMember(crate::data::platoon::PlatoonMemberInfo {
|
||||||
|
public_id: invitee.public_id.clone(),
|
||||||
|
display_name: invitee.display_name.clone(),
|
||||||
|
status: crate::data::platoon::MemberStatus::Invited,
|
||||||
|
added: timestamp,
|
||||||
|
avatar_id: invitee.avatar_id.unwrap_or_default(),
|
||||||
|
use_custom_avatar: invitee.avatar_id.is_none(),
|
||||||
|
})));
|
||||||
|
params.insert(AVATAR_ID_PARAM_KEY, Typed::Int(invitee.avatar_id.unwrap_or_default()));
|
||||||
|
params.insert(USE_CUSTOM_AVATAR_PARAM_KEY, Typed::Bool(invitee.avatar_id.is_none()));
|
||||||
|
}
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn platoon_invite_provider(init_ctx: &crate::InitConfig) -> SimpleOpImpl<crate::data::custom::CustomType, crate::UserTy, PlatoonInviter> {
|
||||||
|
SimpleOpImpl::new(PlatoonInviter {
|
||||||
|
social: init_ctx.social.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
42
rc_social_room/src/operations/platoon_invites.rs
Normal file
42
rc_social_room/src/operations/platoon_invites.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
|
//use crate::data::friend::*;
|
||||||
|
|
||||||
|
const CODE: u8 = 19;
|
||||||
|
|
||||||
|
const INVITER_NAME_PARAM_KEY: u8 = 19;
|
||||||
|
const INVITER_DISPLAY_NAME_PARAM_KEY: u8 = 75;
|
||||||
|
const INVITER_CUSTOM_AVATAR_NAME_PARAM_KEY: u8 = 13;
|
||||||
|
const INVITER_AVATAR_ID_NAME_PARAM_KEY: u8 = 14;
|
||||||
|
|
||||||
|
pub(super) struct PlatoonPendingInviter {
|
||||||
|
social: std::sync::Arc<crate::SocialMesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SimpleOperation<crate::data::custom::CustomType> for PlatoonPendingInviter {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, mut params: ParameterTable<crate::data::custom::CustomType>, user: &Self::User) -> Result<ParameterTable<crate::data::custom::CustomType>, SimpleOpError> {
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let my_public_id = user_info.public_id();
|
||||||
|
log::debug!("Checking for pending platoon invite of user {}", my_public_id);
|
||||||
|
if let Some(platoon_id) = self.social.platoon_of_user(my_public_id).await {
|
||||||
|
let members = self.social.users_of_platoon(&platoon_id).await;
|
||||||
|
let social_infos = user_info.list_social_info(&[members[0].public_id.clone()]).await?;
|
||||||
|
params.insert(INVITER_NAME_PARAM_KEY, Typed::Str(social_infos[0].public_id.clone().into()));
|
||||||
|
params.insert(INVITER_DISPLAY_NAME_PARAM_KEY, Typed::Str(social_infos[0].display_name.clone().into()));
|
||||||
|
params.insert(INVITER_CUSTOM_AVATAR_NAME_PARAM_KEY, Typed::Bool(social_infos[0].avatar_id.is_none()));
|
||||||
|
params.insert(INVITER_AVATAR_ID_NAME_PARAM_KEY, Typed::Int(social_infos[0].avatar_id.unwrap_or_default()));
|
||||||
|
}
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn platoon_pending_provider(init_ctx: &crate::InitConfig) -> SimpleOpImpl<crate::data::custom::CustomType, crate::UserTy, PlatoonPendingInviter> {
|
||||||
|
SimpleOpImpl::new(PlatoonPendingInviter {
|
||||||
|
social: init_ctx.social.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
65
rc_social_room/src/operations/platoon_kick.rs
Normal file
65
rc_social_room/src/operations/platoon_kick.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
|
const CODE: u8 = 14;
|
||||||
|
|
||||||
|
const PUBLIC_ID_PARAM_KEY: u8 = 1; // str; in
|
||||||
|
|
||||||
|
pub(super) struct PlatoonKicker {
|
||||||
|
social: std::sync::Arc<crate::SocialMesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SimpleOperation<crate::data::custom::CustomType> for PlatoonKicker {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, mut params: ParameterTable<crate::data::custom::CustomType>, user: &Self::User) -> Result<ParameterTable<crate::data::custom::CustomType>, SimpleOpError> {
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let my_public_id = user_info.public_id();
|
||||||
|
if let Some(Typed::Str(username)) = params.remove(&PUBLIC_ID_PARAM_KEY) {
|
||||||
|
if let Some(platoon_id) = self.social.platoon_of_user(&username.string).await {
|
||||||
|
let members = self.social.users_of_platoon(&platoon_id).await;
|
||||||
|
if !members.is_empty() && members[0].public_id != my_public_id {
|
||||||
|
return Err(SimpleOpError::with_message(
|
||||||
|
oj_rc_core::data::error_codes::SocialErrorCode::TheyNotPlatoonLeader as i16,
|
||||||
|
"Only platoon leader can kick members".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
log::debug!("User {} kicked from platoon {}", username.string, platoon_id);
|
||||||
|
if members.len() <= 2 {
|
||||||
|
self.social.remove_platoon(&platoon_id).await;
|
||||||
|
let event = crate::events::platoon_removed::PlatoonDisbanded {
|
||||||
|
platoon_id: platoon_id.to_owned(),
|
||||||
|
};
|
||||||
|
for member in members.iter() {
|
||||||
|
if member.public_id == my_public_id { continue; }
|
||||||
|
self.social.send_event_to(&member.public_id, event.clone()).await;
|
||||||
|
}
|
||||||
|
log::debug!("Platoon {} removed", platoon_id);
|
||||||
|
} else {
|
||||||
|
let social_infos = user_info.list_social_info(std::slice::from_ref(&username.string)).await?;
|
||||||
|
self.social.remove_user_from_platoon(&username.string).await;
|
||||||
|
self.social.send_event_to(&username.string, crate::events::platoon_member_kick::PlatoonMemberKick).await;
|
||||||
|
let event = crate::events::platoon_member_left::PlatoonMemberLeft {
|
||||||
|
member_public_id: username.string.clone(),
|
||||||
|
member_display_name: social_infos.first().map(|soc| soc.display_name.clone()).unwrap_or_else(|| username.string.clone()),
|
||||||
|
};
|
||||||
|
for member in members.iter() {
|
||||||
|
if member.public_id == my_public_id { continue; }
|
||||||
|
self.social.send_event_to(&member.public_id, event.clone()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::warn!("Cannot kick {} from platoon because they are not in one", username.string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn platoon_kick_provider(init_ctx: &crate::InitConfig) -> SimpleOpImpl<crate::data::custom::CustomType, crate::UserTy, PlatoonKicker> {
|
||||||
|
SimpleOpImpl::new(PlatoonKicker {
|
||||||
|
social: init_ctx.social.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
65
rc_social_room/src/operations/platoon_leave.rs
Normal file
65
rc_social_room/src/operations/platoon_leave.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
|
use polariton::operation::ParameterTable;
|
||||||
|
|
||||||
|
const CODE: u8 = 13;
|
||||||
|
|
||||||
|
pub(super) struct PlatoonLeaver {
|
||||||
|
social: std::sync::Arc<crate::SocialMesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SimpleOperation<crate::data::custom::CustomType> for PlatoonLeaver {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, params: ParameterTable<crate::data::custom::CustomType>, user: &Self::User) -> Result<ParameterTable<crate::data::custom::CustomType>, SimpleOpError> {
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let my_public_id = user_info.public_id();
|
||||||
|
if let Some(platoon_id) = self.social.platoon_of_user(my_public_id).await {
|
||||||
|
log::debug!("User {} left platoon {}", my_public_id, platoon_id);
|
||||||
|
let members = self.social.users_of_platoon(&platoon_id).await;
|
||||||
|
if members.len() <= 2 {
|
||||||
|
self.social.remove_platoon(&platoon_id).await;
|
||||||
|
let event = crate::events::platoon_removed::PlatoonDisbanded {
|
||||||
|
platoon_id: platoon_id.to_owned(),
|
||||||
|
};
|
||||||
|
for member in members.iter() {
|
||||||
|
if member.public_id == my_public_id { continue; }
|
||||||
|
self.social.send_event_to(&member.public_id, event.clone()).await;
|
||||||
|
}
|
||||||
|
log::debug!("Platoon {} removed", platoon_id);
|
||||||
|
} else {
|
||||||
|
self.social.remove_user_from_platoon(my_public_id).await;
|
||||||
|
let event = crate::events::platoon_member_left::PlatoonMemberLeft {
|
||||||
|
member_public_id: my_public_id.to_owned(),
|
||||||
|
member_display_name: user_info.display_name().to_owned(),
|
||||||
|
};
|
||||||
|
for member in members.iter() {
|
||||||
|
if member.public_id == my_public_id { continue; }
|
||||||
|
self.social.send_event_to(&member.public_id, event.clone()).await;
|
||||||
|
}
|
||||||
|
if members[0].public_id == my_public_id {
|
||||||
|
// current user was the leader; there is now a new leader
|
||||||
|
log::debug!("User {} is now the leader of platoon {}", members[1].public_id, platoon_id);
|
||||||
|
let social_info = user_info.list_social_info(&[members[1].public_id.clone()]).await?;
|
||||||
|
let event = crate::events::platoon_leader_changed::PlatoonLeaderChanged {
|
||||||
|
leader_public_id: social_info[0].public_id.clone(),
|
||||||
|
leader_display_name: social_info[0].display_name.clone(),
|
||||||
|
};
|
||||||
|
for member in members.iter() {
|
||||||
|
if member.public_id == my_public_id { continue; }
|
||||||
|
self.social.send_event_to(&member.public_id, event.clone()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn platoon_leave_provider(init_ctx: &crate::InitConfig) -> SimpleOpImpl<crate::data::custom::CustomType, crate::UserTy, PlatoonLeaver> {
|
||||||
|
SimpleOpImpl::new(PlatoonLeaver {
|
||||||
|
social: init_ctx.social.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
51
rc_social_room/src/operations/platoon_status.rs
Normal file
51
rc_social_room/src/operations/platoon_status.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
|
const CODE: u8 = 17;
|
||||||
|
|
||||||
|
const STATUS_PARAM_KEY: u8 = 3;
|
||||||
|
|
||||||
|
pub(super) struct PlatoonStatusUpdater {
|
||||||
|
social: std::sync::Arc<crate::SocialMesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SimpleOperation<crate::data::custom::CustomType> for PlatoonStatusUpdater {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, mut params: ParameterTable<crate::data::custom::CustomType>, user: &Self::User) -> Result<ParameterTable<crate::data::custom::CustomType>, SimpleOpError> {
|
||||||
|
if let Some(Typed::Int(status)) = params.remove(&STATUS_PARAM_KEY) {
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let my_public_id = user_info.public_id();
|
||||||
|
let member_status = crate::data::platoon::MemberStatus::from_u8(status as u8)
|
||||||
|
.ok_or_else(|| SimpleOpError::with_message(
|
||||||
|
oj_rc_core::data::error_codes::SocialErrorCode::UnexpectedError as i16,
|
||||||
|
"Invalid member status".to_owned(),
|
||||||
|
))?;
|
||||||
|
if let Some(platoon_id) = self.social.platoon_of_user(my_public_id).await {
|
||||||
|
log::debug!("User {} platoon {} status updated to {:?}", my_public_id, platoon_id, member_status);
|
||||||
|
let members = self.social.users_of_platoon(&platoon_id).await;
|
||||||
|
self.social.update_user_in_platoon(my_public_id, member_status).await;
|
||||||
|
let event = crate::events::platoon_member_update::PlatoonMemberStatusUpdate {
|
||||||
|
member_public_id: my_public_id.to_owned(),
|
||||||
|
member_display_name: user_info.display_name().to_owned(),
|
||||||
|
member_status,
|
||||||
|
};
|
||||||
|
for member in members {
|
||||||
|
if member.public_id == my_public_id { continue; }
|
||||||
|
self.social.send_event_to(&member.public_id, event.clone()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn platoon_update_provider(init_ctx: &crate::InitConfig) -> SimpleOpImpl<crate::data::custom::CustomType, crate::UserTy, PlatoonStatusUpdater> {
|
||||||
|
SimpleOpImpl::new(PlatoonStatusUpdater {
|
||||||
|
social: init_ctx.social.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ pub(super) fn search_clans_provider<C: Send + Sync>() -> SimpleFunc<32, crate::U
|
|||||||
}
|
}
|
||||||
params.insert(RESULTS_PARAM_KEY, Typed::Arr(Arr {
|
params.insert(RESULTS_PARAM_KEY, Typed::Arr(Arr {
|
||||||
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
||||||
|
custom_ty: None,
|
||||||
items: vec![
|
items: vec![
|
||||||
ClanInfo {
|
ClanInfo {
|
||||||
clan_name: "RE_clan_search_name".to_owned(),
|
clan_name: "RE_clan_search_name".to_owned(),
|
||||||
|
|||||||
52
rc_social_room/src/operations/user_can_be_platooned.rs
Normal file
52
rc_social_room/src/operations/user_can_be_platooned.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||||
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(u8)]
|
||||||
|
enum ResponseCode {
|
||||||
|
Ok = 0,
|
||||||
|
InCustomGame = 1,
|
||||||
|
OutstandingCustomInvite = 2,
|
||||||
|
Error = 3,
|
||||||
|
PlayerDoesNotExist = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
const CODE: u8 = 59;
|
||||||
|
|
||||||
|
const USERNAME_PARAM_KEY: u8 = 65; // str; in
|
||||||
|
const RESPONSE_CODE_PARAM_KEY: u8 = 66; // bool; out
|
||||||
|
|
||||||
|
pub(super) struct UserCanBeInvitedToPlatoonGetter {
|
||||||
|
social: std::sync::Arc<crate::SocialMesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SimpleOperation<crate::data::custom::CustomType> for UserCanBeInvitedToPlatoonGetter {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
const CODE: u8 = CODE;
|
||||||
|
|
||||||
|
async fn handle(&self, mut params: ParameterTable<crate::data::custom::CustomType>, user: &Self::User) -> Result<ParameterTable<crate::data::custom::CustomType>, SimpleOpError> {
|
||||||
|
if let Some(Typed::Str(username)) = params.remove(&USERNAME_PARAM_KEY) {
|
||||||
|
let _user_info = user.user()?; // just to validate request is authenticated
|
||||||
|
let mut set = std::collections::HashSet::with_capacity(1);
|
||||||
|
set.insert(username.string.clone());
|
||||||
|
self.social.filter_online_only(&mut set).await;
|
||||||
|
if set.is_empty() {
|
||||||
|
params.insert(RESPONSE_CODE_PARAM_KEY, Typed::Int(ResponseCode::Error as i32));
|
||||||
|
return Ok(params);
|
||||||
|
}
|
||||||
|
/*if self.social.platoon_of_user(&username.string).await.is_some() {
|
||||||
|
params.insert(RESPONSE_CODE_PARAM_KEY, Typed::Int(ResponseCode::OutstandingCustomInvite as i32));
|
||||||
|
return Ok(params);
|
||||||
|
}*/
|
||||||
|
params.insert(RESPONSE_CODE_PARAM_KEY, Typed::Int(ResponseCode::Ok as i32));
|
||||||
|
}
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn can_invite_to_platoon_provider(init_ctx: &crate::InitConfig) -> SimpleOpImpl<crate::data::custom::CustomType, crate::UserTy, UserCanBeInvitedToPlatoonGetter> {
|
||||||
|
SimpleOpImpl::new(UserCanBeInvitedToPlatoonGetter {
|
||||||
|
social: init_ctx.social.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/// Primarily keeps track of who is online so events can be sent to them.
|
/// Primarily keeps track of who is online so events can be sent to them.
|
||||||
pub struct SocialMesh {
|
pub struct SocialMesh {
|
||||||
users: tokio::sync::RwLock<std::collections::HashMap<String, UserHandle>>,
|
users: tokio::sync::RwLock<std::collections::HashMap<String, std::sync::Arc<UserHandle>>>,
|
||||||
|
platoons: Platoons,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UserHandle {
|
struct UserHandle {
|
||||||
@@ -8,10 +9,33 @@ struct UserHandle {
|
|||||||
is_alive: std::sync::atomic::AtomicBool,
|
is_alive: std::sync::atomic::AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Platoons {
|
||||||
|
platoon_by_id: tokio::sync::RwLock<std::collections::HashMap<String, Vec<PlatoonMember>>>,
|
||||||
|
platoon_by_user: tokio::sync::RwLock<std::collections::HashMap<String, String>>, // public_id -> platoon_id
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PlatoonMember {
|
||||||
|
public_id: String,
|
||||||
|
//handle: std::sync::Arc<UserHandle>,
|
||||||
|
status: crate::data::platoon::MemberStatus,
|
||||||
|
timestamp: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PlatoonMemberInfo {
|
||||||
|
pub public_id: String,
|
||||||
|
pub status: crate::data::platoon::MemberStatus,
|
||||||
|
pub timestamp: i64,
|
||||||
|
}
|
||||||
|
|
||||||
impl SocialMesh {
|
impl SocialMesh {
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
users: tokio::sync::RwLock::new(std::collections::HashMap::new()),
|
users: tokio::sync::RwLock::new(std::collections::HashMap::new()),
|
||||||
|
platoons: Platoons {
|
||||||
|
platoon_by_id: tokio::sync::RwLock::new(std::collections::HashMap::new()),
|
||||||
|
platoon_by_user: tokio::sync::RwLock::new(std::collections::HashMap::new()),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,10 +57,10 @@ impl SocialMesh {
|
|||||||
) {
|
) {
|
||||||
let mut user_lock = self.users.write().await;
|
let mut user_lock = self.users.write().await;
|
||||||
Self::cleanup_dead_users(&mut user_lock).await;
|
Self::cleanup_dead_users(&mut user_lock).await;
|
||||||
user_lock.insert(public_id, UserHandle {
|
user_lock.insert(public_id, std::sync::Arc::new(UserHandle {
|
||||||
emitter,
|
emitter,
|
||||||
is_alive: std::sync::atomic::AtomicBool::new(true),
|
is_alive: std::sync::atomic::AtomicBool::new(true),
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filter out offline users
|
/// Filter out offline users
|
||||||
@@ -46,7 +70,7 @@ impl SocialMesh {
|
|||||||
public_ids.retain(|public_id| user_lock.contains_key(public_id));
|
public_ids.retain(|public_id| user_lock.contains_key(public_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn cleanup_dead_users(users: &mut std::collections::HashMap<String, UserHandle>) {
|
async fn cleanup_dead_users(users: &mut std::collections::HashMap<String, std::sync::Arc<UserHandle>>) {
|
||||||
users.retain(|_public_id, handle| handle.is_alive.load(std::sync::atomic::Ordering::SeqCst));
|
users.retain(|_public_id, handle| handle.is_alive.load(std::sync::atomic::Ordering::SeqCst));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,4 +80,112 @@ impl SocialMesh {
|
|||||||
.filter(|(_, handle)| handle.is_alive.load(std::sync::atomic::Ordering::SeqCst))
|
.filter(|(_, handle)| handle.is_alive.load(std::sync::atomic::Ordering::SeqCst))
|
||||||
.count() as u64
|
.count() as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn platoon_of_user(&self, public_id: &str) -> Option<String> {
|
||||||
|
self.platoons.platoon_by_user.read().await
|
||||||
|
.get(public_id)
|
||||||
|
.map(|x| x.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn users_of_platoon(&self, platoon_id: &str) -> Vec<PlatoonMemberInfo> {
|
||||||
|
self.platoons.platoon_by_id.read().await
|
||||||
|
.get(platoon_id)
|
||||||
|
.map(|plat| plat.iter()
|
||||||
|
.map(|member| PlatoonMemberInfo {
|
||||||
|
public_id: member.public_id.clone(),
|
||||||
|
status: member.status,
|
||||||
|
timestamp: member.timestamp,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_user_to_platoon(&self, public_id: &str, platoon_id: &str, status: crate::data::platoon::MemberStatus) -> Option<i64> {
|
||||||
|
/*let user_handle = if let Some(handle) = self.users.read().await.get(public_id) {
|
||||||
|
handle.to_owned()
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};*/
|
||||||
|
if let Some(platoon) = self.platoons.platoon_by_id.write().await.get_mut(platoon_id) {
|
||||||
|
let timestamp = chrono::Utc::now().timestamp();
|
||||||
|
platoon.push(PlatoonMember {
|
||||||
|
public_id: public_id.to_owned(),
|
||||||
|
//handle: user_handle,
|
||||||
|
status,
|
||||||
|
timestamp,
|
||||||
|
});
|
||||||
|
self.platoons.platoon_by_user.write().await.insert(public_id.to_owned(), platoon_id.to_owned());
|
||||||
|
Some(timestamp)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_platoon(&self, platoon_id: &str, public_id: &str) -> Option<i64> {
|
||||||
|
/*let user_handle = if let Some(handle) = self.users.read().await.get(public_id) {
|
||||||
|
handle.to_owned()
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};*/
|
||||||
|
if self.platoons.platoon_by_id.read().await.contains_key(platoon_id) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut platoon_members = Vec::with_capacity(5);
|
||||||
|
let timestamp = chrono::Utc::now().timestamp();
|
||||||
|
platoon_members.push(PlatoonMember {
|
||||||
|
public_id: public_id.to_owned(),
|
||||||
|
//handle: user_handle,
|
||||||
|
status: crate::data::platoon::MemberStatus::Ready,
|
||||||
|
timestamp,
|
||||||
|
});
|
||||||
|
self.platoons.platoon_by_id.write().await.insert(platoon_id.to_owned(), platoon_members);
|
||||||
|
self.platoons.platoon_by_user.write().await.insert(public_id.to_owned(), platoon_id.to_owned());
|
||||||
|
Some(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_user_from_platoon(&self, public_id: &str) -> bool {
|
||||||
|
if let Some(platoon_id) = self.platoons.platoon_by_user.write().await.remove(public_id) {
|
||||||
|
let mut platoon_by_id_lock = self.platoons.platoon_by_id.write().await;
|
||||||
|
if let Some(platoon) = platoon_by_id_lock.get_mut(&platoon_id) {
|
||||||
|
platoon.retain(|member| member.public_id != public_id);
|
||||||
|
if platoon.is_empty() {
|
||||||
|
platoon_by_id_lock.remove(&platoon_id);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_user_in_platoon(&self, public_id: &str, status: crate::data::platoon::MemberStatus) -> bool {
|
||||||
|
if let Some(platoon_id) = self.platoons.platoon_by_user.write().await.get_mut(public_id) {
|
||||||
|
if let Some(platoon) = self.platoons.platoon_by_id.write().await.get_mut(platoon_id) {
|
||||||
|
for member in platoon.iter_mut() {
|
||||||
|
if member.public_id != public_id { continue; }
|
||||||
|
member.status = status;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_platoon(&self, platoon_id: &str) -> bool {
|
||||||
|
if let Some(platoon) = self.platoons.platoon_by_id.write().await.remove(platoon_id) {
|
||||||
|
let mut platoon_by_user_lock = self.platoons.platoon_by_user.write().await;
|
||||||
|
for member in platoon {
|
||||||
|
platoon_by_user_lock.remove(&member.public_id);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user