diff --git a/rnex-core/src/executables/friends_backend.rs b/rnex-core/src/executables/friends_backend.rs index 74983f7..6dceda1 100644 --- a/rnex-core/src/executables/friends_backend.rs +++ b/rnex-core/src/executables/friends_backend.rs @@ -9,10 +9,12 @@ use tokio::net::TcpListener; use crate::{ executables::common::{OWN_IP_PRIVATE, SERVER_PORT, new_simple_backend}, - nex::friends_handler::{FriendsGuest, FriendsManager, FriendsUser}, + nex::friends_handler::{FriendsGuest, FriendsManager, FriendsUser, RemoteFriendsUser}, reggie::UnitPacketRead, rmc::{ - protocols::{RmcCallable, new_rmc_gateway_connection}, + protocols::{ + RmcCallable, RmcPureRemoteObject, friends::RemoteFriends, new_rmc_gateway_connection, + }, structures::RmcSerialize, }, rnex_proxy_common::ConnectionInitData, @@ -21,6 +23,7 @@ use crate::{ pub async fn start_friends_backend() { let fm = Arc::new(FriendsManager { cid_counter: AtomicU32::new(1), + users: Default::default(), }); let listen = TcpListener::bind(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT)) .await @@ -53,6 +56,10 @@ pub async fn start_friends_backend() { fm, addr: c.prudpsock_addr, pid: c.pid, + data: Default::default(), + current_friends: Default::default(), + this: this.clone(), + remote: RemoteFriendsUser::new(r), }) }); } else { diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 42e5c71..2d9c2a8 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -1,4 +1,6 @@ use std::io::{Cursor, Write}; +use std::ops::Deref; +use std::sync::Weak; use std::sync::{Arc, atomic::AtomicU32}; use bytemuck::bytes_of; @@ -9,6 +11,10 @@ use rnex_core::rmc::protocols::account_management::{ AccountManagement, RawAccountManagement, RawAccountManagementInfo, RemoteAccountManagement, }; use rnex_core::rmc::protocols::friends::{Friends, RawFriends, RawFriendsInfo, RemoteFriends}; +use rnex_core::rmc::protocols::nintendo_notification::{ + NintendoNotification, RawNintendoNotification, RawNintendoNotificationInfo, + RemoteNintendoNotification, +}; use rnex_core::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure}; use rnex_core::{ define_rmc_proto, @@ -25,12 +31,15 @@ use rnex_core::{ }, }; use std::sync::atomic::Ordering::Relaxed; +use tokio::sync::RwLock; use rnex_core::rmc::protocols::friends::{GameKey, MiiV2, PrincipalBasicInfo}; use rnex_core::PID; +use crate::nex::user; use crate::rmc::protocols::account_management::NintendoCreateAccountData; +use crate::rmc::protocols::nintendo_notification::NintendoNotificationEvent; use rnex_core::rmc::structures::RmcSerialize; define_rmc_proto!( @@ -39,17 +48,32 @@ define_rmc_proto!( Friends } ); +define_rmc_proto!( + proto FriendRemote{ + NintendoNotification + } +); define_rmc_proto!( proto FriendsGuest{ Secure, AccountManagement } ); + +pub struct UserData { + info: NNAInfo, + presence: NintendoPresenceV2, +} + #[rmc_struct(FriendsUser)] pub struct FriendsUser { pub fm: Arc, pub addr: PRUDPSockAddr, pub pid: PID, + pub data: RwLock>, + pub current_friends: RwLock>, + pub this: Weak, + pub remote: RemoteFriendRemote, } #[rmc_struct(FriendsGuest)] @@ -60,6 +84,7 @@ pub struct FriendsGuest { pub struct FriendsManager { pub cid_counter: AtomicU32, + pub users: RwLock>>, } impl FriendsManager { @@ -68,11 +93,26 @@ impl FriendsManager { } } +pub fn friend_info_from_user(data: &UserData) -> FriendInfo { + FriendInfo { + nna_info: data.info.clone(), + presence: data.presence.clone(), + comment: Comment { + unk: 0, + message: "litterally everyone is friends here =w=".to_string(), + last_changed: KerberosDateTime::now(), + }, + became_friends: KerberosDateTime::now(), + last_online: KerberosDateTime::now(), + unk: 0, + } +} + impl Friends for FriendsUser { async fn update_and_get_all_information( &self, - _info: NNAInfo, - _presence: NintendoPresenceV2, + info: NNAInfo, + presence: NintendoPresenceV2, _date_time: KerberosDateTime, ) -> Result< ( @@ -88,17 +128,15 @@ impl Friends for FriendsUser { ), ErrorCode, > { - Ok(( - PrincipalPreference { - block_friend_request: false, - show_online: false, - show_playing_title: false, - }, - Comment { - last_changed: KerberosDateTime::now(), - message: "".to_string(), - unk: 0, - }, + let mut data = self.data.write().await; + *data = Some(UserData { info, presence }); + let self_fr_info = friend_info_from_user(data.as_ref().unwrap()); + let Ok(any_self_fr_info) = Any::new(&self_fr_info) else { + return Err(ErrorCode::RendezVous_ControlScriptFailure); + }; + drop(data); + + let mut fr_list = vec![FriendInfo { became_friends: KerberosDateTime::now(), comment: Comment { @@ -144,7 +182,51 @@ impl Friends for FriendsUser { unk7: 0 }, unk: 0 - }], + }]; + + let users = self.fm.users.read().await; + let mut curr_friends = self.current_friends.write().await; + for u in users.deref().iter().filter_map(|u| u.upgrade()) { + let data = u.data.read().await; + let Some(data) = data.as_ref() else { + continue; + }; + + let mut fr = u.current_friends.write().await; + if !fr.contains(&self.pid) { + fr.push(self.pid); + u.remote + .process_nintendo_notification_event_1(NintendoNotificationEvent { + event_type: 30, + sender: self.pid, + data: any_self_fr_info.clone(), + }) + .await; + } + drop(fr); + + fr_list.push(friend_info_from_user(&data)); + curr_friends.push(u.pid); + } + drop(curr_friends); + drop(users); + + let mut users = self.fm.users.write().await; + users.push(self.this.clone()); + drop(users); + + Ok(( + PrincipalPreference { + block_friend_request: false, + show_online: false, + show_playing_title: false, + }, + Comment { + last_changed: KerberosDateTime::now(), + message: "".to_string(), + unk: 0, + }, + fr_list, vec![], vec![], vec![], @@ -154,6 +236,32 @@ impl Friends for FriendsUser { )) } + async fn update_presence(&self, presence: NintendoPresenceV2) -> Result<(), ErrorCode> { + let mut data = self.data.write().await; + let Some(data) = data.as_mut() else { + return Err(ErrorCode::RendezVous_PermissionDenied); + }; + data.presence = presence; + let Ok(any_self_fr_info) = Any::new(&data.presence) else { + return Err(ErrorCode::RendezVous_ControlScriptFailure); + }; + drop(data); + + let users = self.fm.users.read().await; + for u in users.deref().iter().filter_map(|u| u.upgrade()) { + u.remote + .process_nintendo_notification_event_2(NintendoNotificationEvent { + event_type: 24, + sender: self.pid, + data: any_self_fr_info.clone(), + }) + .await; + } + drop(users); + + Ok(()) + } + async fn check_setting_status(&self) -> Result { Ok(0xFF) } diff --git a/rnex-core/src/rmc/protocols/friends.rs b/rnex-core/src/rmc/protocols/friends.rs index 3f6138f..fb179ce 100644 --- a/rnex-core/src/rmc/protocols/friends.rs +++ b/rnex-core/src/rmc/protocols/friends.rs @@ -29,14 +29,14 @@ pub struct NNAInfo { pub unk2: u8, } -#[derive(RmcSerialize)] +#[derive(RmcSerialize, Clone, Copy, Debug)] #[rmc_struct(0)] pub struct GameKey { pub tid: u64, pub version: u16, } -#[derive(RmcSerialize)] +#[derive(RmcSerialize, Clone, Debug)] #[rmc_struct(0)] pub struct NintendoPresenceV2 { pub changed_flags: u32, @@ -143,6 +143,8 @@ pub trait Friends { ), ErrorCode, >; + #[method_id(13)] + async fn update_presence(&self, presence: NintendoPresenceV2) -> Result<(), ErrorCode>; #[method_id(19)] async fn check_setting_status(&self) -> Result; } diff --git a/rnex-core/src/rmc/protocols/mod.rs b/rnex-core/src/rmc/protocols/mod.rs index 8feb2f3..eed0597 100644 --- a/rnex-core/src/rmc/protocols/mod.rs +++ b/rnex-core/src/rmc/protocols/mod.rs @@ -7,6 +7,7 @@ pub mod matchmake; pub mod matchmake_ext; pub mod matchmake_extension; pub mod nat_traversal; +pub mod nintendo_notification; pub mod notifications; pub mod ranking; pub mod secure; diff --git a/rnex-core/src/rmc/protocols/nintendo_notification.rs b/rnex-core/src/rmc/protocols/nintendo_notification.rs new file mode 100644 index 0000000..f7503fd --- /dev/null +++ b/rnex-core/src/rmc/protocols/nintendo_notification.rs @@ -0,0 +1,39 @@ +use macros::{RmcSerialize, method_id, rmc_proto}; + +use rnex_core::PID; +use rnex_core::rmc::structures::any::Any; + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct NintendoNotificationEvent { + pub event_type: u32, + pub sender: PID, + pub data: Any, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct NintendoNotificationEventGeneral { + pub param1: u32, + pub param2: u64, + pub param3: u64, + pub str_param: String, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct NintendoNotificationEventProfile { + pub region: u8, + pub country: u8, + pub area: u8, + pub language: u8, + pub platform: u8, +} + +#[rmc_proto(3, NoReturn)] +pub trait NintendoNotification { + #[method_id(1)] + async fn process_nintendo_notification_event_1(&self, notif: NintendoNotificationEvent); + #[method_id(2)] + async fn process_nintendo_notification_event_2(&self, notif: NintendoNotificationEvent); +} diff --git a/rnex-core/src/rmc/structures/any.rs b/rnex-core/src/rmc/structures/any.rs index c4dc2ce..45ab453 100644 --- a/rnex-core/src/rmc/structures/any.rs +++ b/rnex-core/src/rmc/structures/any.rs @@ -1,8 +1,8 @@ use rnex_core::rmc::structures::{Result, RmcSerialize}; -use std::io::{Read, Write}; +use std::io::{Cursor, Read, Write}; use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct Any { pub name: String, pub data: Vec, @@ -14,7 +14,7 @@ impl RmcSerialize for Any { let u32_len = self.data.len() as u32; - u32_len.serialize(writer)?; + (u32_len + 4).serialize(writer)?; u32_len.serialize(writer)?; self.data.serialize(writer)?; @@ -35,3 +35,18 @@ impl RmcSerialize for Any { Ok(Any { name, data }) } } + +impl Any { + pub fn try_get(&self) -> Option> { + if self.name != T::name() { + return None; + } + return Some(T::deserialize(&mut Cursor::new(&self.data[..]))); + } + pub fn new(val: &T) -> Result { + return Ok(Self { + name: T::name().to_owned(), + data: val.to_data()?, + }); + } +} diff --git a/rnex-core/src/rmc/structures/mod.rs b/rnex-core/src/rmc/structures/mod.rs index 996cc4b..99bef0c 100644 --- a/rnex-core/src/rmc/structures/mod.rs +++ b/rnex-core/src/rmc/structures/mod.rs @@ -61,6 +61,9 @@ pub trait RmcSerialize { Ok(data) } + fn name() -> &'static str { + "NoNameSpecified" + } } impl RmcSerialize for () {