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:
1 parent
a82488ab3a
commit
50b396f34a
49 files changed
+1152
-47
No files matched your search
@@ -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);
|
||||
params.insert(MEMBERS_PARAM_KEY, Typed::Arr(Arr {
|
||||
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
||||
custom_ty: None,
|
||||
items: vec![
|
||||
ClanMember {
|
||||
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();
|
||||
params.insert(PARAM_KEY, Typed::<C>::Arr(Arr {
|
||||
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
||||
custom_ty: None,
|
||||
/*items: vec![
|
||||
ClanInviteInfo {
|
||||
username: "RE_user1".to_owned(),
|
||||
|
||||
@@ -44,6 +44,7 @@ impl SimpleOperation<crate::data::custom::CustomType> for FriendsLister {
|
||||
// Typed::Custom(crate::data::custom::CustomType::FriendInfo)
|
||||
params.insert(FRIENDS_PARAM_KEY, Typed::Arr(Arr {
|
||||
ty: polariton::serdes::TypePrefix::Custom, // custom
|
||||
custom_ty: Some(0),
|
||||
items: friends.iter().map(|friend|
|
||||
Typed::Custom(crate::data::custom::CustomType::FriendInfo(crate::data::friend::FriendInfo {
|
||||
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 {
|
||||
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
||||
custom_ty: None,
|
||||
items: friends.iter()
|
||||
.map(|friend|
|
||||
AvatarInfo {
|
||||
|
||||
@@ -15,6 +15,14 @@ mod friend_accept;
|
||||
mod friend_decline;
|
||||
mod friend_cancel;
|
||||
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;
|
||||
|
||||
@@ -27,7 +35,8 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy,
|
||||
.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(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(search_clan::search_clans_provider())
|
||||
.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_cancel::friend_cancel_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())
|
||||
})
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
@@ -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 {
|
||||
ty: polariton::serdes::TypePrefix::HashMap, // hashmap
|
||||
custom_ty: None,
|
||||
items: vec![
|
||||
ClanInfo {
|
||||
clan_name: "RE_clan_search_name".to_owned(),
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user