diff --git a/macros/src/lib.rs b/macros/src/lib.rs index d0fc909..432fc03 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -10,8 +10,8 @@ use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{ - parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Fields, FnArg, LitInt, Pat, Token, - TraitItem, + parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Fields, FnArg, Lit, LitInt, + LitStr, Pat, Token, TraitItem, }; struct ProtoInputParams { @@ -400,6 +400,10 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { // generate base data + let str_name = Lit::Str(LitStr::new( + &derive_input.ident.to_string(), + derive_input.ident.span(), + )); let ident = derive_input.ident; let tokens = quote! { @@ -414,6 +418,10 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { } #write_size + + fn name() -> &'static str{ + #str_name + } } }; diff --git a/prudpv0/src/server.rs b/prudpv0/src/server.rs index 34c7647..899bbd6 100644 --- a/prudpv0/src/server.rs +++ b/prudpv0/src/server.rs @@ -106,50 +106,56 @@ impl Server { .expect("packet malformed in creation"), );*/ let mut inner = conn.inner.lock().await; - let seq = inner.server_packet_counter; - let packet = new_data_packet( - NEED_ACK | RELIABLE, - self.param.virtual_port, - conn.addr.virtual_port, - data, - inner.server_packet_counter, - conn.session_id, - 0, - &mut inner.crypto_instance, - &self.crypto, - ); - inner.server_packet_counter += 1; + let pieces = data.chunks(700); + let max_piece = pieces.len() - 1; + let mut frag_num = 1; + for (i, piece) in pieces.enumerate() { + let seq = inner.server_packet_counter; + let packet = new_data_packet( + NEED_ACK | RELIABLE, + (&self).param.virtual_port, + conn.addr.virtual_port, + piece, + inner.server_packet_counter, + conn.session_id, + if i == max_piece { 0 } else { frag_num }, + &mut inner.crypto_instance, + &(&self).crypto, + ); + inner.server_packet_counter += 1; - let packet = Arc::new(packet); - let packet_ref = Arc::downgrade(&packet); + let packet = Arc::new(packet); + let packet_ref = Arc::downgrade(&packet); - inner.unacknowledged_packets.insert(seq, packet); + inner.unacknowledged_packets.insert(seq, packet); + let conn = Arc::downgrade(&conn); + let this = Arc::downgrade(&self); + + spawn(async move { + sleep(Duration::from_millis(i as u64 * 16)).await; + for n in 0..5 { + let Some(data) = packet_ref.upgrade() else { + return; + }; + let Some(conn) = conn.upgrade() else { + return; + }; + let Some(this) = this.upgrade() else { + return; + }; + info!("send attempt {}", n); + + this.socket + .send_to(&data, conn.addr.regular_socket_addr) + .await; + + break; + } + }); + frag_num += 1; + } drop(inner); - - let conn = Arc::downgrade(&conn); - let this = Arc::downgrade(&self); - - spawn(async move { - for n in 0..5 { - let Some(data) = packet_ref.upgrade() else { - return; - }; - let Some(conn) = conn.upgrade() else { - return; - }; - let Some(this) = this.upgrade() else { - return; - }; - info!("send attempt {}", n); - - self.socket - .send_to(&data, conn.addr.regular_socket_addr) - .await; - - break; - } - }); } async fn connection_thread( self: Arc, diff --git a/rnex-core/src/executables/friends_backend.rs b/rnex-core/src/executables/friends_backend.rs index 74983f7..5dfcf02 100644 --- a/rnex-core/src/executables/friends_backend.rs +++ b/rnex-core/src/executables/friends_backend.rs @@ -9,10 +9,14 @@ 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, RemoteFriendRemote, 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 +25,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 +58,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: RemoteFriendRemote::new(r), }) }); } else { diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 42e5c71..941b747 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,31 +31,51 @@ use rnex_core::{ }, }; use std::sync::atomic::Ordering::Relaxed; +use tokio::spawn; +use tokio::sync::RwLock; use rnex_core::rmc::protocols::friends::{GameKey, MiiV2, PrincipalBasicInfo}; use rnex_core::PID; -use crate::rmc::protocols::account_management::NintendoCreateAccountData; +use rnex_core::rmc::protocols::account_management::NintendoCreateAccountData; +use rnex_core::rmc::protocols::nintendo_notification::NintendoNotificationEvent; use rnex_core::rmc::structures::RmcSerialize; +use rnex_core::rmc::structures::data::Data; + define_rmc_proto!( proto FriendsUser{ Secure, 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 +86,7 @@ pub struct FriendsGuest { pub struct FriendsManager { pub cid_counter: AtomicU32, + pub users: RwLock>>, } impl FriendsManager { @@ -68,11 +95,28 @@ impl FriendsManager { } } +pub fn friend_info_from_user(data: &UserData) -> FriendInfo { + FriendInfo { + data: Data {}, + nna_info: data.info.clone(), + presence: data.presence.clone(), + comment: Comment { + data: Data {}, + unk: 0, + message: "haii =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,63 +132,127 @@ impl Friends for FriendsUser { ), ErrorCode, > { + println!("updating own data"); + 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 { + data: Data{}, + became_friends: KerberosDateTime::now(), + comment: Comment { + data: Data{}, + last_changed: KerberosDateTime::now(), + message: "I'm just a dummy account :3".to_string(), + unk: 0, + }, + last_online: KerberosDateTime::now(), + nna_info: NNAInfo { + data: Data{}, + principal_basic_info: PrincipalBasicInfo { + data: Data{}, + pid: 101, + nnid: "dummy:3".to_string(), + mii: MiiV2{ + data: Data{}, + date_time: KerberosDateTime::now(), + name: "TheDummy".to_string(), + mii_data: hex::decode("030000402bd7c32986a771f2dc6b35e31da15e37ff7c0000391e6f006f006d0069000000000000000000000000004040001065033568641e2013661a611821640f0000290052485000000000000000000000000000000000000000000000e838").unwrap(), + unk: 0, + unk2: 0, + }, + unk: 0 + }, + unk: 0, + unk2: 0 + }, + presence: NintendoPresenceV2{ + data: Data{}, + changed_flags: 0, + message: "".to_string(), + app_data: vec![], + game_key: GameKey{ + data: Data{}, + tid: 0x00050002101ce400, + version: 0x0 + }, + game_server_id: 0, + is_online: true, + gid: 0, + pid: 101, + unk: 0, + unk2: 0, + unk3: 0, + unk4: 0, + unk5: 0, + unk6: 0, + unk7: 0 + }, + unk: 0 + }]; + + println!("acquiring user and current friends locks"); + let users = self.fm.users.read().await; + println!("started summing users"); + for u in users.deref().iter().filter_map(|u| u.upgrade()) { + let data = u.data.read().await; + let Some(inner_data) = data.as_ref() else { + continue; + }; + fr_list.push(friend_info_from_user(&inner_data)); + drop(data); + + let mut curr_friends = self.current_friends.write().await; + curr_friends.push(u.pid); + drop(curr_friends); + + let mut fr = u.current_friends.write().await; + if !fr.contains(&self.pid) { + fr.push(self.pid); + drop(fr); + let data = any_self_fr_info.clone(); + let u = u.clone(); + let sender = self.pid; + spawn(async move { + u.remote + .process_nintendo_notification_event_1(NintendoNotificationEvent { + event_type: 30, + sender, + data, + }) + .await; + }); + } else { + drop(fr); + } + } + println!("finished summing users"); + drop(users); + + println!("adding self to users"); + let mut users = self.fm.users.write().await; + users.push(self.this.clone()); + drop(users); + + println!("done..."); Ok(( PrincipalPreference { + data: Data {}, block_friend_request: false, show_online: false, show_playing_title: false, }, Comment { + data: Data {}, last_changed: KerberosDateTime::now(), message: "".to_string(), unk: 0, }, - vec![FriendInfo { - became_friends: KerberosDateTime::now(), - comment: Comment { - last_changed: KerberosDateTime::now(), - message: "I'm just a dummy account :3".to_string(), - unk: 0, - }, - last_online: KerberosDateTime::now(), - nna_info: NNAInfo { - principal_basic_info: PrincipalBasicInfo { - pid: 101, - nnid: "dummy:3".to_string(), - mii: MiiV2{ - date_time: KerberosDateTime::now(), - name: "TheDummy".to_string(), - mii_data: hex::decode("030000402bd7c32986a771f2dc6b35e31da15e37ff7c0000391e6f006f006d0069000000000000000000000000004040001065033568641e2013661a611821640f0000290052485000000000000000000000000000000000000000000000e838").unwrap(), - unk: 0, - unk2: 0, - }, - unk: 0 - }, - unk: 0, - unk2: 0 - }, - presence: NintendoPresenceV2{ - changed_flags: 0, - message: "".to_string(), - app_data: vec![], - game_key: GameKey{ - tid: 0x00050002101ce400, - version: 0x0 - }, - game_server_id: 0, - is_online: true, - gid: 0, - pid: 101, - unk: 0, - unk2: 0, - unk3: 0, - unk4: 0, - unk5: 0, - unk6: 0, - unk7: 0 - }, - unk: 0 - }], + fr_list, vec![], vec![], vec![], @@ -154,6 +262,39 @@ impl Friends for FriendsUser { )) } + async fn update_presence(&self, presence: NintendoPresenceV2) -> Result<(), ErrorCode> { + let mut data = self.data.write().await; + let Some(inner_data) = data.as_mut() else { + return Err(ErrorCode::RendezVous_PermissionDenied); + }; + inner_data.presence = presence; + let Ok(any_self_fr_info) = Any::new(&inner_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 delete_persistent_notification( + &self, + notifs: Vec, + ) -> Result<(), ErrorCode> { + Ok(()) + } + async fn check_setting_status(&self) -> Result { Ok(0xFF) } diff --git a/rnex-core/src/nex/matchmake.rs b/rnex-core/src/nex/matchmake.rs index 032fbad..0a18f74 100644 --- a/rnex-core/src/nex/matchmake.rs +++ b/rnex-core/src/nex/matchmake.rs @@ -247,31 +247,32 @@ impl ExtendedMatchmakeSession { param_3: self.connected_players.len() as _, }) .await; - } + } } for old_conns in &old_particip { let Some(old_conns) = old_conns.upgrade() else { continue; }; - if old_conns.pid != self.session.gathering.host_pid{ - continue; - } - for new_conn_pid in conns.iter().filter_map(Weak::upgrade).map(|c| c.pid){ - old_conns - .remote - .process_notification_event(NotificationEvent { - pid_source: initiating_pid, - notif_type: 3001, - param_1: self.session.gathering.self_gid as PID, - param_2: new_conn_pid, - str_param: join_msg.clone(), - param_3: self.connected_players.len() as _, - }) - .await; + /*if old_conns.pid != self.session.gathering.host_pid { + continue; + }*/ + for new_conn_pid in conns.iter().filter_map(Weak::upgrade).map(|c| c.pid) { + old_conns + .remote + .process_notification_event(NotificationEvent { + pid_source: initiating_pid, + notif_type: 3001, + param_1: self.session.gathering.self_gid as PID, + param_2: new_conn_pid, + str_param: join_msg.clone(), + param_3: self.connected_players.len() as _, + }) + .await; } } } + pub fn has_active_players(&self) -> bool { self.connected_players .iter() diff --git a/rnex-core/src/nex/user.rs b/rnex-core/src/nex/user.rs index a812d7c..c418cfd 100644 --- a/rnex-core/src/nex/user.rs +++ b/rnex-core/src/nex/user.rs @@ -5,15 +5,12 @@ use crate::nex::remote_console::RemoteConsole; use crate::rmc::protocols::matchmake::{ Matchmake, RawMatchmake, RawMatchmakeInfo, RemoteMatchmake, }; -use serde::{Serialize, Deserialize}; -use std::env; -use std::str::FromStr; use crate::rmc::protocols::nat_traversal::{ NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal, RemoteNatTraversalConsole, }; -use rnex_core::kerberos::KerberosDateTime; use rnex_core::PID; +use rnex_core::kerberos::KerberosDateTime; use rnex_core::prudp::station_url::StationUrl; use rnex_core::prudp::station_url::UrlOptions::{ Address, NatFiltering, NatMapping, Port, RVConnectionID, @@ -32,14 +29,21 @@ use rnex_core::rmc::structures::any::Any; use rnex_core::rmc::structures::matchmake::{ AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession, }; +use serde::{Deserialize, Serialize}; +use std::env; +use std::str::FromStr; use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification}; -use log::{info, error}; +use log::{error, info}; use macros::rmc_struct; -use rnex_core::rmc::structures::qbuffer::QBuffer; use rnex_core::prudp::socket_addr::PRUDPSockAddr; +use rnex_core::rmc::protocols::ranking::{ + CompetitionRankingGetParam, CompetitionRankingScoreData, CompetitionRankingScoreInfo, +}; use rnex_core::rmc::response::ErrorCode::{Core_InvalidArgument, RendezVous_AccountExpired}; +use rnex_core::rmc::structures::qbuffer::QBuffer; use rnex_core::rmc::structures::qresult::QResult; +use rnex_core::rmc::structures::ranking::UploadCompetitionData; use std::sync::{Arc, Weak}; use cfg_if::cfg_if; use rnex_core::rmc::protocols::ranking::{CompetitionRankingScoreData, CompetitionRankingGetParam, CompetitionRankingScoreInfo}; @@ -635,10 +639,7 @@ fn fetch_team_votes(fest_id: u32) -> Result, ErrorCode> { })?; let body = body.trim().trim_start_matches('[').trim_end_matches(']'); - let votes: Result, _> = body - .split(',') - .map(|s| u32::from_str(s.trim())) - .collect(); + let votes: Result, _> = body.split(',').map(|s| u32::from_str(s.trim())).collect(); votes.map_err(|e| { error!("failed to parse votes: {:?}", e); @@ -653,23 +654,19 @@ impl Ranking for User { ) -> Result, ErrorCode> { let fest_id = param.festival_ids.get(0).copied().unwrap_or(0); - let endpoint_results = env::var("RNEX_SPLATOON_RESULTS_GET") - .map_err(|_| { - error!("RNEX_SPLATOON_RESULTS_GET not set"); - ErrorCode::RendezVous_InvalidConfiguration - })?; + let endpoint_results = env::var("RNEX_SPLATOON_RESULTS_GET").map_err(|_| { + error!("RNEX_SPLATOON_RESULTS_GET not set"); + ErrorCode::RendezVous_InvalidConfiguration + })?; let url_results = format!("{}?splatfest_id={}", endpoint_results, fest_id); let response_results = ureq::get(&url_results).call(); let results: Vec = match response_results { - Ok(mut res) => res - .body_mut() - .read_json() - .map_err(|e| { - error!("failed to parse JSON: {:?}", e); - ErrorCode::RendezVous_InvalidConfiguration - })?, + Ok(mut res) => res.body_mut().read_json().map_err(|e| { + error!("failed to parse JSON: {:?}", e); + ErrorCode::RendezVous_InvalidConfiguration + })?, Err(e) => { error!("GET failed: {:?}", e); return Err(ErrorCode::RendezVous_InvalidConfiguration); @@ -679,9 +676,10 @@ impl Ranking for User { let team_votes = fetch_team_votes(fest_id)?; let mut wins = vec![0u32, 0u32]; for r in &results { - if r.team_win == 1 && (r.team_id == 0 || r.team_id == 1) { - wins[r.team_id as usize] += 1; - } + let won_team = r.team_id ^ (!r.team_win); + if let Some(team) = wins.get_mut(won_team as usize) { + *team += 1 + }; } let score_data: Vec = results @@ -695,7 +693,7 @@ impl Ranking for User { appdata: QBuffer(vec![]), }) .collect(); - + let info = CompetitionRankingScoreInfo { fest_id, score_data, @@ -707,7 +705,10 @@ impl Ranking for User { Ok(vec![info]) } - async fn upload_competition_ranking_score(&self, param: UploadCompetitionData) -> Result { + async fn upload_competition_ranking_score( + &self, + param: UploadCompetitionData, + ) -> Result { info!("fest results for user {:?}:", self.pid); info!("fest id: {:?}", param.splatfest_id); info!("score: {:?}", param.score); @@ -750,8 +751,7 @@ impl Ranking for User { error!("POST borked: {:?}", e); } } - + Ok(true) } } - diff --git a/rnex-core/src/rmc/protocols/friends.rs b/rnex-core/src/rmc/protocols/friends.rs index 3f6138f..81dd63e 100644 --- a/rnex-core/src/rmc/protocols/friends.rs +++ b/rnex-core/src/rmc/protocols/friends.rs @@ -2,9 +2,13 @@ use macros::{RmcSerialize, method_id, rmc_proto}; use rnex_core::{kerberos::KerberosDateTime, rmc::response::ErrorCode}; +use rnex_core::rmc::structures::{data::Data, rmc_struct}; + #[derive(RmcSerialize, Debug, Clone)] #[rmc_struct(0)] pub struct MiiV2 { + #[extends] + pub data: Data, pub name: String, pub unk: u8, pub unk2: u8, @@ -15,6 +19,8 @@ pub struct MiiV2 { #[derive(RmcSerialize, Debug, Clone)] #[rmc_struct(0)] pub struct PrincipalBasicInfo { + #[extends] + pub data: Data, pub pid: u32, pub nnid: String, pub mii: MiiV2, @@ -24,21 +30,27 @@ pub struct PrincipalBasicInfo { #[derive(RmcSerialize, Debug, Clone)] #[rmc_struct(0)] pub struct NNAInfo { + #[extends] + pub data: Data, pub principal_basic_info: PrincipalBasicInfo, pub unk: u8, pub unk2: u8, } -#[derive(RmcSerialize)] +#[derive(RmcSerialize, Clone, Copy, Debug)] #[rmc_struct(0)] pub struct GameKey { + #[extends] + pub data: Data, pub tid: u64, pub version: u16, } -#[derive(RmcSerialize)] +#[derive(RmcSerialize, Clone, Debug)] #[rmc_struct(0)] pub struct NintendoPresenceV2 { + #[extends] + pub data: Data, pub changed_flags: u32, pub is_online: bool, pub game_key: GameKey, @@ -58,6 +70,8 @@ pub struct NintendoPresenceV2 { #[derive(RmcSerialize)] #[rmc_struct(0)] pub struct PrincipalPreference { + #[extends] + pub data: Data, pub show_online: bool, pub show_playing_title: bool, pub block_friend_request: bool, @@ -66,6 +80,8 @@ pub struct PrincipalPreference { #[derive(RmcSerialize)] #[rmc_struct(0)] pub struct Comment { + #[extends] + pub data: Data, pub unk: u8, pub message: String, pub last_changed: KerberosDateTime, @@ -74,6 +90,8 @@ pub struct Comment { #[derive(RmcSerialize)] #[rmc_struct(0)] pub struct FriendInfo { + #[extends] + pub data: Data, pub nna_info: NNAInfo, pub presence: NintendoPresenceV2, pub comment: Comment, @@ -85,6 +103,8 @@ pub struct FriendInfo { #[derive(RmcSerialize)] #[rmc_struct(0)] pub struct FriendRequestMessage { + #[extends] + pub data: Data, pub friend_request_id: u64, pub is_recieved: u8, pub unk: u8, @@ -99,6 +119,8 @@ pub struct FriendRequestMessage { #[derive(RmcSerialize)] #[rmc_struct(0)] pub struct FriendRequest { + #[extends] + pub data: Data, pub basic_info: PrincipalBasicInfo, pub request_message: FriendRequestMessage, pub sent_on: KerberosDateTime, @@ -107,6 +129,8 @@ pub struct FriendRequest { #[derive(RmcSerialize)] #[rmc_struct(0)] pub struct BlacklistedPrincipal { + #[extends] + pub data: Data, pub basic_info: PrincipalBasicInfo, pub game_key: GameKey, pub since: KerberosDateTime, @@ -114,6 +138,8 @@ pub struct BlacklistedPrincipal { #[derive(RmcSerialize)] #[rmc_struct(0)] pub struct PersistentNotification { + #[extends] + pub data: Data, pub unk1: u64, pub unk2: u32, pub unk3: u32, @@ -143,6 +169,13 @@ pub trait Friends { ), ErrorCode, >; + #[method_id(13)] + async fn update_presence(&self, presence: NintendoPresenceV2) -> Result<(), ErrorCode>; + #[method_id(18)] + async fn delete_persistent_notification( + &self, + notifs: Vec, + ) -> 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 e3f1364..a34b265 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..ae31467 --- /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(100, 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..0f531d8 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, @@ -13,10 +13,7 @@ impl RmcSerialize for Any { self.name.serialize(writer)?; let u32_len = self.data.len() as u32; - - u32_len.serialize(writer)?; - u32_len.serialize(writer)?; - + (u32_len + 4).serialize(writer)?; self.data.serialize(writer)?; Ok(()) @@ -26,12 +23,23 @@ impl RmcSerialize for Any { // also length ? let _len2: u32 = reader.read_struct(IS_BIG_ENDIAN)?; - let length: u32 = reader.read_struct(IS_BIG_ENDIAN)?; - - let mut data = vec![0; length as usize]; - - reader.read_exact(&mut data)?; + let data = Vec::deserialize(reader)?; 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/data.rs b/rnex-core/src/rmc/structures/data.rs new file mode 100644 index 0000000..c6064ae --- /dev/null +++ b/rnex-core/src/rmc/structures/data.rs @@ -0,0 +1,5 @@ +use macros::RmcSerialize; + +#[derive(RmcSerialize, Debug, Clone, Copy)] +#[rmc_struct(0)] +pub struct Data {} diff --git a/rnex-core/src/rmc/structures/mod.rs b/rnex-core/src/rmc/structures/mod.rs index 459aeac..33de120 100644 --- a/rnex-core/src/rmc/structures/mod.rs +++ b/rnex-core/src/rmc/structures/mod.rs @@ -26,6 +26,7 @@ pub type Result = std::result::Result; pub mod any; pub mod buffer; pub mod connection_data; +pub mod data; pub mod helpers; pub mod list; pub mod matchmake; @@ -62,6 +63,9 @@ pub trait RmcSerialize { Ok(data) } + fn name() -> &'static str { + "NoNameSpecified" + } } impl RmcSerialize for () {