progress
Some checks failed
Build and Test / puyopuyo (push) Failing after 26s
Build and Test / splatoon-testfire (push) Failing after 27s
Build and Test / minecraft-wiiu (push) Failing after 2m34s
Build and Test / fast-racing-neo (push) Failing after 2m36s
Build and Test / mario-tennis (push) Failing after 2m38s
Build and Test / splatoon (push) Failing after 2m38s
Build and Test / wii-u-chat (push) Failing after 5m25s
Build and Test / friends (push) Successful in 6m34s
Build and Test / super-mario-maker (push) Failing after 6m43s
Build and Test / sonic-transformed (push) Failing after 6m58s
Build and Test / wii-sports-club (push) Failing after 7m9s

This commit is contained in:
Maple Nebel 2026-07-13 23:47:15 +02:00
commit f1d16e40a1
36 changed files with 730 additions and 1392 deletions

1079
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,8 @@ prudpv1 = { path = "../prudpv1", optional = true }
proxy-common = { path = "../proxy-common" } proxy-common = { path = "../proxy-common" }
cfg-if = "1.0.4" cfg-if = "1.0.4"
rnex-prudp = { path = "../rnex-prudp" } rnex-prudp = { path = "../rnex-prudp" }
rnex-server = { path = "../rnex-server" }
tracing = "0.1.44"
[features] [features]
prudpv0 = ["dep:prudpv0"] prudpv0 = ["dep:prudpv0"]

View file

@ -1,6 +1,6 @@
use proxy::edge_node_dc_callback; use proxy::edge_node_dc_callback;
use proxy_common::{ProxyStartupParam, setup_edge_node_connection}; use proxy_common::{ProxyStartupParam, setup_edge_node_connection};
use rnex_core::common::with_setup; use rnex_server::with_setup;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@ -11,6 +11,7 @@ async fn main() {
setup_edge_node_connection(&param, edge_node_dc_callback).await; setup_edge_node_connection(&param, edge_node_dc_callback).await;
proxy::start_insecure(param).await; proxy::start_insecure(param).await;
Ok(())
}) })
.await; .await;
} }

View file

@ -1,7 +1,7 @@
use std::process::abort; use std::process::abort;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use log::error; use tracing::error;
cfg_if! { cfg_if! {
if #[cfg(feature = "prudpv0")]{ if #[cfg(feature = "prudpv0")]{

View file

@ -1,6 +1,6 @@
use proxy::edge_node_dc_callback; use proxy::edge_node_dc_callback;
use proxy_common::{ProxyStartupParam, setup_edge_node_connection}; use proxy_common::{ProxyStartupParam, setup_edge_node_connection};
use rnex_core::common::with_setup; use rnex_server::with_setup;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@ -10,6 +10,7 @@ async fn main() {
setup_edge_node_connection(&param, edge_node_dc_callback).await; setup_edge_node_connection(&param, edge_node_dc_callback).await;
proxy::start_secure(param).await; proxy::start_secure(param).await;
Ok(())
}) })
.await; .await;
} }

View file

@ -8,14 +8,17 @@ workspace = true
[dependencies] [dependencies]
rnex-prudp = { path = "../rnex-prudp" } rnex-prudp = { path = "../rnex-prudp" }
rnex-util = { path = "../rnex-util" }
rnex-rmc = { path = "../rnex-rmc" }
tokio = { version = "1.47.0", features = ["full"] } tokio = { version = "1.47.0", features = ["full"] }
bytemuck = { version = "1.23.1", features = ["derive"] } bytemuck = { version = "1.23.1", features = ["derive"] }
typenum = "1.18.0" typenum = "1.18.0"
rc4 = "0.1.0" rc4 = "0.2.0"
cfg-if = "1.0.4" cfg-if = "1.0.4"
proxy-common = {path = "../proxy-common"} proxy-common = {path = "../proxy-common"}
hmac = "0.12.1" hmac = "0.13.0"
md-5 = "^0.10.6" md-5 = "0.11.0"
tracing = "0.1.44"
[features] [features]
prudpv0 = [] prudpv0 = []

View file

