rust-nex/rnex-core/src/nex/friends_handler.rs

389 lines
12 KiB
Rust
Raw Normal View History

2026-04-12 18:56:08 +02:00
use std::io::{Cursor, Write};
2026-04-25 14:15:42 +02:00
use std::ops::Deref;
use std::sync::Weak;
2026-02-01 20:59:23 +01:00
use std::sync::{Arc, atomic::AtomicU32};
2026-04-12 18:56:08 +02:00
use bytemuck::bytes_of;
use hmac::Mac;
2026-02-01 20:59:23 +01:00
use log::info;
use macros::rmc_struct;
use rnex_core::rmc::protocols::account_management::{
AccountManagement, RawAccountManagement, RawAccountManagementInfo, RemoteAccountManagement,
};
2026-02-01 20:59:23 +01:00
use rnex_core::rmc::protocols::friends::{Friends, RawFriends, RawFriendsInfo, RemoteFriends};
2026-04-25 14:15:42 +02:00
use rnex_core::rmc::protocols::nintendo_notification::{
NintendoNotification, RawNintendoNotification, RawNintendoNotificationInfo,
RemoteNintendoNotification,
};
2026-02-01 20:59:23 +01:00
use rnex_core::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure};
use rnex_core::{
define_rmc_proto,
kerberos::KerberosDateTime,
nex::common::get_station_urls,
prudp::{socket_addr::PRUDPSockAddr, station_url::StationUrl},
rmc::{
protocols::friends::{
BlacklistedPrincipal, Comment, FriendInfo, FriendRequest, NNAInfo, NintendoPresenceV2,
PersistentNotification, PrincipalPreference,
},
response::ErrorCode,
structures::{any::Any, qresult::QResult},
},
};
use std::sync::atomic::Ordering::Relaxed;
use tokio::spawn;
2026-04-25 14:15:42 +02:00
use tokio::sync::RwLock;
2026-02-01 20:59:23 +01:00
use rnex_core::rmc::protocols::friends::{GameKey, MiiV2, PrincipalBasicInfo};
2026-03-24 15:48:56 +01:00
use rnex_core::PID;
2026-04-25 14:24:33 +02:00
use rnex_core::rmc::protocols::account_management::NintendoCreateAccountData;
use rnex_core::rmc::protocols::nintendo_notification::NintendoNotificationEvent;
2026-04-12 18:56:08 +02:00
use rnex_core::rmc::structures::RmcSerialize;
2026-04-25 18:49:18 +02:00
use rnex_core::rmc::structures::data::Data;
2026-04-25 18:40:32 +02:00
2026-02-01 20:59:23 +01:00
define_rmc_proto!(
proto FriendsUser{
Secure,
Friends
}
);
2026-04-25 14:15:42 +02:00
define_rmc_proto!(
proto FriendRemote{
NintendoNotification
}
);
2026-03-24 15:48:56 +01:00
define_rmc_proto!(
proto FriendsGuest{
Secure,
AccountManagement
2026-03-24 15:48:56 +01:00
}
);
2026-04-25 14:15:42 +02:00
pub struct UserData {
info: NNAInfo,
presence: NintendoPresenceV2,
}
2026-02-01 20:59:23 +01:00
#[rmc_struct(FriendsUser)]
pub struct FriendsUser {
pub fm: Arc<FriendsManager>,
pub addr: PRUDPSockAddr,
2026-03-24 15:48:56 +01:00
pub pid: PID,
2026-04-25 14:15:42 +02:00
pub data: RwLock<Option<UserData>>,
pub current_friends: RwLock<Vec<PID>>,
pub this: Weak<FriendsUser>,
pub remote: RemoteFriendRemote,
2026-03-24 15:48:56 +01:00
}
#[rmc_struct(FriendsGuest)]
pub struct FriendsGuest {
pub fm: Arc<FriendsManager>,
pub addr: PRUDPSockAddr,
2026-02-01 20:59:23 +01:00
}
pub struct FriendsManager {
pub cid_counter: AtomicU32,
2026-04-25 14:15:42 +02:00
pub users: RwLock<Vec<Weak<FriendsUser>>>,
2026-02-01 20:59:23 +01:00
}
impl FriendsManager {
pub fn next_cid(&self) -> u32 {
self.cid_counter.fetch_add(1, Relaxed)
}
}
2026-04-25 14:15:42 +02:00
pub fn friend_info_from_user(data: &UserData) -> FriendInfo {
FriendInfo {
2026-04-25 18:40:32 +02:00
data: Data {},
2026-04-25 14:15:42 +02:00
nna_info: data.info.clone(),
presence: data.presence.clone(),
comment: Comment {
data: Data {},
2026-04-25 14:15:42 +02:00
unk: 0,
2026-04-25 17:24:28 +02:00
message: "haii =w=".to_string(),
2026-04-25 14:15:42 +02:00
last_changed: KerberosDateTime::now(),
},
became_friends: KerberosDateTime::now(),
last_online: KerberosDateTime::now(),
unk: 0,
}
}
2026-02-01 20:59:23 +01:00
impl Friends for FriendsUser {
async fn update_and_get_all_information(
&self,
2026-04-25 14:15:42 +02:00
info: NNAInfo,
presence: NintendoPresenceV2,
2026-03-24 15:48:56 +01:00
_date_time: KerberosDateTime,
2026-02-01 20:59:23 +01:00
) -> Result<
(
PrincipalPreference,
Comment,
Vec<FriendInfo>,
Vec<FriendRequest>,
Vec<FriendRequest>,
Vec<BlacklistedPrincipal>,
bool,
Vec<PersistentNotification>,
bool,
),
ErrorCode,
> {
2026-04-25 15:06:00 +02:00
println!("updating own data");
2026-04-25 14:15:42 +02:00
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 {
2026-04-25 18:49:18 +02:00
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,
2026-02-01 20:59:23 +01:00
},
unk: 0
2026-02-01 20:59:23 +01:00
},
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
2026-02-01 20:59:23 +01:00
},
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
}];
2026-04-25 14:15:42 +02:00
2026-04-25 15:06:00 +02:00
println!("acquiring user and current friends locks");
2026-04-25 14:15:42 +02:00
let users = self.fm.users.read().await;
2026-04-25 15:06:00 +02:00
println!("started summing users");
2026-04-25 14:15:42 +02:00
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 {
2026-04-25 14:15:42 +02:00
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);
2026-04-25 14:15:42 +02:00
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);
2026-04-25 14:15:42 +02:00
}
}
2026-04-25 15:06:00 +02:00
println!("finished summing users");
2026-04-25 14:15:42 +02:00
drop(users);
2026-04-25 15:06:00 +02:00
println!("adding self to users");
2026-04-25 14:15:42 +02:00
let mut users = self.fm.users.write().await;
users.push(self.this.clone());
drop(users);
2026-04-25 15:06:00 +02:00
println!("done...");
2026-04-25 14:15:42 +02:00
Ok((
PrincipalPreference {
data: Data {},
2026-04-25 14:15:42 +02:00
block_friend_request: false,
show_online: false,
show_playing_title: false,
},
Comment {
data: Data {},
2026-04-25 14:15:42 +02:00
last_changed: KerberosDateTime::now(),
message: "".to_string(),
unk: 0,
},
fr_list,
2026-02-01 20:59:23 +01:00
vec![],
vec![],
vec![],
false,
vec![],
false,
))
}
2026-04-11 20:24:13 +02:00
2026-04-25 14:15:42 +02:00
async fn update_presence(&self, presence: NintendoPresenceV2) -> Result<(), ErrorCode> {
let mut data = self.data.write().await;
2026-04-25 15:06:00 +02:00
let Some(inner_data) = data.as_mut() else {
2026-04-25 14:15:42 +02:00
return Err(ErrorCode::RendezVous_PermissionDenied);
};
2026-04-25 15:06:00 +02:00
inner_data.presence = presence;
let Ok(any_self_fr_info) = Any::new(&inner_data.presence) else {
2026-04-25 14:15:42 +02:00
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(())
}
2026-04-25 17:24:28 +02:00
async fn delete_persistent_notification(
&self,
notifs: Vec<PersistentNotification>,
) -> Result<(), ErrorCode> {
Ok(())
}
2026-04-11 20:24:13 +02:00
async fn check_setting_status(&self) -> Result<u8, ErrorCode> {
Ok(0xFF)
}
2026-02-01 20:59:23 +01:00
}
2026-04-12 18:56:08 +02:00
type HMacMd5 = hmac::Hmac<md5::Md5>;
2026-02-01 20:59:23 +01:00
impl Secure for FriendsUser {
async fn register(
&self,
station_urls: Vec<StationUrl>,
) -> Result<(QResult, u32, StationUrl), ErrorCode> {
let cid = self.fm.next_cid();
Ok((
QResult::success(ErrorCode::Core_Unknown),
cid,
get_station_urls(&station_urls, self.addr, self.pid, cid).await?[0].clone(),
))
}
async fn register_ex(
&self,
station_urls: Vec<StationUrl>,
2026-03-24 15:48:56 +01:00
_data: Any,
) -> Result<(QResult, u32, StationUrl), ErrorCode> {
info!("register");
self.register(station_urls).await
}
async fn replace_url(&self, _target: StationUrl, _dest: StationUrl) -> Result<(), ErrorCode> {
Err(ErrorCode::Core_NotImplemented)
}
}
impl Secure for FriendsGuest {
async fn register(
&self,
station_urls: Vec<StationUrl>,
) -> Result<(QResult, u32, StationUrl), ErrorCode> {
let cid = self.fm.next_cid();
Ok((
QResult::success(ErrorCode::Core_Unknown),
cid,
get_station_urls(&station_urls, self.addr, 100, cid).await?[0].clone(),
))
}
async fn register_ex(
&self,
station_urls: Vec<StationUrl>,
_data: Any,
2026-02-01 20:59:23 +01:00
) -> Result<(QResult, u32, StationUrl), ErrorCode> {
info!("register");
self.register(station_urls).await
}
2026-03-24 15:48:56 +01:00
async fn replace_url(&self, _target: StationUrl, _dest: StationUrl) -> Result<(), ErrorCode> {
2026-02-01 20:59:23 +01:00
Err(ErrorCode::Core_NotImplemented)
}
}
impl AccountManagement for FriendsGuest {
async fn nintendo_create_account(
&self,
principal_name: String,
key: String,
groups: u32,
email: String,
auth_data: Any,
) -> Result<(PID, String), ErrorCode> {
println!("{}, {}, {}, {}", principal_name, key, groups, email);
2026-04-12 18:56:08 +02:00
if auth_data.name == "NintendoCreateAccountData" {
let Ok(data) =
NintendoCreateAccountData::deserialize(&mut Cursor::new(&auth_data.data))
else {
return Err(ErrorCode::Authentication_InvalidParam);
};
let pid = data.nna_info.principal_basic_info.pid;
info!("create account: {}", pid);
let Ok(mut mac) = HMacMd5::new_from_slice(key.as_bytes()) else {
return Err(ErrorCode::Authentication_InvalidParam);
};
mac.write_all(bytes_of(&pid))
.expect("failed to write to hmac???");
let mac = mac.finalize().into_bytes();
let hex_str = hex::encode(mac);
return Ok((pid, hex_str));
}
Err(ErrorCode::Core_NotImplemented)
}
}