diff --git a/Cargo.lock b/Cargo.lock index 543efb9..4423e9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,9 +45,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "async-trait" @@ -3909,22 +3909,13 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3936,67 +3927,34 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -4009,48 +3967,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/editions.yaml b/editions.yaml index df2ef8a..478ea3d 100644 --- a/editions.yaml +++ b/editions.yaml @@ -10,7 +10,7 @@ sonic-transformed: RNEX_DEFAULT_PORT: 10000 RNEX_ACCESS_KEY: "b26a3421" ac-new-leaf: - include-in-checkall: true + include-in-checkall: false features: - prudpv1 - datastore diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 31d4022..3885e40 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -1,11 +1,14 @@ +use std::collections::{HashMap, HashSet}; use std::env; use std::io::{Cursor, Write}; use std::ops::Deref; +use std::process::id; use std::sync::{Arc, atomic::AtomicU32}; use std::sync::{LazyLock, Weak}; use base64::{Engine as _, engine::general_purpose}; use bytemuck::{Pod, Zeroable, bytes_of}; +use futures::StreamExt; use hex::decode; use hmac::Mac; use log::info; @@ -56,10 +59,13 @@ use rnex_core::rmc::structures::data::Data; use crate::executables::common::get_db; +use crate::kerberos; use crate::rmc::protocols::friends_3ds::{ FriendComment, FriendMii, FriendMiiList, FriendPersistentInfo, FriendPicture, FriendPresence, FriendRelationship, Mii, MiiList, MyProfile, NintendoPresence, PlayedGame, }; +use crate::rmc::protocols::friends_wiiu::FriendRequestMessage; +use crate::rmc::protocols::nintendo_notification::NintendoNotificationEventGeneral; use nex_account::grpc::ActCreateInfo; use nex_account::grpc::nex_account_service_client::NexAccountServiceClient; use nex_account::{derive_pid_hmac, grpc_client}; @@ -86,11 +92,6 @@ define_rmc_proto!( static NEX_ACCOUNT_URL: LazyLock = LazyLock::new(|| env::var("NEX_ACCOUNT_ENDPOINT").expect("NEX_ACCOUNT_ENDPOINT not set")); -pub struct UserData { - info: NNAInfo, - presence: NintendoPresenceV2, -} - #[repr(C, packed)] #[derive(Pod, Zeroable, Copy, Clone, Debug)] pub struct NascToken { @@ -104,8 +105,9 @@ pub struct FriendsUser { pub fm: Arc, pub addr: PRUDPSockAddr, pub pid: PID, - pub data: RwLock>, - pub current_friends: RwLock>, + pub friend_pids: RwLock>, + pub maybe_remote_friend: RwLock>>, + pub presence: RwLock>, pub this: Weak, pub remote: RemoteFriendRemote, } @@ -118,7 +120,7 @@ pub struct FriendsGuest { pub struct FriendsManager { pub cid_counter: AtomicU32, - pub users: RwLock>>, + pub users: RwLock>>, } impl FriendsManager { @@ -371,12 +373,78 @@ impl Friends3DS for FriendsUser { } } +fn smoosh_to_i16(val1: u8, val2: u8) -> i16 { + bytemuck::cast(((val1 as u16) << 8) | val2 as u16) +} +fn unsmoosh_from_i16(val: i16) -> (u8, u8) { + let val: u16 = bytemuck::cast(val); + ((val & 0xFF00 >> 8) as u8, (val & 0xFF) as u8) +} +macro_rules! basic_principal_from_record { + ($record:expr) => {{ + let (mii_unk1, mii_unk2) = unsmoosh_from_i16($record.mii_unks); + PrincipalBasicInfo { + data: Data {}, + pid: $record.pid, + nnid: $record.nnid, + mii: MiiV2 { + data: Data {}, + date_time: KerberosDateTime::from_naive($record.mii_unk_datetime), + mii_data: $record.mii_ffl_data, + name: $record.mii_name, + unk: mii_unk1, + unk2: mii_unk2, + }, + unk: (bytemuck::cast::<_, u16>($record.principal_info_unk) & 0xFF) as u8, + } + }}; +} + +macro_rules! nna_info_from_record { + ($record:expr) => {{ + let (nna_unk1, nna_unk2) = unsmoosh_from_i16($record.nna_info_unks_2); + NNAInfo { + data: Data {}, + principal_basic_info: basic_principal_from_record!($record), + unk: nna_unk1, + unk2: nna_unk2, + } + }}; +} + +macro_rules! friend_request_from_record { + ($record:expr) => {{ + let (unk, unk2) = unsmoosh_from_i16($record.unks_1); + FriendRequest { + data: Data {}, + basic_info: basic_principal_from_record!($record), + request_message: FriendRequestMessage { + data: Data {}, + expires_on: KerberosDateTime::PRACTICALLY_NEVER, + friend_request_id: $record.id, + game_key: GameKey { + data: Data {}, + tid: $record.game_key_tid, + version: $record.game_key_version, + }, + is_recieved: $record.is_recieved, + message: $record.message, + unk, + unk2, + unk3: $record.unk_2, + unk4: KerberosDateTime::from_naive($record.unk_3), + }, + sent_on: KerberosDateTime::from_naive($record.creation_time), + } + }}; +} + impl FriendsWiiU for FriendsUser { async fn update_and_get_all_information( &self, info: NNAInfo, presence: NintendoPresenceV2, - date_time: KerberosDateTime, + birthday: KerberosDateTime, ) -> Result< ( PrincipalPreference, @@ -391,39 +459,231 @@ impl FriendsWiiU for FriendsUser { ), ErrorCode, > { - // let query = query!("select ", self.pid).fetch_all(get_db()).await; + if info.principal_basic_info.pid == self.pid { + return Err(ErrorCode::FPD_InvalidArgument); + } + + *self.presence.write().await = Some(presence.clone()); + + let Ok(query) = query!(" + insert into nintendo_network_accounts ( + pid, nnid, mii_name, mii_unks, mii_ffl_data, mii_unk_datetime, principal_info_unk, nna_info_unks_2, birthday + ) values ( + $1, $2, $3, $4, $5, $6 , $7, $8, $9 + ) + on conflict (pid) + do update set + mii_name = excluded.mii_name, + mii_unks = excluded.mii_unks, + mii_ffl_data = excluded.mii_ffl_data, + mii_unk_datetime = excluded.mii_unk_datetime, + principal_info_unk = excluded.principal_info_unk, + nna_info_unks_2 = excluded.nna_info_unks_2, + birthday = excluded.birthday + returning comment_unk, comment_message, comment_lastchanged, principal_preference_show_online, principal_preference_show_currently_playing_title, principal_preference_block_friend_requests + ", + self.pid, + info.principal_basic_info.nnid, + info.principal_basic_info.mii.name, + smoosh_to_i16(info.principal_basic_info.mii.unk, info.principal_basic_info.mii.unk2), + info.principal_basic_info.mii.mii_data, + info.principal_basic_info.mii.date_time.to_regular_time().naive_utc(), + info.principal_basic_info.unk as i16, + smoosh_to_i16(info.unk, info.unk2), + birthday.to_regular_time().naive_utc() + ) + .fetch_one(get_db()) + .await else { + println!("psql failed(unable to update user)"); + return Err(ErrorCode::Core_SystemError) + }; + + let Ok(outgoing_friend_requests) = query!( + " + select + * + from friend_requests + inner join nintendo_network_accounts on recipient = nintendo_network_accounts.pid + where sender = $1 + ", + self.pid + ) + .fetch_all(get_db()) + .await + .map(|v| { + v.into_iter() + .map(|v| friend_request_from_record!(v)) + .collect::>() + }) else { + println!("error whilest getting friend requests"); + return Err(ErrorCode::Core_SystemError); + }; + + let Ok(incoming_friend_requests) = query!( + " + select + * + from friend_requests + inner join nintendo_network_accounts on sender = nintendo_network_accounts.pid + where recipient = $1 + ", + self.pid + ) + .fetch_all(get_db()) + .await + .map(|v| { + v.into_iter() + .map(|v| friend_request_from_record!(v)) + .collect::>() + }) else { + println!("error whilest getting friend requests"); + return Err(ErrorCode::Core_SystemError); + }; + + let Ok(friends_raw) = query!( + " + select + nintendo_network_accounts.pid, + since, + nnid, + comment_message, + comment_unk, + comment_lastchanged, + nna_info_unks_2, + principal_info_unk, + mii_name, + mii_unks, + mii_ffl_data, + mii_unk_datetime, + last_online + from friendships_of_pid($1) as friendships + inner join nintendo_network_accounts on friendships.pid = nintendo_network_accounts.pid", + self.pid + ) + .fetch_all(get_db()) + .await + else { + println!("error whilest getting friends"); + return Err(ErrorCode::Core_SystemError); + }; + + let mut friends = Vec::with_capacity(friends_raw.len()); + + for mut friend in friends_raw { + let Some(friend_since) = friend.since else { + println!("this should absolutely never happen(psql messed up somehow)"); + return Err(ErrorCode::Core_SystemError); + }; + + let mut presence = NintendoPresenceV2::default(); + + let mut current_friends = self.friend_pids.write().await; + current_friends.push(friend.pid); + drop(current_friends); + + let mut users = self.fm.users.read().await; + let user = users.get(&friend.pid).cloned(); + drop(users); + if let Some(user) = user { + if let Some(user) = user.upgrade() { + let online_presence = user.presence.read().await; + if let Some(online_presence) = online_presence.as_ref() { + presence = online_presence.clone(); + } else { + println!( + "internal server error, user is somehow online and in the friends manager yet has not set their presence yet..." + ); + } + drop(online_presence); + let mut other_friend_remotes = user.maybe_remote_friend.write().await; + other_friend_remotes.insert(self.pid, self.this.clone()); + drop(other_friend_remotes); + let mut remo_friends = self.maybe_remote_friend.write().await; + remo_friends.insert(friend.pid, Arc::downgrade(&user)); + drop(remo_friends); + } + } + + friends.push(FriendInfo { + data: Data {}, + nna_info: nna_info_from_record!(friend), + presence, + comment: Comment { + data: Data {}, + unk: (bytemuck::cast::<_, u16>(friend.comment_unk) & 0xFF) as u8, + message: friend.comment_message, + last_changed: KerberosDateTime::from_naive(friend.comment_lastchanged), + }, + became_friends: KerberosDateTime::from_naive(friend_since), + last_online: KerberosDateTime::from_naive(friend.last_online), + unk: 0, + }); + } + + self.fm + .users + .write() + .await + .insert(self.pid, self.this.clone()); + + ::update_presence(self, presence).await?; Ok(( PrincipalPreference { data: Data {}, - block_friend_request: false, - show_online: true, - show_playing_title: false, + block_friend_request: query.principal_preference_block_friend_requests, + show_online: query.principal_preference_show_online, + show_playing_title: query.principal_preference_show_currently_playing_title, }, Comment { data: Data {}, - last_changed: KerberosDateTime::now(), - message: "stub(will be impl'd later)".into(), - unk: 0, + last_changed: KerberosDateTime::from_naive(query.comment_lastchanged), + message: query.comment_message, + unk: (bytemuck::cast::<_, u16>(query.comment_unk) & 0xFF) as u8, }, - vec![], - vec![], - vec![], + friends, + outgoing_friend_requests, + incoming_friend_requests, + // todo: blacklisted principals vec![], false, + // todo: persistent notifications vec![], false, )) } async fn add_friend(&self, friend: PID) -> Result<(FriendRequest, FriendInfo), ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + self.add_friend_request( + friend, + 0, + "".into(), + 0, + "".into(), + GameKey::default(), + KerberosDateTime::now(), + ) + .await } async fn add_friend_by_name( &self, name: String, ) -> Result<(FriendRequest, FriendInfo), ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + let Ok(pid) = query!( + "select pid from nintendo_network_accounts where nnid = $1", + name + ) + .fetch_optional(get_db()) + .await + else { + println!("db error when trying to look up nnid"); + return Err(ErrorCode::Core_Exception); + }; + + match pid { + Some(pid) => self.add_friend(pid.pid).await, + None => Err(ErrorCode::FPD_InvalidAccount), + } } async fn remove_friend(&self, friend: PID) -> Result<(), ErrorCode> { @@ -440,7 +700,98 @@ impl FriendsWiiU for FriendsUser { game_key: GameKey, unk4: KerberosDateTime, ) -> Result<(FriendRequest, FriendInfo), ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + // check for too many friend requests both ways + let Ok(query) = query!( + "select count(recipient) from friend_requests where sender = $1", + self.pid + ) + .fetch_one(get_db()) + .await + else { + println!("friend request count check failed to execute on database"); + return Err(ErrorCode::Core_Exception); + }; + if query.count.is_none_or(|v| v >= 100) { + return Err(ErrorCode::FPD_RequestLimitExceed); + } + let Ok(query) = query!( + "select count(recipient) from friend_requests where recipient = $1", + friend + ) + .fetch_one(get_db()) + .await + else { + println!("friend request count check failed to execute on database"); + return Err(ErrorCode::Core_Exception); + }; + if query.count.is_none_or(|v| v >= 100) { + return Err(ErrorCode::FPD_RequestLimitExceed); + } + + let unks_1 = smoosh_to_i16(unk1, unk2); + let query = query!( + "with fr_base as (insert into friend_requests + (sender, recipient, message, unks_1, unk_2, unk_3, game_key_tid, game_key_version) + values ($1, $2, $3, $4, $5, $6, $7, $8) + returning *) + select * + from fr_base + inner join nintendo_network_accounts + on recipient = pid + ", + //inner join on recipient = pid + self.pid, + friend, + message, + unks_1, + unk3, + unk4.to_regular_time().naive_utc(), + game_key.tid, + game_key.version + ) + .fetch_one(get_db()) + .await; + + let query = match query { + Ok(q) => q, + Err(e) => { + if let Some(e) = e.into_database_error() { + match e.kind() { + sqlx::error::ErrorKind::UniqueViolation => { + return Err(ErrorCode::FPD_AddFriendProhibited); + } + sqlx::error::ErrorKind::ForeignKeyViolation => { + return Err(ErrorCode::FPD_NotNetworkAccount); + } + sqlx::error::ErrorKind::CheckViolation => { + return Err(ErrorCode::FPD_FriendRequestNotAllowed); + } + _ => { + println!("unknown db error occurred"); + return Err(ErrorCode::Core_Exception); + } + } + } else { + println!("unknown db error occurred"); + return Err(ErrorCode::Core_Exception); + } + } + }; + + let fr = friend_request_from_record!(query); + + let users = self.fm.users.read().await; + if let Some(user) = users.get(&friend).and_then(|v| v.upgrade()) { + user.remote + .process_nintendo_notification_event_2(NintendoNotificationEvent { + event_type: 25, + sender: self.pid, + data: Any::new(&fr).expect("type check failed"), + }) + .await; + } + + Ok((fr, Default::default())) } async fn cancel_friend_request(&self, id: u64) -> Result<(), ErrorCode> { @@ -448,7 +799,75 @@ impl FriendsWiiU for FriendsUser { } async fn accept_friend_request(&self, id: u64) -> Result { - Err(ErrorCode::Core_NotImplemented) + let Ok(query) = query!( + "delete from friend_requests where id = $1 and recipient = $2 returning recipient, sender", + bytemuck::cast::<_, i64>(id), + self.pid + ) + .fetch_one(get_db()) + .await + else { + return Err(ErrorCode::FPD_InvalidMessageID); + }; + + let sender = query.sender; + + match query!( + "insert into friendships (pid_a, pid_b) values ($1, $2)", + query.recipient, + query.sender + ) + .execute(get_db()) + .await + { + Ok(_) => {} + Err(e) => return Err(ErrorCode::FPD_FriendAlreadyAdded), + }; + + let Ok(query) = query!( + "select * from nintendo_network_accounts where pid = $1", + query.sender + ) + .fetch_one(get_db()) + .await + else { + println!("failed to acquire account info after adding friend"); + return Err(ErrorCode::FPD_InvalidMessageID); + }; + + let mut presence = NintendoPresenceV2::default(); + + let users = self.fm.users.read().await; + let user = users.get(&sender).cloned(); + drop(users); + if let Some(user) = user { + if let Some(user) = user.upgrade() { + let online_presence = user.presence.read().await; + if let Some(online_presence) = online_presence.as_ref() { + presence = online_presence.clone(); + } else { + println!( + "internal server error, user is somehow online and in the friends manager yet has not set their presence yet..." + ); + } + drop(online_presence); + } + } + + Ok(FriendInfo { + data: Data {}, + nna_info: nna_info_from_record!(query), + presence, + comment: Comment { + data: Data {}, + last_changed: KerberosDateTime::from_naive(query.comment_lastchanged), + message: query.comment_message, + unk: (bytemuck::cast::<_, u16>(query.comment_unk) & 0xFF) as u8, + }, + became_friends: KerberosDateTime::now(), + last_online: KerberosDateTime::from_naive(query.last_online), + unk: 0, + }) } async fn delete_friend_request(&self, id: u64) -> Result<(), ErrorCode> { @@ -460,6 +879,14 @@ impl FriendsWiiU for FriendsUser { } async fn mark_friend_requests_as_received(&self, ids: Vec) -> Result<(), ErrorCode> { + for id in ids { + query!( + "update friend_requests set is_recieved = true where id = $1", + bytemuck::cast::<_, i64>(id) + ) + .execute(get_db()) + .await; + } Err(ErrorCode::Core_NotImplemented) } @@ -475,23 +902,164 @@ impl FriendsWiiU for FriendsUser { } async fn update_presence(&self, presence: NintendoPresenceV2) -> Result<(), ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + let data = Any::new(&presence).expect("type error"); + let mut user_presence = self.presence.write().await; + *user_presence = Some(presence); + drop(user_presence); + + let friends = self.maybe_remote_friend.read().await; + for friend in friends.iter().filter_map(|f| f.1.upgrade()) { + friend + .remote + .process_nintendo_notification_event_2(NintendoNotificationEvent { + event_type: 24, + sender: self.pid, + data: data.clone(), + }) + .await; + } + Ok(()) } - async fn update_mii(&self, presence: MiiV2) -> Result { - Err(ErrorCode::Core_NotImplemented) + async fn update_mii(&self, mii: MiiV2) -> Result { + if let Err(e) = query!( + " + update nintendo_network_accounts + set mii_name = $1, mii_unks = $2, mii_ffl_data = $3, mii_unk_datetime = $4 + where pid = $5", + mii.name, + smoosh_to_i16(mii.unk, mii.unk2), + mii.mii_data, + mii.date_time.to_regular_time().naive_utc(), + self.pid + ) + .execute(get_db()) + .await + { + println!("internal server error whilest updating mii: {}", e); + return Err(ErrorCode::Core_Exception); + } + + let Ok(r) = query!( + "select * from nintendo_network_accounts where pid = $1", + self.pid + ) + .fetch_one(get_db()) + .await + else { + println!("internal server error whilest getting nna info"); + return Err(ErrorCode::Core_Exception); + }; + + let nna_info = nna_info_from_record!(r); + let data = Any::new(&nna_info).expect("type error"); + + let friends = self.maybe_remote_friend.read().await; + for friend in friends.iter().filter_map(|f| f.1.upgrade()) { + friend + .remote + .process_nintendo_notification_event_2(NintendoNotificationEvent { + event_type: 21, + sender: self.pid, + data: data.clone(), + }) + .await; + } + + Ok(KerberosDateTime::now()) } - async fn update_comment(&self, presence: Comment) -> Result { - Err(ErrorCode::Core_NotImplemented) + async fn update_comment(&self, comment: Comment) -> Result { + if let Err(e) = query!( + " + update nintendo_network_accounts + set comment_unk = $1, comment_message = $2, comment_lastchanged = now() + where pid = $3", + comment.unk as i16, + comment.message.clone(), + self.pid + ) + .execute(get_db()) + .await + { + println!("internal server error whilest updating mii: {}", e); + return Err(ErrorCode::Core_Exception); + } + + let data = Any::new(&NintendoNotificationEventGeneral { + param1: bytemuck::cast(self.pid), + str_param: comment.message, + ..Default::default() + }) + .expect("type error"); + let friends = self.maybe_remote_friend.read().await; + for friend in friends.iter().filter_map(|f| f.1.upgrade()) { + friend + .remote + .process_nintendo_notification_event_2(NintendoNotificationEvent { + event_type: 21, + sender: self.pid, + data: data.clone(), + }) + .await; + } + + Ok(KerberosDateTime::now()) } async fn update_preference(&self, preference: PrincipalPreference) -> Result<(), ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + let data = Any::new(&preference).expect("type error"); + + if let Err(e) = query!( + " + update nintendo_network_accounts + set + principal_preference_show_online = $1, principal_preference_show_currently_playing_title = $2, principal_preference_block_friend_requests = $3 + where pid = $4", + preference.show_online, + preference.show_playing_title, + preference.block_friend_request, + self.pid + ) + .execute(get_db()) + .await + { + println!("internal server error whilest updating mii: {}", e); + return Err(ErrorCode::Core_Exception); + } + + let friends = self.maybe_remote_friend.read().await; + for friend in friends.iter().filter_map(|f| f.1.upgrade()) { + friend + .remote + .process_nintendo_notification_event_2(NintendoNotificationEvent { + event_type: 23, + sender: self.pid, + data: data.clone(), + }) + .await; + } + Ok(()) } async fn get_basic_info(&self, pids: Vec) -> Result, ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + let mut principal_infos = Vec::with_capacity(pids.len()); + + for pid in pids { + let Ok(user) = query!( + "select * from nintendo_network_accounts where pid = $1", + pid + ) + .fetch_one(get_db()) + .await + else { + return Err(ErrorCode::FPD_InvalidPrincipalID); + }; + + principal_infos.push(basic_principal_from_record!(user)); + } + + Ok(principal_infos) } async fn delete_persistent_notification( @@ -521,10 +1089,6 @@ impl Secure for FriendsUser { station_urls: Vec, ) -> Result<(QResult, u32, StationUrl), ErrorCode> { let cid = self.fm.next_cid(); - let users = self.fm.users.read().await; - if users.iter().filter(|u| u.upgrade().is_some()).count() >= 100 { - return Err(ErrorCode::RendezVous_ConnectionFailure); - } Ok(( QResult::success(ErrorCode::Core_Unknown), cid, diff --git a/rnex-core/src/rmc/protocols/friends_wiiu.rs b/rnex-core/src/rmc/protocols/friends_wiiu.rs index 1764c93..49ef1a0 100644 --- a/rnex-core/src/rmc/protocols/friends_wiiu.rs +++ b/rnex-core/src/rmc/protocols/friends_wiiu.rs @@ -6,7 +6,7 @@ use rnex_core::rmc::structures::data::Data; use rnex_core::PID; -#[derive(RmcSerialize, Debug, Clone)] +#[derive(RmcSerialize, Debug, Clone, Default)] #[rmc_struct(0)] pub struct MiiV2 { #[extends] @@ -18,7 +18,7 @@ pub struct MiiV2 { pub date_time: KerberosDateTime, } -#[derive(RmcSerialize, Debug, Clone)] +#[derive(RmcSerialize, Debug, Clone, Default)] #[rmc_struct(0)] pub struct PrincipalBasicInfo { #[extends] @@ -29,7 +29,7 @@ pub struct PrincipalBasicInfo { pub unk: u8, } -#[derive(RmcSerialize, Debug, Clone)] +#[derive(RmcSerialize, Debug, Clone, Default)] #[rmc_struct(0)] pub struct NNAInfo { #[extends] @@ -39,16 +39,16 @@ pub struct NNAInfo { pub unk2: u8, } -#[derive(RmcSerialize, Clone, Copy, Debug)] +#[derive(RmcSerialize, Clone, Copy, Debug, Default)] #[rmc_struct(0)] pub struct GameKey { #[extends] pub data: Data, - pub tid: u64, - pub version: u16, + pub tid: i64, + pub version: i16, } -#[derive(RmcSerialize, Clone, Debug)] +#[derive(RmcSerialize, Clone, Debug, Default)] #[rmc_struct(0)] pub struct NintendoPresenceV2 { #[extends] @@ -79,7 +79,7 @@ pub struct PrincipalPreference { pub block_friend_request: bool, } -#[derive(RmcSerialize)] +#[derive(RmcSerialize, Default)] #[rmc_struct(0)] pub struct Comment { #[extends] @@ -89,7 +89,7 @@ pub struct Comment { pub last_changed: KerberosDateTime, } -#[derive(RmcSerialize)] +#[derive(RmcSerialize, Default)] #[rmc_struct(0)] pub struct FriendInfo { #[extends] @@ -107,8 +107,8 @@ pub struct FriendInfo { pub struct FriendRequestMessage { #[extends] pub data: Data, - pub friend_request_id: u64, - pub is_recieved: u8, + pub friend_request_id: i64, + pub is_recieved: bool, pub unk: u8, pub message: String, pub unk2: u8, diff --git a/rnex-core/src/rmc/protocols/nintendo_notification.rs b/rnex-core/src/rmc/protocols/nintendo_notification.rs index ae31467..0142cee 100644 --- a/rnex-core/src/rmc/protocols/nintendo_notification.rs +++ b/rnex-core/src/rmc/protocols/nintendo_notification.rs @@ -3,6 +3,8 @@ use macros::{RmcSerialize, method_id, rmc_proto}; use rnex_core::PID; use rnex_core::rmc::structures::any::Any; +use crate::rmc::structures::data::Data; + #[derive(RmcSerialize)] #[rmc_struct(0)] pub struct NintendoNotificationEvent { @@ -11,9 +13,11 @@ pub struct NintendoNotificationEvent { pub data: Any, } -#[derive(RmcSerialize)] +#[derive(RmcSerialize, Default)] #[rmc_struct(0)] pub struct NintendoNotificationEventGeneral { + #[extends] + pub data: Data, pub param1: u32, pub param2: u64, pub param3: u64, @@ -23,6 +27,8 @@ pub struct NintendoNotificationEventGeneral { #[derive(RmcSerialize)] #[rmc_struct(0)] pub struct NintendoNotificationEventProfile { + #[extends] + pub data: Data, pub region: u8, pub country: u8, pub area: u8, diff --git a/rnex-core/src/rmc/structures/data.rs b/rnex-core/src/rmc/structures/data.rs index c6064ae..26d4a0d 100644 --- a/rnex-core/src/rmc/structures/data.rs +++ b/rnex-core/src/rmc/structures/data.rs @@ -1,5 +1,5 @@ use macros::RmcSerialize; -#[derive(RmcSerialize, Debug, Clone, Copy)] +#[derive(RmcSerialize, Debug, Clone, Copy, Default)] #[rmc_struct(0)] pub struct Data {}