@ -3,13 +3,11 @@ use std::io::Write;
use hmac::Mac; use hmac::Mac;
use md5::{Digest, Md5}; use md5::{Digest, Md5};
use rc4::{KeyInit, Rc4, StreamCipher}; use rc4::{KeyInit, Rc4, StreamCipher};
use rnex_core::{ use rnex_prudp::{
PID, encryption::{DEFAULT_KEY, EncryptionPair},
prudp::{ types_flags::{TypesFlags, types::DATA},
encryption::{DEFAULT_KEY, EncryptionPair},
types_flags::{TypesFlags, types::DATA},
},
}; };
use rnex_util::PID;
use typenum::U5; use typenum::U5;
use crate::crypto::{ use crate::crypto::{
@ -19,7 +17,7 @@ use crate::crypto::{
}; };
pub struct InsecureInstance { pub struct InsecureInstance {
pair: EncryptionPair<Rc4<U5>>, pair: EncryptionPair<Rc4>,
self_signat: [u8; 4], self_signat: [u8; 4],
#[allow(dead_code)] #[allow(dead_code)]
remote_signat: [u8; 4], remote_signat: [u8; 4],
@ -41,8 +39,8 @@ impl CryptoInstance for InsecureInstance {
[0x78, 0x56, 0x34, 0x12] [0x78, 0x56, 0x34, 0x12]
} else { } else {
let mut hash = Md5::new(); let mut hash = Md5::new();
hash.write(ACCESS_KEY.as_bytes()).unwrap(); hash.update(ACCESS_KEY.as_bytes());
let mut hmac = <HmacMd5 as Mac>::new_from_slice(&hash.finalize().as_slice()) let mut hmac = HmacMd5::new_from_slice(&hash.finalize().as_slice())
.expect("unable to create hmac md5"); .expect("unable to create hmac md5");
hmac.update(data); hmac.update(data);
hmac.finalize().into_bytes()[0..4].try_into().unwrap() hmac.finalize().into_bytes()[0..4].try_into().unwrap()
@ -57,7 +55,7 @@ pub struct Insecure();
impl Crypto for Insecure { impl Crypto for Insecure {
type Instance = InsecureInstance; type Instance = InsecureInstance;
fn new() -> Self { async fn new() -> Self {
Self() Self()
} }
fn calculate_checksum(&self, data: &[u8]) -> u8 { fn calculate_checksum(&self, data: &[u8]) -> u8 {
@ -72,7 +70,9 @@ impl Crypto for Insecure {
) -> Option<(Self::Instance, Vec<u8>)> { ) -> Option<(Self::Instance, Vec<u8>)> {
Some(( Some((
InsecureInstance { InsecureInstance {
pair: EncryptionPair::init_both(|| Rc4::new(&DEFAULT_KEY)), pair: EncryptionPair::init_both(|| {
Rc4::new_from_slice(DEFAULT_KEY).expect("incorrect key size")
}),
self_signat, self_signat,
remote_signat, remote_signat,
}, },

View file

@ -1,17 +1,13 @@
use hmac::Mac; use hmac::Mac;
use md5::{Digest, Md5}; use md5::{Digest, Md5};
use rc4::{KeyInit, Rc4, StreamCipher}; use rc4::{KeyInit, Rc4, StreamCipher};
use rnex_core::{ use rnex_prudp::{
PID, encryption::EncryptionPair,
executables::common::SECURE_SERVER_ACCOUNT, ticket::read_secure_connection_data,
nex::account::Account, types_flags::{TypesFlags, types::DATA},
prudp::{
encryption::EncryptionPair,
ticket::read_secure_connection_data,
types_flags::{TypesFlags, types::DATA},
},
rmc::structures::RmcSerialize,
}; };
use rnex_rmc::serialization::RmcSerialize;
use rnex_util::{PID, account::Account};
use std::io::Write; use std::io::Write;
use typenum::U16; use typenum::U16;
@ -22,7 +18,7 @@ use crate::crypto::{
}; };
pub struct SecureInstance { pub struct SecureInstance {
pair: EncryptionPair<Rc4<U16>>, pair: EncryptionPair<Rc4>,
uid: PID, uid: PID,
self_signat: [u8; 4], self_signat: [u8; 4],
#[allow(dead_code)] #[allow(dead_code)]
@ -45,8 +41,8 @@ impl CryptoInstance for SecureInstance {
[0x78, 0x56, 0x34, 0x12] [0x78, 0x56, 0x34, 0x12]
} else { } else {
let mut hash = Md5::new(); let mut hash = Md5::new();
hash.write(ACCESS_KEY.as_bytes()).unwrap(); hash.update(ACCESS_KEY.as_bytes());
let mut hmac = <HmacMd5 as Mac>::new_from_slice(&hash.finalize().as_slice()) let mut hmac = HmacMd5::new_from_slice(&hash.finalize().as_slice())
.expect("unable to create hmac md5"); .expect("unable to create hmac md5");
hmac.update(data); hmac.update(data);
hmac.finalize().into_bytes()[0..4].try_into().unwrap() hmac.finalize().into_bytes()[0..4].try_into().unwrap()
@ -57,12 +53,16 @@ impl CryptoInstance for SecureInstance {
} }
} }
pub struct Secure(&'static Account); pub struct Secure(Account);
impl Crypto for Secure { impl Crypto for Secure {
type Instance = SecureInstance; type Instance = SecureInstance;
fn new() -> Self { async fn new() -> Self {
Self(&SECURE_SERVER_ACCOUNT) Self(
Account::from_nexact(2, "Quazal Rendez-Vous")
.await
.expect("unable to get account info"),
)
} }
fn calculate_checksum(&self, data: &[u8]) -> u8 { fn calculate_checksum(&self, data: &[u8]) -> u8 {
common_checksum(ACCESS_KEY, data) common_checksum(ACCESS_KEY, data)

View file

@ -1,5 +1,6 @@
use cfg_if::cfg_if; use cfg_if::cfg_if;
use rnex_core::{PID, prudp::types_flags::TypesFlags}; use rnex_prudp::types_flags::TypesFlags;
use rnex_util::PID;
mod common_crypto; mod common_crypto;
@ -12,7 +13,7 @@ pub trait CryptoInstance: Send + 'static {
pub trait Crypto: Send + Sync + 'static { pub trait Crypto: Send + Sync + 'static {
type Instance: CryptoInstance; type Instance: CryptoInstance;
fn new() -> Self; async fn new() -> Self;
fn calculate_checksum(&self, data: &[u8]) -> u8; fn calculate_checksum(&self, data: &[u8]) -> u8;
fn instantiate( fn instantiate(
&self, &self,

View file

@ -1,7 +1,7 @@
use cfg_if::cfg_if; use cfg_if::cfg_if;
cfg_if! { cfg_if! {
if #[cfg(feature = "prudpv0")] { if #[cfg(feature = "prudpv0")] {
use log::info; use tracing::info;
use proxy_common::ProxyStartupParam; use proxy_common::ProxyStartupParam;
use std::env; use std::env;
use std::net::SocketAddrV4; use std::net::SocketAddrV4;

View file

@ -1,6 +1,5 @@
use bytemuck::{Pod, Zeroable, try_from_bytes, try_from_bytes_mut}; use bytemuck::{Pod, Zeroable, try_from_bytes, try_from_bytes_mut};
use log::{info, warn}; use rnex_prudp::{
use rnex_core::prudp::{
types_flags::{ types_flags::{
TypesFlags, TypesFlags,
flags::HAS_SIZE, flags::HAS_SIZE,
@ -8,6 +7,7 @@ use rnex_core::prudp::{
}, },
virtual_port::VirtualPort, virtual_port::VirtualPort,
}; };
use tracing::{info, warn};
use crate::crypto::{Crypto, CryptoInstance}; use crate::crypto::{Crypto, CryptoInstance};

View file

@ -5,24 +5,22 @@ use std::{
time::Duration, time::Duration,
}; };
use log::{error, info, warn};
use proxy_common::{ProxyStartupParam, new_backend_connection}; use proxy_common::{ProxyStartupParam, new_backend_connection};
use rnex_core::{ use rnex_prudp::{
prudp::{ socket_addr::PRUDPSockAddr,
socket_addr::PRUDPSockAddr, types_flags::{
types_flags::{ flags::{ACK, NEED_ACK, RELIABLE},
flags::{ACK, NEED_ACK, RELIABLE}, types::{CONNECT, DATA, DISCONNECT, PING, SYN},
types::{CONNECT, DATA, DISCONNECT, PING, SYN},
},
}, },
util::{SendingBufferConnection, SplittableBufferConnection},
}; };
use rnex_util::{SendingBufferConnection, SplittableBufferConnection};
use tokio::{ use tokio::{
net::UdpSocket, net::UdpSocket,
spawn, spawn,
sync::{Mutex, RwLock}, sync::{Mutex, RwLock},
time::{Instant, sleep}, time::{Instant, sleep},
}; };
use tracing::{error, info, warn};
use crate::{ use crate::{
crypto::{Crypto, CryptoInstance}, crypto::{Crypto, CryptoInstance},
@ -528,7 +526,7 @@ impl<C: Crypto> Server<C> {
.expect("unable to bind socket"); .expect("unable to bind socket");
Self { Self {
socket, socket,
crypto: C::new(), crypto: C::new().await,
connections: RwLock::new(HashMap::new()), connections: RwLock::new(HashMap::new()),
param, param,
} }

View file

@ -4,11 +4,6 @@
#![allow(async_fn_in_trait)] #![allow(async_fn_in_trait)]
//#![warn(missing_docs)] //#![warn(missing_docs)]
#[cfg(feature = "big_pid")]
pub type PID = i64;
#[cfg(not(feature = "big_pid"))]
pub type PID = i32;
pub use ctor::ctor; pub use ctor::ctor;
pub mod prudp; pub mod prudp;

View file

@ -8,5 +8,8 @@ ctor = "1.0.8"
rnex-rmc = { path = "../../rnex-rmc" } rnex-rmc = { path = "../../rnex-rmc" }
rnex-base-protos = { path = "../base-protos" } rnex-base-protos = { path = "../base-protos" }
[feature]
datastore = []
[lints] [lints]
workspace = true workspace = true

View file

@ -22,14 +22,14 @@ pub struct Permission {
pub recipient_ids: Vec<PID>, pub recipient_ids: Vec<PID>,
} }
#[derive(RmcSerialize, Clone, Default)] #[derive(RmcSerialize, Clone, Default, Debug)]
#[rmc_struct(0)] #[rmc_struct(0)]
pub struct RatingInfoWithSlot { pub struct RatingInfoWithSlot {
pub slot: i8, pub slot: i8,
pub rating: RatingInfo, pub rating: RatingInfo,
} }
#[derive(RmcSerialize, Clone, Default)] #[derive(RmcSerialize, Clone, Default, Debug)]
#[rmc_struct(0)] #[rmc_struct(0)]
pub struct RatingInfo { pub struct RatingInfo {
pub total_value: i64, pub total_value: i64,
@ -46,7 +46,7 @@ pub struct GetMetaParam {
pub access_password: i64, pub access_password: i64,
} }
#[derive(RmcSerialize, Clone, Default)] #[derive(RmcSerialize, Clone, Default, Debug)]
#[rmc_struct(0)] #[rmc_struct(0)]
pub struct GetMetaInfo { pub struct GetMetaInfo {
pub dataid: i64, pub dataid: i64,

View file

@ -1,4 +1,5 @@
#![allow(async_fn_in_trait)] #![allow(async_fn_in_trait)]
#![cfg(feature = "datastore")]
pub mod datastore; pub mod datastore;
use datastore::{DataStore, RawDataStore, RawDataStoreInfo, RemoteDataStore}; use datastore::{DataStore, RawDataStore, RawDataStoreInfo, RemoteDataStore};

View file

@ -1,4 +1,3 @@
#[cfg(not(feature = "rmc_struct_header"))]
use std::io::Read; use std::io::Read;
use std::{ use std::{
fmt::Arguments, fmt::Arguments,
@ -102,7 +101,7 @@ pub fn read_struct<T: Sized, R: Read + ?Sized>(
version: u8, version: u8,
pred: impl FnOnce(&mut SubRead<R>) -> Result<T>, pred: impl FnOnce(&mut SubRead<R>) -> Result<T>,
) -> Result<T> { ) -> Result<T> {
use crate::rmc::structures::Error::VersionMismatch; use crate::serialization::Error::VersionMismatch;
use v_byte_helpers::IS_BIG_ENDIAN; use v_byte_helpers::IS_BIG_ENDIAN;
use v_byte_helpers::ReadExtensions; use v_byte_helpers::ReadExtensions;
let ver: u8 = reader.read_struct(IS_BIG_ENDIAN)?; let ver: u8 = reader.read_struct(IS_BIG_ENDIAN)?;

View file

@ -122,13 +122,7 @@ impl Secure for BaseUser {
println!("{:?}", station_urls); println!("{:?}", station_urls);
/* /*
let mut users = self.matchmake_manager.users.write().await; */
users.insert(cid, self.this.clone());
drop(users);
let mut users = self.matchmake_manager.users_by_pid.write().await;
users.insert(self.pid, self.this.clone());
drop(users);
*/
let stations = get_station_urls(&station_urls, self.addr, self.pid, cid).await?; let stations = get_station_urls(&station_urls, self.addr, self.pid, cid).await?;

View file

@ -10,12 +10,10 @@ rnex-base = { path = "../rnex-base" }
rnex-base-protos = { path = "../../rnex-protocols/base-protos" } rnex-base-protos = { path = "../../rnex-protocols/base-protos" }
rnex-ds-protos = { path = "../../rnex-protocols/ds-protos" } rnex-ds-protos = { path = "../../rnex-protocols/ds-protos" }
rnex-server = { path = "../../rnex-server" } rnex-server = { path = "../../rnex-server" }
sqlx = "0.9.0" sqlx = { version = "0.9.0", features = ["chrono"] }
tracing = "0.1.44" tracing = "0.1.44"
thiserror = "2.0.18" thiserror = "2.0.18"
chrono = "0.4.45" chrono = "0.4.45"
aws-sdk-s3 = "1.138.0"
aws-config = "1.9.0"
sha2 = "0.11.0" sha2 = "0.11.0"
hmac = "0.13.0" hmac = "0.13.0"
base64 = "0.22.1" base64 = "0.22.1"
@ -24,5 +22,8 @@ hex = "0.4.3"
urlencoding = "2.1.3" urlencoding = "2.1.3"
futures = "0.3.32" futures = "0.3.32"
[features]
datastore = []
[lints] [lints]
workspace = true workspace = true

View file

@ -1,5 +1,5 @@
use chrono::Utc; use chrono::Utc;
use futures::future::join_all; use futures::{TryStreamExt, future::join_all};
use rnex_base::user::BaseUser; use rnex_base::user::BaseUser;
use rnex_ds_protos::{ use rnex_ds_protos::{
LocalDatastoreProtocol, LocalDatastoreProtocol,
@ -11,8 +11,8 @@ use rnex_ds_protos::{
DataStorePrepareGetParam, DataStoreRateObjectParam, DataStoreRatingTarget, DataStorePrepareGetParam, DataStoreRateObjectParam, DataStoreRatingTarget,
DataStoreReportCourseParam, DataStoreReqGetInfo, DataStoreSearchParam, DataStoreReportCourseParam, DataStoreReqGetInfo, DataStoreSearchParam,
DataStoreUploadCourseRecordParam, GetMetaInfo, GetMetaParam, KeyValue, Permission, DataStoreUploadCourseRecordParam, GetMetaInfo, GetMetaParam, KeyValue, Permission,
PersistenceTarget, PreparePostParam, RatingInfo, RatingInfoWithSlot, PersistenceTarget, PreparePostParam, RateCustomRankingParam, RatingInfo,
RatingInitParamWithSlot, ReqPostInfo, RatingInfoWithSlot, RatingInitParamWithSlot, ReqPostInfo,
}, },
}; };
use rnex_rmc::{qbuffer::QBuffer, qresult::QResult, response::ErrorCode, rmc_struct}; use rnex_rmc::{qbuffer::QBuffer, qresult::QResult, response::ErrorCode, rmc_struct};
@ -20,12 +20,13 @@ use rnex_server::PassthroughInitModule;
use rnex_util::{PID, date_time::DateTime}; use rnex_util::{PID, date_time::DateTime};
use sqlx::query; use sqlx::query;
use std::convert; use std::convert;
use tracing::{error, info, warn}; use tracing::{error, info, instrument, warn};
use crate::{DatastoreManager, s3presigner::S3Presigner}; use crate::{DatastoreManager, s3presigner::S3Presigner};
// todo: refactor this further to make some of the helper functions attached to the user and some to // todo: refactor this further to make some of the helper functions attached to the user and some to
// the manager and also move the usages of pid into the helper functions attached to user // the manager and also move the usages of pid into the helper functions attached to user
#[derive(Debug)]
#[rmc_struct(DatastoreProtocol)] #[rmc_struct(DatastoreProtocol)]
pub struct DatastoreUser { pub struct DatastoreUser {
pub base: PassthroughInitModule<BaseUser>, pub base: PassthroughInitModule<BaseUser>,
@ -33,6 +34,7 @@ pub struct DatastoreUser {
} }
impl DatastoreUser { impl DatastoreUser {
#[instrument]
fn map_row_to_meta_info( fn map_row_to_meta_info(
&self, &self,
row_data_id: i64, row_data_id: i64,
@ -88,6 +90,7 @@ impl DatastoreUser {
} }
} }
#[instrument]
pub async fn check_object_availability( pub async fn check_object_availability(
&self, &self,
data_id: i64, data_id: i64,
@ -121,6 +124,7 @@ impl DatastoreUser {
Ok(()) Ok(())
} }
#[instrument]
pub async fn get_object_ratings( pub async fn get_object_ratings(
&self, &self,
data_id: i64, data_id: i64,
@ -158,6 +162,7 @@ impl DatastoreUser {
Ok(ratings) Ok(ratings)
} }
#[instrument]
pub async fn get_object_info_by_data_id( pub async fn get_object_info_by_data_id(
&self, &self,
data_id: i64, data_id: i64,
@ -210,6 +215,7 @@ impl DatastoreUser {
)) ))
} }
#[instrument]
async fn get_object_info_by_persistence_target( async fn get_object_info_by_persistence_target(
&self, &self,
target: PersistenceTarget, target: PersistenceTarget,
@ -271,6 +277,7 @@ impl DatastoreUser {
)) ))
} }
#[instrument]
async fn get_buffer_queues_by_data_id_and_slot( async fn get_buffer_queues_by_data_id_and_slot(
&self, &self,
data_id: i64, data_id: i64,
@ -300,13 +307,13 @@ impl DatastoreUser {
Ok(buffer_queues) Ok(buffer_queues)
} }
#[instrument]
fn verify_object_permission( fn verify_object_permission(
&self, &self,
owner_id: PID, owner_id: PID,
viewer_id: PID,
permission: &Permission, permission: &Permission,
) -> Result<(), ErrorCode> { ) -> Result<(), ErrorCode> {
if owner_id == viewer_id { if owner_id == self.base.pid {
return Ok(()); return Ok(());
} }
@ -315,7 +322,7 @@ impl DatastoreUser {
1 => Err(ErrorCode::DataStore_PermissionDenied), // Friends only, unimplemented 1 => Err(ErrorCode::DataStore_PermissionDenied), // Friends only, unimplemented
2 => { 2 => {
// Recipient IDs can read // Recipient IDs can read
if permission.recipient_ids.contains(&viewer_id) { if permission.recipient_ids.contains(&self.base.pid) {
Ok(()) Ok(())
} else { } else {
Err(ErrorCode::DataStore_PermissionDenied) Err(ErrorCode::DataStore_PermissionDenied)
@ -327,6 +334,7 @@ impl DatastoreUser {
} }
} }
#[instrument]
fn filter_properties_by_result_option(&self, meta_info: &mut GetMetaInfo, result_option: u8) { fn filter_properties_by_result_option(&self, meta_info: &mut GetMetaInfo, result_option: u8) {
if (result_option & 0x01) == 0 { if (result_option & 0x01) == 0 {
meta_info.meta_binary = QBuffer(Vec::new()); meta_info.meta_binary = QBuffer(Vec::new());
@ -339,6 +347,7 @@ impl DatastoreUser {
// No idea what the other things do. :shrug: // No idea what the other things do. :shrug:
} }
#[instrument]
async fn init_object_rating_slot(&self, data_id: i64, rating_param: RatingInitParamWithSlot) { async fn init_object_rating_slot(&self, data_id: i64, rating_param: RatingInitParamWithSlot) {
info!("running init object rating slot"); info!("running init object rating slot");
sqlx::query!( sqlx::query!(
@ -376,11 +385,12 @@ impl DatastoreUser {
.map_err(|e| { .map_err(|e| {
error!("DB Error: {:?}", e); error!("DB Error: {:?}", e);
ErrorCode::DataStore_NotFound ErrorCode::DataStore_NotFound
}); })?;
info!("done running"); info!("done running");
} }
// Dawg... // Dawg...
#[instrument]
async fn get_custom_rankings_by_data_ids( async fn get_custom_rankings_by_data_ids(
&self, &self,
application_id: u32, application_id: u32,
@ -431,6 +441,7 @@ impl DatastoreUser {
results results
} }
#[instrument]
async fn get_user_course_object_ids(&self, owner_pid: PID) -> Result<Vec<i64>, ErrorCode> { async fn get_user_course_object_ids(&self, owner_pid: PID) -> Result<Vec<i64>, ErrorCode> {
let rows = sqlx::query!( let rows = sqlx::query!(
r#" r#"
@ -459,6 +470,7 @@ impl DatastoreUser {
Ok(valid_ids) Ok(valid_ids)
} }
#[instrument]
fn get_blacklist_1(&self) -> Vec<String> { fn get_blacklist_1(&self) -> Vec<String> {
vec![ vec![
"けされ", "けされ",
@ -533,6 +545,7 @@ impl DatastoreUser {
.collect() .collect()
} }
#[instrument]
fn get_blacklist_2(&self) -> Vec<String> { fn get_blacklist_2(&self) -> Vec<String> {
vec![ vec![
"ゼロから", "ゼロから",
@ -548,6 +561,7 @@ impl DatastoreUser {
.collect() .collect()
} }
#[instrument]
fn get_blacklist_3(&self) -> Vec<String> { fn get_blacklist_3(&self) -> Vec<String> {
vec![ vec![
"いいね", "いいね",
@ -617,6 +631,7 @@ impl DatastoreUser {
.collect() .collect()
} }
#[instrument]
// couldn't find a better way to do this im going crazyy // couldn't find a better way to do this im going crazyy
async fn rate_object( async fn rate_object(
&self, &self,
@ -651,6 +666,7 @@ impl DatastoreUser {
Ok(rating) Ok(rating)
} }
#[instrument]
async fn change_meta_object_check( async fn change_meta_object_check(
&self, &self,
param: &DataStoreChangeMetaParam, param: &DataStoreChangeMetaParam,
@ -679,6 +695,7 @@ impl DatastoreUser {
Ok(()) Ok(())
} }
#[instrument]
async fn get_rating_with_slot_data_id( async fn get_rating_with_slot_data_id(
&self, &self,
dataid: i64, dataid: i64,
@ -713,6 +730,7 @@ impl DatastoreUser {
Ok(ratings) Ok(ratings)
} }
#[instrument]
pub async fn insert_buffer(&self, dataid: i64, slot: i32, buffer: &QBuffer) { pub async fn insert_buffer(&self, dataid: i64, slot: i32, buffer: &QBuffer) {
let db_now = Utc::now().naive_utc(); let db_now = Utc::now().naive_utc();
@ -756,8 +774,7 @@ impl DataStore for DatastoreUser {
.await? .await?
}; };
let current_pid = self.pid; self.verify_object_permission(meta_info.owner, &meta_info.permission)?;
self.verify_object_permission(meta_info.owner, current_pid, &meta_info.permission)?;
self.filter_properties_by_result_option(&mut meta_info, metaparam.result_option); self.filter_properties_by_result_option(&mut meta_info, metaparam.result_option);
@ -794,7 +811,7 @@ impl DataStore for DatastoreUser {
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17
) RETURNING data_id ) RETURNING data_id
"#, "#,
self.pid as i32, self.base.pid as i32,
postparam.size as i32, postparam.size as i32,
postparam.name, postparam.name,
postparam.data_type as i32, postparam.data_type as i32,
@ -815,7 +832,7 @@ impl DataStore for DatastoreUser {
.fetch_one(&self.dm.db_pool) .fetch_one(&self.dm.db_pool)
.await .await
.map_err(|e| { .map_err(|e| {
log::error!("DB Error: {:?}", e); error!("DB Error: {:?}", e);
ErrorCode::DataStore_NotFound ErrorCode::DataStore_NotFound
})?; })?;
@ -998,7 +1015,7 @@ impl DataStore for DatastoreUser {
], ],
10 => vec![35, 75, 96, 40, 5, 6], 10 => vec![35, 75, 96, 40, 5, 6],
_ => { _ => {
log::error!("unknown SMM app id: {}", appid); error!("unknown SMM app id: {}", appid);
return Err(ErrorCode::DataStore_Unknown); return Err(ErrorCode::DataStore_Unknown);
} }
}; };
@ -1081,7 +1098,7 @@ impl DataStore for DatastoreUser {
}; };
info!("verifying object permission"); info!("verifying object permission");
self.verify_object_permission(meta_info.owner, self.base.pid, &meta_info.permission)?; self.verify_object_permission(meta_info.owner, &meta_info.permission)?;
let key = format!("data/{}.bin", meta_info.dataid); let key = format!("data/{}.bin", meta_info.dataid);
let download_url = self.dm.s3_presigner.generate_presigned_get(&key); let download_url = self.dm.s3_presigner.generate_presigned_get(&key);
@ -1123,8 +1140,7 @@ impl DataStore for DatastoreUser {
res.meta_info.ratings = Vec::new(); res.meta_info.ratings = Vec::new();
} }
if course_search_param.result_option & 0x4 == 0 { if course_search_param.result_option & 0x4 == 0 {
res.meta_info.meta_binary = res.meta_info.meta_binary = QBuffer(Vec::new());
rnex_core::rmc::structures::qbuffer::QBuffer(Vec::new());
} }
if course_search_param.result_option & 0x20 == 0 { if course_search_param.result_option & 0x20 == 0 {
res.score = 0; res.score = 0;
@ -1180,9 +1196,7 @@ impl DataStore for DatastoreUser {
match info_result { match info_result {
Ok(mut meta) => { Ok(mut meta) => {
if let Err(e) = if let Err(e) = self.verify_object_permission(meta.owner, &meta.permission) {
self.verify_object_permission(meta.owner, self.base.pid, &meta.permission)
{
metas.push(GetMetaInfo::default()); metas.push(GetMetaInfo::default());
results.push(QResult::error(e)); results.push(QResult::error(e));
} else { } else {
@ -1255,7 +1269,7 @@ impl DataStore for DatastoreUser {
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17
) RETURNING data_id ) RETURNING data_id
"#, "#,
self.pid as i32, self.base.pid as i32,
param.post_param.size as i32, param.post_param.size as i32,
param.post_param.name, param.post_param.name,
param.post_param.data_type as i32, param.post_param.data_type as i32,
@ -1349,11 +1363,7 @@ impl DataStore for DatastoreUser {
.get_object_info_by_data_id(target.dataid, param.access_password) .get_object_info_by_data_id(target.dataid, param.access_password)
.await?; .await?;
info!("object info get complete"); info!("object info get complete");
self.verify_object_permission( self.verify_object_permission(object_info.owner, &object_info.permission)?;
object_info.owner,
self.base.pid,
&object_info.permission,
)?;
info!("object permission complete"); info!("object permission complete");
if fetch_ratings { if fetch_ratings {
@ -1386,11 +1396,11 @@ impl DataStore for DatastoreUser {
} }
async fn change_meta(&self, param: DataStoreChangeMetaParam) -> Result<(), ErrorCode> { async fn change_meta(&self, param: DataStoreChangeMetaParam) -> Result<(), ErrorCode> {
let object_info = get_object_info_by_data_id(param.dataid, 0).await?; let object_info = self.get_object_info_by_data_id(param.dataid, 0).await?;
verify_object_permission(object_info.owner, self.pid, &object_info.permission).await?; self.verify_object_permission(object_info.owner, &object_info.permission)?;
if param.modifies_flag & 0x08 != 0 { if param.modifies_flag & 0x08 != 0 {
change_meta_object_check(&param).await?; self.change_meta_object_check(&param).await?;
sqlx::query!( sqlx::query!(
r#"UPDATE datastore.objects SET period=$1 WHERE data_id=$2"#, r#"UPDATE datastore.objects SET period=$1 WHERE data_id=$2"#,
@ -1406,7 +1416,7 @@ impl DataStore for DatastoreUser {
} }
if param.modifies_flag & 0x10 != 0 { if param.modifies_flag & 0x10 != 0 {
change_meta_object_check(&param).await?; self.change_meta_object_check(&param).await?;
sqlx::query!( sqlx::query!(
r#"UPDATE datastore.objects SET meta_binary=$1 WHERE data_id=$2"#, r#"UPDATE datastore.objects SET meta_binary=$1 WHERE data_id=$2"#,
@ -1422,7 +1432,7 @@ impl DataStore for DatastoreUser {
} }
if param.modifies_flag & 0x80 != 0 { if param.modifies_flag & 0x80 != 0 {
change_meta_object_check(&param).await?; self.change_meta_object_check(&param).await?;
sqlx::query!( sqlx::query!(
r#"UPDATE datastore.objects SET data_type=$1 WHERE data_id=$2"#, r#"UPDATE datastore.objects SET data_type=$1 WHERE data_id=$2"#,
@ -1583,8 +1593,8 @@ impl DataStore for DatastoreUser {
"#, "#,
upload_course_record_param.dataid, upload_course_record_param.dataid,
upload_course_record_param.slot as i16, upload_course_record_param.slot as i16,
self.pid, self.base.pid,
self.pid, self.base.pid,
upload_course_record_param.score, upload_course_record_param.score,
now, now,
now now
@ -1641,7 +1651,7 @@ impl DataStore for DatastoreUser {
) -> Result<Vec<QResult>, ErrorCode> { ) -> Result<Vec<QResult>, ErrorCode> {
let mut results = Vec::new(); let mut results = Vec::new();
let client_pid = self.pid; let client_pid = self.base.pid;
for (param, buffer) in bufferparam.iter().zip(buffers.iter()) { for (param, buffer) in bufferparam.iter().zip(buffers.iter()) {
if param.slot == 0 { if param.slot == 0 {
@ -1713,7 +1723,7 @@ impl DataStore for DatastoreUser {
) )
"#, "#,
report_course_param.dataid, report_course_param.dataid,
self.pid, self.base.pid,
report_course_param.report_category as i16, report_course_param.report_category as i16,
report_course_param.report_reason report_course_param.report_reason
) )

View file

@ -1,3 +1,4 @@
#![cfg(feature = "datastore")]
use std::env; use std::env;
use rnex_server::{ConnectionInitData, RnexManager, RnexModule}; use rnex_server::{ConnectionInitData, RnexManager, RnexModule};
@ -9,20 +10,21 @@ use crate::{datastore::DatastoreUser, s3presigner::S3Presigner};
pub mod datastore; pub mod datastore;
pub(crate) mod s3presigner; pub(crate) mod s3presigner;
struct DatastoreManager { #[derive(Debug)]
pub struct DatastoreManager {
db_pool: PgPool, db_pool: PgPool,
s3_presigner: S3Presigner, s3_presigner: S3Presigner,
} }
struct DatastoreModule; pub struct DatastoreModule;
impl RnexManager for DatastoreManager { impl RnexManager for DatastoreManager {
type User = DatastoreUser; type User = DatastoreUser;
type InitData = ConnectionInitData; type InitData = ConnectionInitData;
async fn init_new_user( async fn init_new_user(
this: rnex_server::PassthroughInitModule<Self>, this: rnex_server::PassthroughInitModule<Self>,
mod_holder: &rnex_server::ModuleHolder, mod_holder: &rnex_server::ModuleHolder,
remote: &rnex_rmc::RmcConnection, _: &rnex_rmc::RmcConnection,
init_data: &Self::InitData, _: &Self::InitData,
weak_user: rnex_server::WeakPassthroughInitModule<Self::User>, _: rnex_server::WeakPassthroughInitModule<Self::User>,
) -> Self::User { ) -> Self::User {
DatastoreUser { DatastoreUser {
dm: this, dm: this,

View file

@ -4,6 +4,7 @@ use hmac::{Hmac, KeyInit, Mac};
use serde_json::json; use serde_json::json;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
#[derive(Debug)]
pub struct S3Presigner { pub struct S3Presigner {
endpoint: String, endpoint: String,
bucket: String, bucket: String,

View file

@ -4,6 +4,22 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
bytemuck = { version = "1.25.1", features = ["derive"] }
rnex-rmc = { path = "../../rnex-rmc" }
rnex-util = { path = "../../rnex-util" }
rnex-fpd-protos = { path = "../../rnex-protocols/fpd-protos" }
rnex-base = { path = "../rnex-base" }
rnex-server = { path = "../../rnex-server" }
tokio = { version = "1.52.3", features = ["sync"] }
rand = "0.10.2"
tracing = "0.1.44"
sqlx = { version = "0.9.0", features = ["chrono"] }
chrono = "0.4.45"
hmac = "0.13.0"
md-5 = "0.11.0"
nex-account = { version = "0.2.4", registry = "spbr" }
hex = "0.4.3"
thiserror = "2.0.18"
[lints] [lints]
workspace = true workspace = true

View file

@ -1,92 +1,38 @@
use std::collections::HashMap; use std::{
use std::sync::{Arc, atomic::AtomicU32}; collections::HashMap,
use std::sync::{LazyLock, Weak}; mem,
use std::time::Duration; sync::{Arc, Weak},
use std::{env, mem}; time::Duration,
};
use crate::rmc::protocols::account_management::{
AccountExtraInfo, AccountManagement, RawAccountManagement, RawAccountManagementInfo,
RemoteAccountManagement,
};
use crate::rmc::protocols::friends_3ds::{
Friends3DS, RawFriends3DS, RawFriends3DSInfo, RemoteFriends3DS,
};
use crate::rmc::protocols::friends_wiiu::{
FriendsWiiU, RawFriendsWiiU, RawFriendsWiiUInfo, RemoteFriendsWiiU,
};
use crate::rmc::protocols::nintendo_notification::{
NintendoNotification, RawNintendoNotification, RawNintendoNotificationInfo,
RemoteNintendoNotification,
};
use crate::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure};
use crate::{
define_rmc_proto,
kerberos::KerberosDateTime,
nex::common::get_station_urls,
prudp::{socket_addr::PRUDPSockAddr, station_url::StationUrl},
rmc::{
protocols::friends_wiiu::{
BlacklistedPrincipal, Comment, FriendInfo, FriendRequest, NNAInfo, NintendoPresenceV2,
PersistentNotification, PrincipalPreference, PrincipalRequestBlockSetting,
},
response::ErrorCode,
structures::{any::Any, qresult::QResult},
},
};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
use rnex_rmc::rmc_struct; use nex_account::{derive_pid_hmac, grpc::ActCreateInfo, grpc_client};
use sqlx::query; use rnex_fpd_protos::{
use std::sync::atomic::Ordering::Relaxed; LocalFriendsGuest, LocalFriendsUser, RemoteFriendRemote,
use tokio::spawn; account_management::{AccountExtraInfo, AccountManagement, NintendoCreateAccountData},
use tokio::sync::RwLock; friends_3ds::{
use tokio::time::sleep; self, FriendComment, FriendMii, FriendMiiList, FriendPersistentInfo, FriendPicture,
use tracing::warn; FriendPresence, FriendRelationship, Friends3DS, Mii, MiiList, MyProfile, NintendoPresence,
use tracing::{error, info}; PlayedGame,
},
use crate::rmc::protocols::friends_wiiu::{GameKey, MiiV2, PrincipalBasicInfo}; friends_wiiu::{
self, BlacklistedPrincipal, Comment, FriendRequest, FriendRequestMessage, FriendsWiiU,
use crate::PID; MiiV2, NNAInfo, NintendoPresenceV2, PersistentNotification, PrincipalBasicInfo,
PrincipalPreference, PrincipalRequestBlockSetting,
use crate::rmc::protocols::account_management::NintendoCreateAccountData; },
use crate::rmc::protocols::nintendo_notification::NintendoNotificationEvent; nintendo_notification::{
NintendoNotificationEvent, NintendoNotificationEventGeneral, RemoteNintendoNotification,
use crate::rmc::structures::data::Data; },
use crate::executables::common::get_db;
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 rnex_rmc::{any::Any, data::Data, qbuffer::QBuffer, response::ErrorCode, rmc_struct};
use crate::rmc::protocols::nintendo_notification::NintendoNotificationEventGeneral; use rnex_server::{PassthroughInitModule, WeakPassthroughInitModule};
use crate::rmc::response::ErrorCode::FPD_InvalidArgument; use rnex_util::{PID, date_time::DateTime};
use crate::rmc::structures::qbuffer::QBuffer; use sqlx::query;
use nex_account::grpc::ActCreateInfo; use tokio::{spawn, sync::RwLock, time::sleep};
use nex_account::{derive_pid_hmac, grpc_client}; use tracing::{error, info, warn};
define_rmc_proto!( use crate::FriendsManager;
proto FriendsUser{
Secure,
FriendsWiiU,
Friends3DS
}
);
define_rmc_proto!(
proto FriendRemote{
NintendoNotification
}
);
define_rmc_proto!(
proto FriendsGuest{
Secure,
AccountManagement
}
);
static NEX_ACCOUNT_URL: LazyLock<String> =
LazyLock::new(|| env::var("NEX_ACCOUNT_ENDPOINT").expect("NEX_ACCOUNT_ENDPOINT not set"));
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Pod, Zeroable, Copy, Clone, Debug)] #[derive(Pod, Zeroable, Copy, Clone, Debug)]
@ -96,10 +42,11 @@ pub struct NascToken {
pub pwd_hash: [u8; 4], pub pwd_hash: [u8; 4],
} }
#[derive(Debug)]
#[rmc_struct(FriendsUser)] #[rmc_struct(FriendsUser)]
pub struct FriendsUser { pub struct FriendsUser {
pub fm: Arc<FriendsManager>, pub fm: PassthroughInitModule<FriendsManager>,
pub addr: PRUDPSockAddr, //pub addr: PRUDPSockAddr,
pub pid: PID, pub pid: PID,
pub friend_pids: RwLock<Vec<PID>>, pub friend_pids: RwLock<Vec<PID>>,
pub maybe_remote_friend: RwLock<HashMap<PID, Weak<FriendsUser>>>, pub maybe_remote_friend: RwLock<HashMap<PID, Weak<FriendsUser>>>,
@ -108,27 +55,14 @@ pub struct FriendsUser {
pub remote: RemoteFriendRemote, pub remote: RemoteFriendRemote,
} }
#[derive(Debug)]
#[rmc_struct(FriendsGuest)] #[rmc_struct(FriendsGuest)]
pub struct FriendsGuest { pub struct FriendsGuest;
pub fm: Arc<FriendsManager>,
pub addr: PRUDPSockAddr,
}
pub struct FriendsManager {
pub cid_counter: AtomicU32,
pub users: RwLock<HashMap<PID, Weak<FriendsUser>>>,
}
impl FriendsManager {
pub fn next_cid(&self) -> u32 {
self.cid_counter.fetch_add(1, Relaxed)
}
}
impl FriendsManager { impl FriendsManager {
async fn denies_friend_requests(&self, pid: PID) -> Result<bool, ErrorCode> { async fn denies_friend_requests(&self, pid: PID) -> Result<bool, ErrorCode> {
query!("select principal_preference_block_friend_requests from nintendo_network_accounts where pid = $1", pid) query!("select principal_preference_block_friend_requests from nintendo_network_accounts where pid = $1", pid)
.fetch_one(get_db()) .fetch_one(&self.db)
.await .await
.map_err(|_| ErrorCode::FPD_InvalidAccount) .map_err(|_| ErrorCode::FPD_InvalidAccount)
.map(|v| v.principal_preference_block_friend_requests) .map(|v| v.principal_preference_block_friend_requests)
@ -165,7 +99,7 @@ impl Friends3DS for FriendsUser {
async fn get_friend_mii( async fn get_friend_mii(
&self, &self,
_friends: Vec<crate::rmc::protocols::friends_3ds::FriendInfo>, _friends: Vec<friends_3ds::FriendInfo>,
) -> Result<Vec<FriendMii>, ErrorCode> { ) -> Result<Vec<FriendMii>, ErrorCode> {
// sorry for the copying pretendo but i don't have a mii on hand rn // sorry for the copying pretendo but i don't have a mii on hand rn
let data: Vec<u8> = vec![ let data: Vec<u8> = vec![
@ -196,7 +130,7 @@ impl Friends3DS for FriendsUser {
async fn get_friend_mii_list( async fn get_friend_mii_list(
&self, &self,
_friends: Vec<crate::rmc::protocols::friends_3ds::FriendInfo>, _friends: Vec<friends_3ds::FriendInfo>,
) -> Result<Vec<FriendMiiList>, ErrorCode> { ) -> Result<Vec<FriendMiiList>, ErrorCode> {
Err(ErrorCode::Core_NotImplemented) Err(ErrorCode::Core_NotImplemented)
} }
@ -204,7 +138,7 @@ impl Friends3DS for FriendsUser {
async fn is_active_game( async fn is_active_game(
&self, &self,
_unk: Vec<u32>, _unk: Vec<u32>,
_game_key: crate::rmc::protocols::friends_3ds::GameKey, _game_key: friends_3ds::GameKey,
) -> Result<Vec<u32>, ErrorCode> { ) -> Result<Vec<u32>, ErrorCode> {
Err(ErrorCode::Core_NotImplemented) Err(ErrorCode::Core_NotImplemented)
} }
@ -298,9 +232,9 @@ impl Friends3DS for FriendsUser {
async fn update_favorite_game_key( async fn update_favorite_game_key(
&self, &self,
game_key: rnex_core::rmc::protocols::friends_3ds::GameKey, game_key: friends_3ds::GameKey,
) -> Result<(), ErrorCode> { ) -> Result<(), ErrorCode> {
log::info!("favorite game key: {:?}", game_key); info!("favorite game key: {:?}", game_key);
Ok(()) Ok(())
} }
@ -314,7 +248,7 @@ impl Friends3DS for FriendsUser {
} }
async fn get_friend_presence(&self, unk: Vec<u32>) -> Result<Vec<FriendPresence>, ErrorCode> { async fn get_friend_presence(&self, unk: Vec<u32>) -> Result<Vec<FriendPresence>, ErrorCode> {
log::info!("pids: {:?}", unk); info!("pids: {:?}", unk);
let presence = FriendPresence { let presence = FriendPresence {
data: Data {}, data: Data {},
@ -322,7 +256,7 @@ impl Friends3DS for FriendsUser {
presence: NintendoPresence { presence: NintendoPresence {
data: Data {}, data: Data {},
changed_bit_flag: 0xFFFF_FFFF, changed_bit_flag: 0xFFFF_FFFF,
game_key: rnex_core::rmc::protocols::friends_3ds::GameKey { game_key: friends_3ds::GameKey {
data: Data {}, data: Data {},
title_id: 1_125_899_907_457_280, title_id: 1_125_899_907_457_280,
version: 2064, version: 2064,
@ -343,7 +277,7 @@ impl Friends3DS for FriendsUser {
async fn get_friend_comment( async fn get_friend_comment(
&self, &self,
_unk: Vec<crate::rmc::protocols::friends_3ds::FriendInfo>, _unk: Vec<friends_3ds::FriendInfo>,
) -> Result<Vec<FriendComment>, ErrorCode> { ) -> Result<Vec<FriendComment>, ErrorCode> {
Err(ErrorCode::Core_NotImplemented) Err(ErrorCode::Core_NotImplemented)
} }
@ -364,15 +298,15 @@ impl Friends3DS for FriendsUser {
area: 0, area: 0,
language: 0, language: 0,
platform: 0, platform: 0,
game_key: rnex_core::rmc::protocols::friends_3ds::GameKey { game_key: friends_3ds::GameKey {
data: Data {}, data: Data {},
title_id: 1_125_899_907_457_280, title_id: 1_125_899_907_457_280,
version: 2064, version: 2064,
}, },
message: "yo whats up".to_string(), message: "yo whats up".to_string(),
msg_updated_at: KerberosDateTime::now(), msg_updated_at: DateTime::now(),
friended_at: KerberosDateTime::now(), friended_at: DateTime::now(),
last_online: KerberosDateTime::now(), last_online: DateTime::now(),
}; };
Ok(vec![dummypersistentinfo]) Ok(vec![dummypersistentinfo])
@ -399,7 +333,7 @@ macro_rules! basic_principal_from_record {
nnid: $record.nnid.clone(), nnid: $record.nnid.clone(),
mii: MiiV2 { mii: MiiV2 {
data: Data {}, data: Data {},
date_time: KerberosDateTime(bytemuck::cast($record.mii_unk_datetime)), date_time: DateTime(bytemuck::cast($record.mii_unk_datetime)),
mii_data: $record.mii_ffl_data.clone(), mii_data: $record.mii_ffl_data.clone(),
name: QBuffer($record.mii_name.clone()), name: QBuffer($record.mii_name.clone()),
unk: mii_unk1, unk: mii_unk1,
@ -424,7 +358,7 @@ macro_rules! nna_info_from_record {
macro_rules! game_key_from_record { macro_rules! game_key_from_record {
($record:expr) => { ($record:expr) => {
GameKey { friends_wiiu::GameKey {
data: Data {}, data: Data {},
tid: $record.game_key_tid, tid: $record.game_key_tid,
version: $record.game_key_version, version: $record.game_key_version,
@ -440,7 +374,7 @@ macro_rules! friend_request_from_record {
basic_info: basic_principal_from_record!($record), basic_info: basic_principal_from_record!($record),
request_message: FriendRequestMessage { request_message: FriendRequestMessage {
data: Data {}, data: Data {},
expires_on: KerberosDateTime::from_naive( expires_on: DateTime::from_naive(
Utc.timestamp_opt(Utc::now().timestamp() + 2592000, 0) Utc.timestamp_opt(Utc::now().timestamp() + 2592000, 0)
.unwrap() .unwrap()
.naive_utc(), .naive_utc(),
@ -452,9 +386,9 @@ macro_rules! friend_request_from_record {
unk, unk,
unk2, unk2,
unk3: $record.unk_2, unk3: $record.unk_2,
unk4: KerberosDateTime(bytemuck::cast($record.unk_3)), unk4: DateTime(bytemuck::cast($record.unk_3)),
}, },
sent_on: KerberosDateTime::from_naive($record.creation_time), sent_on: DateTime::from_naive($record.creation_time),
} }
}}; }};
} }
@ -464,12 +398,12 @@ impl FriendsWiiU for FriendsUser {
&self, &self,
info: NNAInfo, info: NNAInfo,
presence: NintendoPresenceV2, presence: NintendoPresenceV2,
birthday: KerberosDateTime, birthday: DateTime,
) -> Result< ) -> Result<
( (
PrincipalPreference, PrincipalPreference,
Comment, Comment,
Vec<FriendInfo>, Vec<friends_wiiu::FriendInfo>,
Vec<FriendRequest>, Vec<FriendRequest>,
Vec<FriendRequest>, Vec<FriendRequest>,
Vec<BlacklistedPrincipal>, Vec<BlacklistedPrincipal>,
@ -512,7 +446,7 @@ impl FriendsWiiU for FriendsUser {
smoosh_to_i16(info.unk, info.unk2), smoosh_to_i16(info.unk, info.unk2),
bytemuck::cast::<_, i64>(birthday) bytemuck::cast::<_, i64>(birthday)
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await else { .await else {
println!("psql failed(unable to update user)"); println!("psql failed(unable to update user)");
return Err(ErrorCode::Core_SystemError) return Err(ErrorCode::Core_SystemError)
@ -528,7 +462,7 @@ impl FriendsWiiU for FriendsUser {
", ",
self.pid self.pid
) )
.fetch_all(get_db()) .fetch_all(&self.fm.db)
.await .await
.map(|v| { .map(|v| {
v.into_iter() v.into_iter()
@ -549,7 +483,7 @@ impl FriendsWiiU for FriendsUser {
", ",
self.pid self.pid
) )
.fetch_all(get_db()) .fetch_all(&self.fm.db)
.await .await
.map(|v| { .map(|v| {
v.into_iter() v.into_iter()
@ -580,7 +514,7 @@ impl FriendsWiiU for FriendsUser {
inner join nintendo_network_accounts on friendships.pid = nintendo_network_accounts.pid", inner join nintendo_network_accounts on friendships.pid = nintendo_network_accounts.pid",
self.pid self.pid
) )
.fetch_all(get_db()) .fetch_all(&self.fm.db)
.await .await
else { else {
println!("error whilest getting friends"); println!("error whilest getting friends");
@ -624,7 +558,7 @@ impl FriendsWiiU for FriendsUser {
drop(remo_friends); drop(remo_friends);
} }
friends.push(FriendInfo { friends.push(friends_wiiu::FriendInfo {
data: Data {}, data: Data {},
nna_info: nna_info_from_record!(friend), nna_info: nna_info_from_record!(friend),
presence, presence,
@ -632,10 +566,10 @@ impl FriendsWiiU for FriendsUser {
data: Data {}, data: Data {},
unk: (bytemuck::cast::<_, u16>(friend.comment_unk) & 0xFF) as u8, unk: (bytemuck::cast::<_, u16>(friend.comment_unk) & 0xFF) as u8,
message: friend.comment_message, message: friend.comment_message,
last_changed: KerberosDateTime::from_naive(friend.comment_lastchanged), last_changed: DateTime::from_naive(friend.comment_lastchanged),
}, },
became_friends: KerberosDateTime::from_naive(friend_since), became_friends: DateTime::from_naive(friend_since),
last_online: KerberosDateTime::from_naive(friend.last_online), last_online: DateTime::from_naive(friend.last_online),
unk: 0, unk: 0,
}); });
} }
@ -650,13 +584,13 @@ impl FriendsWiiU for FriendsUser {
", ",
self.pid self.pid
) )
.fetch_all(get_db()) .fetch_all(&self.fm.db)
.await .await
.map(|v| { .map(|v| {
v.into_iter() v.into_iter()
.map(|v| BlacklistedPrincipal { .map(|v| BlacklistedPrincipal {
basic_info: basic_principal_from_record!(v), basic_info: basic_principal_from_record!(v),
since: KerberosDateTime::from_naive(v.since), since: DateTime::from_naive(v.since),
..Default::default() ..Default::default()
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
@ -681,7 +615,7 @@ impl FriendsWiiU for FriendsUser {
}, },
Comment { Comment {
data: Data {}, data: Data {},
last_changed: KerberosDateTime::from_naive(query.comment_lastchanged), last_changed: DateTime::from_naive(query.comment_lastchanged),
message: query.comment_message, message: query.comment_message,
unk: (bytemuck::cast::<_, u16>(query.comment_unk) & 0xFF) as u8, unk: (bytemuck::cast::<_, u16>(query.comment_unk) & 0xFF) as u8,
}, },
@ -697,15 +631,18 @@ impl FriendsWiiU for FriendsUser {
))) )))
} }
async fn add_friend(&self, friend: PID) -> Result<(FriendRequest, FriendInfo), ErrorCode> { async fn add_friend(
&self,
friend: PID,
) -> Result<(FriendRequest, friends_wiiu::FriendInfo), ErrorCode> {
self.add_friend_request( self.add_friend_request(
friend, friend,
0, 0,
"".into(), "".into(),
0, 0,
"".into(), "".into(),
GameKey::default(), friends_wiiu::GameKey::default(),
KerberosDateTime::now(), DateTime::now(),
) )
.await .await
} }
@ -713,12 +650,12 @@ impl FriendsWiiU for FriendsUser {
async fn add_friend_by_name( async fn add_friend_by_name(
&self, &self,
name: String, name: String,
) -> Result<(FriendRequest, FriendInfo), ErrorCode> { ) -> Result<(FriendRequest, friends_wiiu::FriendInfo), ErrorCode> {
let Ok(pid) = query!( let Ok(pid) = query!(
"select pid from nintendo_network_accounts where nnid = $1", "select pid from nintendo_network_accounts where nnid = $1",
name name
) )
.fetch_optional(get_db()) .fetch_optional(&self.fm.db)
.await .await
else { else {
println!("db error when trying to look up nnid"); println!("db error when trying to look up nnid");
@ -737,7 +674,7 @@ impl FriendsWiiU for FriendsUser {
self.pid, self.pid,
friend friend
) )
.execute(get_db()) .execute(&self.fm.db)
.await .await
else { else {
return Err(ErrorCode::FPD_InvalidMessageID); return Err(ErrorCode::FPD_InvalidMessageID);
@ -790,9 +727,9 @@ impl FriendsWiiU for FriendsUser {
message: String, message: String,
_unk2: u8, _unk2: u8,
unk3: String, unk3: String,
game_key: GameKey, game_key: friends_wiiu::GameKey,
unk4: KerberosDateTime, unk4: DateTime,
) -> Result<(FriendRequest, FriendInfo), ErrorCode> { ) -> Result<(FriendRequest, friends_wiiu::FriendInfo), ErrorCode> {
let unk1 = 0; let unk1 = 0;
let unk2 = 1; let unk2 = 1;
@ -805,12 +742,12 @@ impl FriendsWiiU for FriendsUser {
"insert into friendships (pid_a, pid_b) values(99, $1)", "insert into friendships (pid_a, pid_b) values(99, $1)",
self.pid self.pid
) )
.execute(get_db()) .execute(&self.fm.db)
.await .await
.ok(); .ok();
let Ok(v) = query!("select * from nintendo_network_accounts where pid = 99") let Ok(v) = query!("select * from nintendo_network_accounts where pid = 99")
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
else { else {
return Err(ErrorCode::Core_Exception); return Err(ErrorCode::Core_Exception);
@ -829,22 +766,22 @@ impl FriendsWiiU for FriendsUser {
drop(friends); drop(friends);
let Ok(r) = query!("select * from nintendo_network_accounts where pid = 99") let Ok(r) = query!("select * from nintendo_network_accounts where pid = 99")
.fetch_one(get_db()) .fetch_one(&this.fm.db)
.await .await
else { else {
return; return;
}; };
let data = Any::new(&FriendInfo { let data = Any::new(&friends_wiiu::FriendInfo {
data: Data {}, data: Data {},
nna_info: nna_info_from_record!(r), nna_info: nna_info_from_record!(r),
became_friends: KerberosDateTime::now(), became_friends: DateTime::now(),
comment: Comment { comment: Comment {
data: Data {}, data: Data {},
last_changed: KerberosDateTime::from_naive(r.comment_lastchanged), last_changed: DateTime::from_naive(r.comment_lastchanged),
message: r.comment_message, message: r.comment_message,
unk: (bytemuck::cast::<_, u16>(r.comment_unk) & 0xFF) as u8, unk: (bytemuck::cast::<_, u16>(r.comment_unk) & 0xFF) as u8,
}, },
last_online: KerberosDateTime::now(), last_online: DateTime::now(),
presence: NintendoPresenceV2::default(), presence: NintendoPresenceV2::default(),
unk: 0, unk: 0,
}) })
@ -870,21 +807,21 @@ impl FriendsWiiU for FriendsUser {
unk2: 1, unk2: 1,
unk3: "Dummy".into(), unk3: "Dummy".into(),
game_key, game_key,
unk4: KerberosDateTime::now(), unk4: DateTime::now(),
expires_on: KerberosDateTime::now(), expires_on: DateTime::now(),
}, },
data: Data {}, data: Data {},
sent_on: KerberosDateTime::now(), sent_on: DateTime::now(),
}, },
FriendInfo { friends_wiiu::FriendInfo {
became_friends: KerberosDateTime::now(), became_friends: DateTime::now(),
comment: Comment { comment: Comment {
data: Data {}, data: Data {},
unk: bytemuck::cast::<_, u16>(v.comment_unk & 0xFF) as u8, unk: bytemuck::cast::<_, u16>(v.comment_unk & 0xFF) as u8,
message: v.comment_message, message: v.comment_message,
last_changed: KerberosDateTime::from_naive(v.comment_lastchanged), last_changed: DateTime::from_naive(v.comment_lastchanged),
}, },
last_online: KerberosDateTime::now(), last_online: DateTime::now(),
nna_info: nna_info_from_record!(v), nna_info: nna_info_from_record!(v),
..Default::default() ..Default::default()
}, },
@ -896,7 +833,7 @@ impl FriendsWiiU for FriendsUser {
friend, friend,
self.pid self.pid
) )
.fetch_optional(get_db()) .fetch_optional(&self.fm.db)
.await .await
else { else {
return Err(ErrorCode::Authentication_AccountLibraryError); return Err(ErrorCode::Authentication_AccountLibraryError);
@ -911,7 +848,7 @@ impl FriendsWiiU for FriendsUser {
"select count(recipient) from friend_requests where sender = $1", "select count(recipient) from friend_requests where sender = $1",
self.pid self.pid
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
else { else {
println!("friend request count check failed to execute on database"); println!("friend request count check failed to execute on database");
@ -924,7 +861,7 @@ impl FriendsWiiU for FriendsUser {
"select count(recipient) from friend_requests where recipient = $1", "select count(recipient) from friend_requests where recipient = $1",
friend friend
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
else { else {
println!("friend request count check failed to execute on database"); println!("friend request count check failed to execute on database");
@ -955,7 +892,7 @@ impl FriendsWiiU for FriendsUser {
game_key.tid, game_key.tid,
game_key.version game_key.version
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await; .await;
let query = match query { let query = match query {
@ -993,7 +930,7 @@ impl FriendsWiiU for FriendsUser {
"select * from nintendo_network_accounts where pid = $1", "select * from nintendo_network_accounts where pid = $1",
self.pid self.pid
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
else { else {
println!("failed to acquire account info after adding friend"); println!("failed to acquire account info after adding friend");
@ -1012,7 +949,7 @@ impl FriendsWiiU for FriendsUser {
dbg!(Ok(( dbg!(Ok((
fr, fr,
FriendInfo { friends_wiiu::FriendInfo {
presence: NintendoPresenceV2 { presence: NintendoPresenceV2 {
game_key, game_key,
app_data: vec![0x00], app_data: vec![0x00],
@ -1029,7 +966,7 @@ impl FriendsWiiU for FriendsUser {
bytemuck::cast::<_, i64>(id), bytemuck::cast::<_, i64>(id),
self.pid self.pid
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
else { else {
return Err(ErrorCode::FPD_InvalidMessageID); return Err(ErrorCode::FPD_InvalidMessageID);
@ -1055,8 +992,8 @@ impl FriendsWiiU for FriendsUser {
Ok(()) Ok(())
} }
async fn accept_friend_request(&self, id: u64) -> Result<FriendInfo, ErrorCode> { async fn accept_friend_request(&self, id: u64) -> Result<friends_wiiu::FriendInfo, ErrorCode> {
let Ok(mut tx) = get_db().begin().await else { let Ok(mut tx) = self.fm.db.begin().await else {
return Err(ErrorCode::Core_Exception); return Err(ErrorCode::Core_Exception);
}; };
let Ok(query) = query!( let Ok(query) = query!(
@ -1120,17 +1057,17 @@ impl FriendsWiiU for FriendsUser {
println!("internal server error whilest getting nna info"); println!("internal server error whilest getting nna info");
return Err(ErrorCode::Core_Exception); return Err(ErrorCode::Core_Exception);
}; };
let data = Any::new(&FriendInfo { let data = Any::new(&friends_wiiu::FriendInfo {
data: Data {}, data: Data {},
nna_info: nna_info_from_record!(r), nna_info: nna_info_from_record!(r),
became_friends: KerberosDateTime::now(), became_friends: DateTime::now(),
comment: Comment { comment: Comment {
data: Data {}, data: Data {},
last_changed: KerberosDateTime::from_naive(r.comment_lastchanged), last_changed: DateTime::from_naive(r.comment_lastchanged),
message: r.comment_message, message: r.comment_message,
unk: (bytemuck::cast::<_, u16>(r.comment_unk) & 0xFF) as u8, unk: (bytemuck::cast::<_, u16>(r.comment_unk) & 0xFF) as u8,
}, },
last_online: KerberosDateTime::now(), last_online: DateTime::now(),
presence: self.presence.read().await.clone().unwrap_or_default(), presence: self.presence.read().await.clone().unwrap_or_default(),
unk: 0, unk: 0,
}) })
@ -1155,18 +1092,18 @@ impl FriendsWiiU for FriendsUser {
tx.commit().await.ok(); tx.commit().await.ok();
Ok(FriendInfo { Ok(friends_wiiu::FriendInfo {
data: Data {}, data: Data {},
nna_info: nna_info_from_record!(query), nna_info: nna_info_from_record!(query),
presence, presence,
comment: Comment { comment: Comment {
data: Data {}, data: Data {},
last_changed: KerberosDateTime::from_naive(query.comment_lastchanged), last_changed: DateTime::from_naive(query.comment_lastchanged),
message: query.comment_message, message: query.comment_message,
unk: (bytemuck::cast::<_, u16>(query.comment_unk) & 0xFF) as u8, unk: (bytemuck::cast::<_, u16>(query.comment_unk) & 0xFF) as u8,
}, },
became_friends: KerberosDateTime::now(), became_friends: DateTime::now(),
last_online: KerberosDateTime::from_naive(query.last_online), last_online: DateTime::from_naive(query.last_online),
unk: 0, unk: 0,
}) })
} }
@ -1176,7 +1113,7 @@ impl FriendsWiiU for FriendsUser {
"delete from friend_requests where id = $1 returning sender, recipient", "delete from friend_requests where id = $1 returning sender, recipient",
bytemuck::cast::<_, i64>(id), bytemuck::cast::<_, i64>(id),
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
else { else {
return Err(ErrorCode::FPD_InvalidMessageID); return Err(ErrorCode::FPD_InvalidMessageID);
@ -1216,7 +1153,7 @@ impl FriendsWiiU for FriendsUser {
bytemuck::cast::<_, i64>(id), bytemuck::cast::<_, i64>(id),
self.pid self.pid
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
else { else {
return Err(ErrorCode::FPD_InvalidMessageID); return Err(ErrorCode::FPD_InvalidMessageID);
@ -1235,7 +1172,7 @@ impl FriendsWiiU for FriendsUser {
self.pid, self.pid,
query.sender query.sender
) )
.execute(get_db()) .execute(&self.fm.db)
.await .await
{ {
println!("{}", e); println!("{}", e);
@ -1264,18 +1201,18 @@ impl FriendsWiiU for FriendsUser {
"select * from nintendo_network_accounts where pid = $1", "select * from nintendo_network_accounts where pid = $1",
query.sender query.sender
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
else { else {
println!("attempt to get invalid user which is in friend request"); println!("attempt to get invalid user which is in friend request");
return Err(FPD_InvalidArgument); return Err(ErrorCode::FPD_InvalidArgument);
}; };
Ok(BlacklistedPrincipal { Ok(BlacklistedPrincipal {
data: Data {}, data: Data {},
basic_info: basic_principal_from_record!(user), basic_info: basic_principal_from_record!(user),
game_key: GameKey::default(), game_key: friends_wiiu::GameKey::default(),
since: KerberosDateTime::now(), since: DateTime::now(),
}) })
} }
@ -1285,7 +1222,7 @@ impl FriendsWiiU for FriendsUser {
"update friend_requests set is_recieved = true where id = $1", "update friend_requests set is_recieved = true where id = $1",
bytemuck::cast::<_, i64>(id) bytemuck::cast::<_, i64>(id)
) )
.execute(get_db()) .execute(&self.fm.db)
.await .await
.ok(); .ok();
} }
@ -1301,7 +1238,7 @@ impl FriendsWiiU for FriendsUser {
self.pid, self.pid,
principal.basic_info.pid principal.basic_info.pid
) )
.execute(get_db()) .execute(&self.fm.db)
.await .await
{ {
println!("{}", e); println!("{}", e);
@ -1312,18 +1249,18 @@ impl FriendsWiiU for FriendsUser {
"select * from nintendo_network_accounts where pid = $1", "select * from nintendo_network_accounts where pid = $1",
principal.basic_info.pid principal.basic_info.pid
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
else { else {
println!("attempt to get invalid user which is in friend request"); println!("attempt to get invalid user which is in friend request");
return Err(FPD_InvalidArgument); return Err(ErrorCode::FPD_InvalidArgument);
}; };
Ok(BlacklistedPrincipal { Ok(BlacklistedPrincipal {
data: Data {}, data: Data {},
basic_info: basic_principal_from_record!(user), basic_info: basic_principal_from_record!(user),
game_key: GameKey::default(), game_key: friends_wiiu::GameKey::default(),
since: KerberosDateTime::now(), since: DateTime::now(),
}) })
} }
@ -1333,7 +1270,7 @@ impl FriendsWiiU for FriendsUser {
self.pid, self.pid,
id id
) )
.execute(get_db()) .execute(&self.fm.db)
.await .await
{ {
println!("{}", e); println!("{}", e);
@ -1343,16 +1280,16 @@ impl FriendsWiiU for FriendsUser {
} }
async fn update_presence(&self, mut presence: NintendoPresenceV2) -> Result<(), ErrorCode> { async fn update_presence(&self, mut presence: NintendoPresenceV2) -> Result<(), ErrorCode> {
if !query!("select principal_preference_show_currently_playing_title from nintendo_network_accounts where pid = $1", self.pid).fetch_one(get_db()).await.map_err(|_| ErrorCode::FPD_InvalidAccount)?.principal_preference_show_currently_playing_title{ if !query!("select principal_preference_show_currently_playing_title from nintendo_network_accounts where pid = $1", self.pid).fetch_one(&self.fm.db).await.map_err(|_| ErrorCode::FPD_InvalidAccount)?.principal_preference_show_currently_playing_title{
presence.game_server_id = 0; presence.game_server_id = 0;
presence.game_key = GameKey::default(); presence.game_key = friends_wiiu::GameKey::default();
presence.app_data = vec![]; presence.app_data = vec![];
} }
if !query!( if !query!(
"select principal_preference_show_online from nintendo_network_accounts where pid = $1", "select principal_preference_show_online from nintendo_network_accounts where pid = $1",
self.pid self.pid
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
.map_err(|_| ErrorCode::FPD_InvalidAccount)? .map_err(|_| ErrorCode::FPD_InvalidAccount)?
.principal_preference_show_online .principal_preference_show_online
@ -1380,7 +1317,7 @@ impl FriendsWiiU for FriendsUser {
Ok(()) Ok(())
} }
async fn update_mii(&self, mii: MiiV2) -> Result<KerberosDateTime, ErrorCode> { async fn update_mii(&self, mii: MiiV2) -> Result<DateTime, ErrorCode> {
if let Err(e) = query!( if let Err(e) = query!(
" "
update nintendo_network_accounts update nintendo_network_accounts
@ -1392,7 +1329,7 @@ impl FriendsWiiU for FriendsUser {
bytemuck::cast::<_, i64>(mii.date_time.0), bytemuck::cast::<_, i64>(mii.date_time.0),
self.pid self.pid
) )
.execute(get_db()) .execute(&self.fm.db)
.await .await
{ {
println!("internal server error whilest updating mii: {}", e); println!("internal server error whilest updating mii: {}", e);
@ -1403,7 +1340,7 @@ impl FriendsWiiU for FriendsUser {
"select * from nintendo_network_accounts where pid = $1", "select * from nintendo_network_accounts where pid = $1",
self.pid self.pid
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
else { else {
println!("internal server error whilest getting nna info"); println!("internal server error whilest getting nna info");
@ -1425,10 +1362,10 @@ impl FriendsWiiU for FriendsUser {
.await; .await;
} }
Ok(KerberosDateTime::now()) Ok(DateTime::now())
} }
async fn update_comment(&self, comment: Comment) -> Result<KerberosDateTime, ErrorCode> { async fn update_comment(&self, comment: Comment) -> Result<DateTime, ErrorCode> {
if let Err(e) = query!( if let Err(e) = query!(
" "
update nintendo_network_accounts update nintendo_network_accounts
@ -1438,7 +1375,7 @@ impl FriendsWiiU for FriendsUser {
comment.message.clone(), comment.message.clone(),
self.pid self.pid
) )
.execute(get_db()) .execute(&self.fm.db)
.await .await
{ {
println!("internal server error whilest updating mii: {}", e); println!("internal server error whilest updating mii: {}", e);
@ -1463,7 +1400,7 @@ impl FriendsWiiU for FriendsUser {
.await; .await;
} }
Ok(KerberosDateTime::now()) Ok(DateTime::now())
} }
async fn update_preference(&self, preference: PrincipalPreference) -> Result<(), ErrorCode> { async fn update_preference(&self, preference: PrincipalPreference) -> Result<(), ErrorCode> {
@ -1480,7 +1417,7 @@ impl FriendsWiiU for FriendsUser {
preference.block_friend_request, preference.block_friend_request,
self.pid self.pid
) )
.execute(get_db()) .execute(&self.fm.db)
.await .await
{ {
println!("internal server error whilest updating mii: {e}"); println!("internal server error whilest updating mii: {e}");
@ -1494,14 +1431,14 @@ impl FriendsWiiU for FriendsUser {
if !preference.show_playing_title { if !preference.show_playing_title {
presence.game_server_id = 0; presence.game_server_id = 0;
presence.game_key = GameKey::default(); presence.game_key = friends_wiiu::GameKey::default();
presence.app_data = vec![]; presence.app_data = vec![];
} }
query!( query!(
"update nintendo_network_accounts set last_online = now() where pid = $1", "update nintendo_network_accounts set last_online = now() where pid = $1",
self.pid self.pid
) )
.execute(get_db()) .execute(&self.fm.db)
.await .await
.ok(); .ok();
let friends = self.maybe_remote_friend.read().await; let friends = self.maybe_remote_friend.read().await;
@ -1530,7 +1467,7 @@ impl FriendsWiiU for FriendsUser {
event_type: 10, event_type: 10,
sender: self.pid, sender: self.pid,
data: Any::new(&NintendoNotificationEventGeneral { data: Any::new(&NintendoNotificationEventGeneral {
param3: KerberosDateTime::now().0, param3: DateTime::now().0,
..Default::default() ..Default::default()
}) })
.expect("type error"), .expect("type error"),
@ -1551,7 +1488,7 @@ impl FriendsWiiU for FriendsUser {
"select * from nintendo_network_accounts where pid = $1", "select * from nintendo_network_accounts where pid = $1",
pid pid
) )
.fetch_one(get_db()) .fetch_one(&self.fm.db)
.await .await
else { else {
return Err(ErrorCode::FPD_NotFriend); return Err(ErrorCode::FPD_NotFriend);
@ -1591,7 +1528,7 @@ impl FriendsWiiU for FriendsUser {
pid, pid,
self.pid self.pid
) )
.fetch_optional(get_db()) .fetch_optional(&self.fm.db)
.await .await
else { else {
warn!("user requested request setting of invalid user"); warn!("user requested request setting of invalid user");
@ -1619,61 +1556,12 @@ impl FriendsWiiU for FriendsUser {
type HMacMd5 = hmac::Hmac<md5::Md5>; type HMacMd5 = hmac::Hmac<md5::Md5>;
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>,
_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,
) -> 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 Drop for FriendsUser { impl Drop for FriendsUser {
fn drop(&mut self) { fn drop(&mut self) {
let friends = mem::take(&mut self.maybe_remote_friend); let friends = mem::take(&mut self.maybe_remote_friend);
let users = friends.into_inner(); let users = friends.into_inner();
let pid = self.pid; let pid = self.pid;
let fm = self.fm.clone();
tokio::spawn(async move { tokio::spawn(async move {
for user in users { for user in users {
let Some(user) = user.1.upgrade() else { let Some(user) = user.1.upgrade() else {
@ -1685,7 +1573,7 @@ impl Drop for FriendsUser {
event_type: 10, event_type: 10,
sender: pid, sender: pid,
data: Any::new(&NintendoNotificationEventGeneral { data: Any::new(&NintendoNotificationEventGeneral {
param3: KerberosDateTime::now().0, param3: DateTime::now().0,
..Default::default() ..Default::default()
}) })
.expect("type error"), .expect("type error"),
@ -1696,7 +1584,7 @@ impl Drop for FriendsUser {
"update nintendo_network_accounts set last_online = now() where pid = $1", "update nintendo_network_accounts set last_online = now() where pid = $1",
pid pid
) )
.execute(get_db()) .execute(&fm.db)
.await .await
.ok(); .ok();
}); });

View file

@ -0,0 +1,102 @@
use crate::friends_handler::{FriendsGuest, FriendsUser};
use nex_account::GUEST_PID;
use rnex_fpd_protos::RemoteFriendRemote;
use rnex_rmc::{RmcCallable, RmcPureRemoteObject};
use rnex_server::{ConnectionInitData, RnexManager, RnexModule, WeakPassthroughInitModule};
use rnex_util::PID;
use sqlx::PgPool;
use std::{
collections::HashMap,
env,
sync::{Arc, Weak, atomic::AtomicU32},
};
use thiserror::Error;
use tokio::sync::RwLock;
pub mod friends_handler;
#[derive(Error, Debug)]
pub enum ModuleInitError {
#[error(transparent)]
Sqlx(#[from] sqlx::Error),
#[error(transparent)]
Env(#[from] env::VarError),
}
#[derive(Debug)]
pub enum FriendsMaybeGuest {
Guest(FriendsGuest),
User(Arc<FriendsUser>),
}
impl RmcCallable for FriendsMaybeGuest {
async fn rmc_call(
&self,
responder: &rnex_util::SendingBufferConnection,
protocol_id: u16,
method_id: u32,
call_id: u32,
rest: &[u8],
) -> bool {
match self {
FriendsMaybeGuest::Guest(friends_guest) => {
friends_guest
.rmc_call(responder, protocol_id, method_id, call_id, rest)
.await
}
FriendsMaybeGuest::User(friends_user) => {
friends_user
.rmc_call(responder, protocol_id, method_id, call_id, rest)
.await
}
}
}
}
#[derive(Debug)]
pub struct FriendsManager {
pub users: RwLock<HashMap<PID, Weak<FriendsUser>>>,
pub db: PgPool,
}
pub struct FriendsModule;
impl RnexManager for FriendsManager {
type InitData = ConnectionInitData;
type User = FriendsMaybeGuest;
async fn init_new_user(
mgr: rnex_server::PassthroughInitModule<Self>,
mod_holder: &rnex_server::ModuleHolder,
remote: &rnex_rmc::RmcConnection,
init_data: &Self::InitData,
weak_user: WeakPassthroughInitModule<Self::User>,
) -> Self::User {
if init_data.pid == GUEST_PID {
return FriendsMaybeGuest::Guest(FriendsGuest);
}
FriendsMaybeGuest::User(Arc::new_cyclic(|this| FriendsUser {
fm: mgr,
pid: init_data.pid,
friend_pids: Default::default(),
maybe_remote_friend: Default::default(),
presence: Default::default(),
this: this.clone(),
remote: RemoteFriendRemote::new(remote.clone()),
}))
}
}
impl RnexModule for FriendsModule {
type Manager = FriendsManager;
type InitError = ModuleInitError;
async fn create_manager(
mod_holder: &rnex_server::ModuleHolder,
) -> Result<Self::Manager, Self::InitError> {
Ok(FriendsManager {
users: Default::default(),
db: PgPool::connect(&env::var("RNEX_DATASTORE_DATABASE")?).await?,
})
}
}

View file

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

View file

@ -4,6 +4,9 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
rnex-rmc = { path = "../../rnex-rmc" }
rnex-msg-protos = { path = "../../rnex-protocols/msg-protos" }
rnex-server = { path = "../../rnex-server" }
tokio = { version = "1.52.3", features = ["sync"] }
[lints] [lints]
workspace = true workspace = true

View file

@ -0,0 +1,40 @@
use std::collections::HashMap;
use rnex_msg_protos::RemoteMessagingClient;
use rnex_rmc::{RmcPureRemoteObject, util::PID};
use rnex_server::{ConnectionInitData, RnexManager, WeakPassthroughInitModule};
use tokio::sync::RwLock;
use crate::user::MessagingUser;
pub mod user;
pub struct MessagingManager {
users_by_pid: RwLock<HashMap<PID, WeakPassthroughInitModule<MessagingUser>>>,
}
pub struct MessagingModule;
impl RnexManager for MessagingManager {
type User = MessagingUser;
type InitData = ConnectionInitData;
async fn init_new_user(
this: rnex_server::PassthroughInitModule<Self>,
mod_holder: &rnex_server::ModuleHolder,
remote: &rnex_rmc::RmcConnection,
init_data: &Self::InitData,
weak_user: rnex_server::WeakPassthroughInitModule<Self::User>,
) -> Self::User {
this.users_by_pid
.write()
.await
.insert(init_data.pid, weak_user);
MessagingUser {
msgm: this,
pid: init_data.pid,
remote: RemoteMessagingClient::new(remote.clone()),
}
}
}

View file

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

View file

@ -1,16 +1,32 @@
impl MessageDelivery for User { use crate::MessagingManager;
use rnex_msg_protos::message_delivery::RemoteMessageDeliveryNoResponse;
use rnex_msg_protos::{
LocalMessagingProtocol, RemoteMessagingClient, message_delivery::MessageDelivery,
messaging::UserMessage,
};
use rnex_rmc::{any::Any, response::ErrorCode, rmc_struct, util::PID};
use rnex_server::{PassthroughInitModule, WeakPassthroughInitModule};
#[rmc_struct(MessagingProtocol)]
pub struct MessagingUser {
pub msgm: PassthroughInitModule<MessagingManager>,
pub pid: PID,
pub remote: RemoteMessagingClient,
}
impl MessageDelivery for MessagingUser {
async fn deliver_message(&self, mut message: Any<UserMessage>) -> Result<(), ErrorCode> { async fn deliver_message(&self, mut message: Any<UserMessage>) -> Result<(), ErrorCode> {
let mut msg = message.get()?; let mut msg = message.get()?;
let _users = match msg.recipient_type { let _users = match msg.recipient_type {
1 => { 1 => {
let Some(user) = self let Some(user) = self
.matchmake_manager .msgm
.users_by_pid .users_by_pid
.read() .read()
.await .await
.get(&msg.recipient_id) .get(&msg.recipient_id)
.map(Weak::upgrade) .map(WeakPassthroughInitModule::upgrade)
.flatten() .flatten()
else { else {
return Err(ErrorCode::Core_InvalidArgument); return Err(ErrorCode::Core_InvalidArgument);

View file

@ -4,6 +4,15 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
rnex-rmc = { path = "../../rnex-rmc" }
rnex-util = { path = "../../rnex-util" }
rnex-rk-protos = { path = "../../rnex-protocols/rk-protos" }
rnex-server = { path = "../../rnex-server" }
serde = { version = "1.0.228", features = ["derive"] }
tracing = "0.1.44"
ureq = { version = "3.3.0", features = ["json"] }
tokio = { version = "1.52.3", features = ["rt"] }
serde_json = "1.0.150"
[lints] [lints]
workspace = true workspace = true

View file

@ -0,0 +1,86 @@
use std::env::{self, VarError};
use rnex_rmc::response::ErrorCode;
use rnex_server::{ConnectionInitData, RnexManager, RnexModule};
use std::str::FromStr;
use tracing::error;
use crate::user::RankingUser;
pub mod user;
pub struct RankingManager {
rnex_result_get: String,
rnex_result_votes_get: String,
rnex_result_post: String,
}
pub struct RankingModule;
impl RankingManager {
// Seperate function because I cannot give a fuck right now
async fn fetch_team_votes(&self, fest_id: u32) -> Result<Vec<u32>, ErrorCode> {
let url_votes = format!("{}?splatfest_id={}", self.rnex_result_votes_get, fest_id);
let Ok(response) = tokio::task::spawn_blocking(move || {
ureq::get(&url_votes).call().map_err(|e| {
error!("GET for votes failed: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})
})
.await
else {
error!("failed to make request");
return Err(ErrorCode::Core_Exception);
};
let mut response = response?;
let body = response.body_mut().read_to_string().map_err(|e| {
error!("failed to read votes body: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})?;
let body = body.trim().trim_start_matches('[').trim_end_matches(']');
let votes: Result<Vec<u32>, _> = body.split(',').map(|s| u32::from_str(s.trim())).collect();
votes.map_err(|e| {
error!("failed to parse votes: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})
}
}
impl RnexManager for RankingManager {
type User = RankingUser;
type InitData = ConnectionInitData;
async fn init_new_user(
this: rnex_server::PassthroughInitModule<Self>,
mod_holder: &rnex_server::ModuleHolder,
remote: &rnex_rmc::RmcConnection,
init_data: &Self::InitData,
weak_user: rnex_server::WeakPassthroughInitModule<Self::User>,
) -> Self::User {
RankingUser {
rm: this,
pid: init_data.pid,
}
}
}
impl RnexModule for RankingModule {
type Manager = RankingManager;
type InitError = VarError;
async fn create_manager(
_: &rnex_server::ModuleHolder,
) -> Result<Self::Manager, Self::InitError> {
Ok(RankingManager {
rnex_result_votes_get: env::var("RNEX_SPLATOON_RESULTS_VOTES_GET")?,
rnex_result_post: env::var("RNEX_SPLATOON_RESULTS_POST")?,
rnex_result_get: env::var("RNEX_SPLATOON_RESULTS_GET")?,
})
}
}

View file

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

View file

@ -1,3 +1,25 @@
use rnex_rk_protos::{
LocalRankingProtocol,
ranking::{
CompetitionRankingGetParam, CompetitionRankingScoreData, CompetitionRankingScoreInfo,
Ranking, UploadCompetitionData,
},
};
use rnex_rmc::{qbuffer::QBuffer, response::ErrorCode, rmc_struct};
use rnex_server::PassthroughInitModule;
use rnex_util::{PID, date_time::DateTime};
use serde::{Deserialize, Serialize};
use std::{env, str::FromStr};
use tracing::{error, info};
use crate::RankingManager;
#[rmc_struct(RankingProtocol)]
pub struct RankingUser {
pub rm: PassthroughInitModule<RankingManager>,
pub pid: PID,
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct CompetitionPostResults { pub struct CompetitionPostResults {
pub splatfest_id: u32, pub splatfest_id: u32,
@ -7,50 +29,20 @@ pub struct CompetitionPostResults {
pub user: PID, pub user: PID,
} }
// Seperate function because I cannot give a fuck right now impl Ranking for RankingUser {
async fn fetch_team_votes(fest_id: u32) -> Result<Vec<u32>, ErrorCode> {
let endpoint_votes = env::var("RNEX_SPLATOON_RESULTS_VOTES_GET").map_err(|_| {
error!("RNEX_SPLATOON_RESULTS_VOTES_GET not set");
ErrorCode::RendezVous_InvalidConfiguration
})?;
let url_votes = format!("{}?splatfest_id={}", endpoint_votes, fest_id);
let mut response = tokio::task::spawn_blocking(|| {
ureq::get(&url_votes).call().map_err(|e| {
error!("GET for votes failed: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})
})
.await?;
let body = response.body_mut().read_to_string().map_err(|e| {
error!("failed to read votes body: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})?;
let body = body.trim().trim_start_matches('[').trim_end_matches(']');
let votes: Result<Vec<u32>, _> = body.split(',').map(|s| u32::from_str(s.trim())).collect();
votes.map_err(|e| {
error!("failed to parse votes: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})
}
impl Ranking for User {
async fn competition_ranking_get_param( async fn competition_ranking_get_param(
&self, &self,
param: CompetitionRankingGetParam, param: CompetitionRankingGetParam,
) -> Result<Vec<CompetitionRankingScoreInfo>, ErrorCode> { ) -> Result<Vec<CompetitionRankingScoreInfo>, ErrorCode> {
let fest_id = param.festival_ids.get(0).copied().unwrap_or(0); let fest_id = param.festival_ids.get(0).copied().unwrap_or(0);
let endpoint_results = env::var("RNEX_SPLATOON_RESULTS_GET").map_err(|_| { let url_results = format!("{}?splatfest_id={}", self.rm.rnex_result_get, fest_id);
error!("RNEX_SPLATOON_RESULTS_GET not set"); let Ok(response_results) =
ErrorCode::RendezVous_InvalidConfiguration tokio::task::spawn_blocking(move || ureq::get(&url_results).call()).await
})?; else {
error!("failed to join task");
let url_results = format!("{}?splatfest_id={}", endpoint_results, fest_id); return Err(ErrorCode::Core_Exception);
let response_results = ureq::get(&url_results).call(); };
let results: Vec<CompetitionPostResults> = match response_results { let results: Vec<CompetitionPostResults> = match response_results {
Ok(mut res) => res.body_mut().read_json().map_err(|e| { Ok(mut res) => res.body_mut().read_json().map_err(|e| {
@ -69,7 +61,7 @@ impl Ranking for User {
let start = offset.min(results.len()); let start = offset.min(results.len());
let end = (start + size).min(results.len()); let end = (start + size).min(results.len());
let team_votes = fetch_team_votes(fest_id)?; let team_votes = self.rm.fetch_team_votes(fest_id).await?;
let mut wins = vec![0u32, 0u32]; let mut wins = vec![0u32, 0u32];
for r in &results { for r in &results {
let won_team = (r.team_id ^ (!r.team_win)) & 1; let won_team = (r.team_id ^ (!r.team_win)) & 1;
@ -84,7 +76,7 @@ impl Ranking for User {
unk: 1, unk: 1,
pid: r.user, pid: r.user,
score: r.score, score: r.score,
modified: KerberosDateTime::now(), modified: DateTime::now(),
unk2: 1, unk2: 1,
appdata: QBuffer(vec![]), appdata: QBuffer(vec![]),
}) })
@ -113,14 +105,6 @@ impl Ranking for User {
info!("team id: {:?}", param.team_id); info!("team id: {:?}", param.team_id);
info!("did current team win: {:?}", param.team_win); info!("did current team win: {:?}", param.team_win);
let endpoint = match env::var("RNEX_SPLATOON_RESULTS_POST") {
Ok(url) => url,
Err(_) => {
error!("RNEX_SPLATOON_RESULTS_POST not set");
return Ok(false);
}
};
let payload = CompetitionPostResults { let payload = CompetitionPostResults {
splatfest_id: param.splatfest_id, splatfest_id: param.splatfest_id,
score: param.score, score: param.score,
@ -137,9 +121,18 @@ impl Ranking for User {
} }
}; };
let response = ureq::post(&endpoint) let rm = self.rm.clone();
.header("Content-Type", "application/json")
.send(json_body); let Ok(response) = tokio::task::spawn_blocking(move || {
ureq::post(&rm.rnex_result_post)
.header("Content-Type", "application/json")
.send(json_body)
})
.await
else {
error!("unable to spawn blocking");
return Err(ErrorCode::Core_Exception);
};
match response { match response {
Ok(res) => { Ok(res) => {

View file

@ -10,6 +10,7 @@ chrono = "0.4.39"
bytemuck = { version = "1.25.0", features = ["derive"] } bytemuck = { version = "1.25.0", features = ["derive"] }
md-5 = "0.11.0" md-5 = "0.11.0"
thiserror = "2.0.18" thiserror = "2.0.18"
nex-account = { version = "0.2.4", registry = "spbr" }
[features] [features]
nx = [] nx = []

View file

@ -1,4 +1,5 @@
use md5::{Digest, Md5}; use md5::{Digest, Md5};
use nex_account::{grpc::Pid, grpc_client};
use crate::PID; use crate::PID;
@ -44,4 +45,17 @@ impl Account {
pub fn get_login_data(&self) -> (PID, [u8; 16]) { pub fn get_login_data(&self) -> (PID, [u8; 16]) {
(self.pid, self.nex_key) (self.pid, self.nex_key)
} }
pub async fn from_nexact(pid: PID, username: &str) -> Option<Self> {
let key: [u8; 16] = grpc_client()
.await
.ok()?
.get_nex_key_by_pid(Pid { pid })
.await
.ok()?
.into_inner()
.key
.try_into()
.ok()?;
Some(Self::new_raw_key(pid, username, key))
}
} }