add wii u chat functions
All checks were successful
Build and Test / wii-u-chat (push) Successful in 8m1s
Build and Test / splatoon (push) Successful in 8m1s
Build and Test / friends (push) Successful in 8m7s
Build and Test / super-mario-maker (push) Successful in 12m38s

This commit is contained in:
Maple 2026-04-28 00:13:07 +02:00
commit c828d3b973
7 changed files with 297 additions and 203 deletions

View file

@ -44,7 +44,8 @@ guest_login = []
friends = ["guest_login", "database-support"]
big_pid = []
v3-3-2 = []
v3-8-15 = ["rmc_struct_header", "v3-3-2"]
v3-5-0 = ["v3-3-2"]
v3-8-15 = ["rmc_struct_header", "v3-5-0"]
v4-3-11 = ["v3-8-15"]
nx = ["big_pid"]
datastore = ["database-support", "v3-8-15", "dep:aws-sdk-s3", "dep:aws-config"]

View file

@ -1,127 +0,0 @@
#![allow(dead_code)]
#![allow(async_fn_in_trait)]
//#![warn(missing_docs)]
//! # Splatoon RNEX server
//!
//! This server still includes the code for rnex itself as this is the first rnex server and thus
//! also the first and only current usage of rnex, expect this and rnex to be split into seperate
//! repos soon.
extern crate self as rust_nex;
use once_cell::sync::Lazy;
use std::hint::black_box;
mod prudp;
pub mod rmc;
//mod protocols;
mod grpc;
mod kerberos;
mod nex;
mod result;
mod versions;
pub mod reggie;
pub mod util;
pub mod common;
pub mod config{
pub const FEATURE_HAS_STRUCT_HEADER: bool = cfg!(feature = "rmc_struct_header");
}
use std::io::Cursor;
use std::ops::Deref;
use rnex_core::kerberos::KerberosDateTime;
use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, Gathering, MatchmakeParam, MatchmakeSession, MatchmakeSessionSearchCriteria};
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::rmc::structures::variant::Variant;
static DUMMY: Lazy<AutoMatchmakeParam> = Lazy::new(|| AutoMatchmakeParam{
additional_participants: vec![1,2,3,4],
auto_matchmake_option: 10,
gid_for_participation_check: 9,
join_message: "hi".to_string(),
participation_count: 32,
target_gids: vec![45,2,51,1,1,1,1],
search_criteria: vec![MatchmakeSessionSearchCriteria{
attribs: vec!["hi".to_string(), "ig".to_string(), "gotta put data here".to_string()],
exclude_locked: true,
exclude_non_host_pid: false,
exclude_system_password_set: true,
exclude_user_password_set: false,
game_mode: "some gamemode".to_string(),
matchmake_param: MatchmakeParam{
params: vec![
("SR".to_string(), Variant::Bool(true)),
("SR2".to_string(), Variant::Double(1.0)),
("SR3".to_string(), Variant::SInt64(42)),
("SR4".to_string(), Variant::String("test".to_string()))
]
},
matchmake_system_type: "some type".to_string(),
maximum_participants: "???".to_string(),
minimum_participants: "-99".to_string(),
refer_gid: 123,
selection_method: 9999999,
vacant_only: true,
vacant_participants: 1000
}],
matchmake_session: MatchmakeSession{
refer_gid: 10,
matchmake_system_type: 139,
matchmake_param: MatchmakeParam{
params: vec![
("QSR".to_string(), Variant::Bool(false)),
("SRQ2".to_string(), Variant::Double(1.1)),
("SQR3".to_string(), Variant::SInt64(422)),
("SDR4".to_string(), Variant::String("tetst".to_string()))
]
},
participation_count: 99,
application_buffer: vec![1,2,3,4,5,6,7,8,9],
attributes: vec![10,20,99,100000],
datetime: KerberosDateTime::now(),
gamemode: 111,
open_participation: false,
option0: 100,
progress_score: 1,
system_password_enabled: false,
user_password: "aaa".to_string(),
session_key: vec![91,123,5,2,1,2,4,124,4],
user_password_enabled: false,
gathering: Gathering{
minimum_participants: 1,
maximum_participants: 12,
description: "aaargh".to_string(),
flags: 100,
host_pid: 999999919,
owner_pid: 138830,
participant_policy: 1,
policy_argument: 99837,
self_gid: 129,
state: 1389488
}
}
});
static DUMMY_SER: Lazy<Vec<u8>> = Lazy::new(|| serialize_to_vec(DUMMY.deref()));
fn serialize_to_vec(r: &impl RmcSerialize) -> Vec<u8>{
let vec = r.to_data();
vec.unwrap()
}
fn read_struct<T: RmcSerialize>(r: &[u8]) -> T{
T::deserialize(&mut Cursor::new(r)).unwrap()
}
fn main(){
for _ in 0..10000000 {
let v = serialize_to_vec(black_box(DUMMY.deref()));
let u = read_struct::<AutoMatchmakeParam>(black_box(DUMMY_SER.deref().as_slice()));
black_box(v);
black_box(u);
}
}

View file

@ -1,12 +1,12 @@
use crate::nex::user::User;
use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification};
use log::info;
use rand::random;
use rnex_core::PID;
use rnex_core::kerberos::KerberosDateTime;
use rnex_core::nex::user::User;
use rnex_core::rmc::protocols::notifications::notification_types::{
HOST_CHANGED, OWNERSHIP_CHANGED,
};
use rnex_core::rmc::protocols::notifications::{NotificationEvent, RemoteNotification};
use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::response::ErrorCode::{Core_InvalidArgument, RendezVous_SessionVoid};
use rnex_core::rmc::structures::matchmake::gathering_flags::PERSISTENT_GATHERING;
@ -155,28 +155,47 @@ impl ExtendedMatchmakeSession {
return Default::default();
};
let mm_session = MatchmakeSession {
gathering: Gathering {
self_gid: gid,
owner_pid: host.pid,
host_pid: host.pid,
..session.gathering.clone()
},
datetime: KerberosDateTime::now(),
session_key: (0..32).map(|_| random()).collect(),
matchmake_param: MatchmakeParam {
params: vec![
("@SR".to_owned(), Variant::Bool(true)),
("@GIR".to_owned(), Variant::SInt64(3)),
],
},
system_password_enabled: false,
..session
};
cfg_if::cfg_if! {
if #[cfg(feature = "v3-5-0")]{
let mm_session = MatchmakeSession {
gathering: Gathering {
self_gid: gid,
owner_pid: host.pid,
host_pid: host.pid,
..session.gathering.clone()
},
datetime: KerberosDateTime::now(),
session_key: (0..32).map(|_| random()).collect(),
matchmake_param: MatchmakeParam {
params: vec![
("@SR".to_owned(), Variant::Bool(true)),
("@GIR".to_owned(), Variant::SInt64(3)),
],
},
system_password_enabled: false,
..session
};
Self {
session: mm_session,
connected_players: Default::default(),
return Self {
session: mm_session,
connected_players: Default::default(),
}
} else {
let mm_session = MatchmakeSession {
gathering: Gathering {
self_gid: gid,
owner_pid: host.pid,
host_pid: host.pid,
..session.gathering.clone()
},
session_key: (0..32).map(|_| random()).collect(),
..session
};
return Self {
session: mm_session,
connected_players: Default::default(),
}
}
}
}
@ -212,6 +231,7 @@ impl ExtendedMatchmakeSession {
param_1: self.session.gathering.self_gid as PID,
param_2: other_pid,
str_param: "".into(),
#[cfg(feature = "v3-5-0")]
param_3: 0,
})
.await;
@ -244,6 +264,7 @@ impl ExtendedMatchmakeSession {
param_1: self.session.gathering.self_gid as PID,
param_2: *pid,
str_param: join_msg.clone(),
#[cfg(feature = "v3-5-0")]
param_3: self.connected_players.len() as _,
})
.await;
@ -266,6 +287,7 @@ impl ExtendedMatchmakeSession {
param_1: self.session.gathering.self_gid as PID,
param_2: new_conn_pid,
str_param: join_msg.clone(),
#[cfg(feature = "v3-5-0")]
param_3: self.connected_players.len() as _,
})
.await;
@ -318,15 +340,19 @@ impl ExtendedMatchmakeSession {
}
}
if search_criteria.exclude_system_password_set {
if self.session.system_password_enabled {
return Ok(false);
}
}
cfg_if::cfg_if! {
if #[cfg(feature = "v3-5-0")]{
if search_criteria.exclude_system_password_set {
if self.session.system_password_enabled {
return Ok(false);
}
}
if search_criteria.exclude_user_password_set {
if self.session.user_password_enabled {
return Ok(false);
if search_criteria.exclude_user_password_set {
if self.session.user_password_enabled {
return Ok(false);
}
}
}
}

View file

@ -1,26 +1,29 @@
use crate::define_rmc_proto;
use crate::nex::common::get_station_urls;
use crate::nex::matchmake::{ExtendedMatchmakeSession, MatchmakeManager};
use crate::nex::remote_console::RemoteConsole;
use crate::rmc::protocols::matchmake::{
Matchmake, RawMatchmake, RawMatchmakeInfo, RemoteMatchmake,
};
use crate::rmc::protocols::nat_traversal::{
NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal,
RemoteNatTraversalConsole,
};
use rnex_core::PID;
use rnex_core::define_rmc_proto;
use rnex_core::kerberos::KerberosDateTime;
use rnex_core::nex::common::get_station_urls;
use rnex_core::nex::matchmake::{ExtendedMatchmakeSession, MatchmakeManager};
use rnex_core::nex::remote_console::RemoteConsole;
use rnex_core::prudp::station_url::StationUrl;
use rnex_core::prudp::station_url::UrlOptions::{
Address, NatFiltering, NatMapping, Port, RVConnectionID,
};
use rnex_core::rmc::protocols::matchmake::{
Matchmake, RawMatchmake, RawMatchmakeInfo, RemoteMatchmake,
};
use rnex_core::rmc::protocols::matchmake_ext::{
MatchmakeExt, RawMatchmakeExt, RawMatchmakeExtInfo, RemoteMatchmakeExt,
};
use rnex_core::rmc::protocols::matchmake_extension::{
MatchmakeExtension, RawMatchmakeExtension, RawMatchmakeExtensionInfo, RemoteMatchmakeExtension,
};
use rnex_core::rmc::protocols::nat_traversal::{
NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal,
RemoteNatTraversalConsole,
};
use rnex_core::rmc::protocols::notifications::notification_types::{
END_GATHERING, REQUEST_JOIN_GATHERING,
};
use rnex_core::rmc::protocols::ranking::{Ranking, RawRanking, RawRankingInfo, RemoteRanking};
use rnex_core::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure};
use rnex_core::rmc::response::ErrorCode;
@ -32,11 +35,13 @@ use serde::{Deserialize, Serialize};
use std::env;
use std::str::FromStr;
use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification};
use cfg_if::cfg_if;
use log::{error, info};
use macros::rmc_struct;
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
use rnex_core::rmc::protocols::notifications::{
self, Notification, NotificationEvent, RemoteNotification,
};
use rnex_core::rmc::protocols::ranking::{
CompetitionRankingGetParam, CompetitionRankingScoreData, CompetitionRankingScoreInfo,
};
@ -248,6 +253,8 @@ impl MatchmakeExtension for User {
.await?;
let mut session = session.lock().await;
#[cfg(feature = "v3-5-0")]
if join_session_param.user_password != session.session.user_password {
return Err(ErrorCode::RendezVous_InvalidPassword);
}
@ -396,6 +403,101 @@ impl MatchmakeExtension for User {
Ok(())
}
async fn create_matchmake_session(
&self,
gathering: Any,
message: String,
) -> Result<(u32, Vec<u8>), ErrorCode> {
let Some(Ok(session)): Option<Result<MatchmakeSession, _>> = gathering.try_get() else {
return Err(ErrorCode::Core_InvalidArgument);
};
let session = self
.create_matchmake_session_with_param(CreateMatchmakeSessionParam {
matchmake_session: session,
additional_participants: vec![],
gid_for_participation_check: 0,
create_matchmake_session_option: 0,
join_message: message,
participation_count: 1,
})
.await?;
Ok((session.gathering.self_gid, session.session_key))
}
async fn get_friend_notification_data(
&self,
ty: i32,
) -> Result<Vec<NotificationEvent>, ErrorCode> {
Ok(vec![])
}
async fn update_notification_data(
&self,
ty: u32,
param_1: u32,
param_2: u32,
str_param: String,
) -> Result<(), ErrorCode> {
let recpipent = param_2;
let Some(user) = self
.matchmake_manager
.users
.read()
.await
.get(&recpipent)
.and_then(|v| v.upgrade())
else {
return Err(ErrorCode::Core_InvalidArgument);
};
match ty {
REQUEST_JOIN_GATHERING => {
user.remote
.process_notification_event(NotificationEvent {
pid_source: self.pid,
notif_type: REQUEST_JOIN_GATHERING * 1000,
param_1,
param_2,
#[cfg(feature = "v3-5-0")]
param_3: 0,
str_param,
})
.await;
}
END_GATHERING => {
user.remote
.process_notification_event(NotificationEvent {
pid_source: self.pid,
notif_type: END_GATHERING * 1000,
param_1,
param_2,
#[cfg(feature = "v3-5-0")]
param_3: 0,
str_param,
})
.await;
}
_ => {
return Err(ErrorCode::Core_InvalidArgument);
}
}
Ok(())
}
async fn join_matchmake_session_ex(
&self,
gid: u32,
message: String,
dont_care_block_list: bool,
participation_count: u16,
) -> Result<Vec<u8>, ErrorCode> {
let sess = self.matchmake_manager.get_session(gid).await?;
let mut sess = sess.lock().await;
sess.add_players(&[self.this.clone()], message).await;
Ok(sess.session.session_key.clone())
}
}
impl Matchmake for User {
@ -448,6 +550,7 @@ impl Matchmake for User {
pid_source: self.pid,
param_1: gid as PID,
param_2: self.pid,
#[cfg(feature = "v3-5-0")]
param_3: 0,
str_param: "".to_string(),
})
@ -469,6 +572,7 @@ impl Matchmake for User {
pid_source: self.pid,
param_1: gid as PID,
param_2: self.pid,
#[cfg(feature = "v3-5-0")]
param_3: 0,
str_param: "".to_string(),
})
@ -504,6 +608,7 @@ impl Matchmake for User {
pid_source: self.pid,
param_1: gid as PID,
param_2: *candidate as PID,
#[cfg(feature = "v3-5-0")]
param_3: 0,
str_param: "".to_string(),
})

View file

@ -1,17 +1,57 @@
use macros::{method_id, rmc_proto};
use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession};
use rnex_core::rmc::structures::matchmake::{
AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession,
};
use crate::rmc::protocols::notifications::NotificationEvent;
use crate::rmc::structures::any::Any;
#[rmc_proto(109)]
pub trait MatchmakeExtension{
pub trait MatchmakeExtension {
#[method_id(1)]
async fn close_participation(&self, gid: u32) -> Result<(), ErrorCode>;
#[method_id(2)]
async fn open_participation(&self, gid: u32) -> Result<(), ErrorCode>;
#[method_id(6)]
async fn create_matchmake_session(
&self,
gathering: Any,
message: String,
) -> Result<(u32, Vec<u8>), ErrorCode>;
#[method_id(9)]
async fn update_notification_data(
&self,
ty: u32,
param1: u32,
param2: u32,
str_param: String,
) -> Result<(), ErrorCode>;
#[method_id(10)]
async fn get_friend_notification_data(
&self,
ty: i32,
) -> Result<Vec<NotificationEvent>, ErrorCode>;
#[method_id(30)]
async fn join_matchmake_session_ex(
&self,
gid: u32,
message: String,
dont_care_block_list: bool,
participation_count: u16,
) -> Result<Vec<u8>, ErrorCode>;
#[method_id(8)]
async fn modify_current_game_attribute(&self, gid: u32, attrib_index: u32, attrib_val: u32) -> Result<(), ErrorCode>;
async fn modify_current_game_attribute(
&self,
gid: u32,
attrib_index: u32,
attrib_val: u32,
) -> Result<(), ErrorCode>;
#[method_id(16)]
async fn get_playing_session(&self, pids: Vec<u32>) -> Result<Vec<()>, ErrorCode>;
@ -19,14 +59,26 @@ pub trait MatchmakeExtension{
#[method_id(34)]
async fn update_progress_score(&self, gid: u32, progress: u8) -> Result<(), ErrorCode>;
#[method_id(38)]
async fn create_matchmake_session_with_param(&self, session: CreateMatchmakeSessionParam) -> Result<MatchmakeSession, ErrorCode>;
async fn create_matchmake_session_with_param(
&self,
session: CreateMatchmakeSessionParam,
) -> Result<MatchmakeSession, ErrorCode>;
#[method_id(39)]
async fn join_matchmake_session_with_param(&self, session: JoinMatchmakeSessionParam) -> Result<MatchmakeSession, ErrorCode>;
async fn join_matchmake_session_with_param(
&self,
session: JoinMatchmakeSessionParam,
) -> Result<MatchmakeSession, ErrorCode>;
#[method_id(40)]
async fn auto_matchmake_with_param_postpone(&self, session: AutoMatchmakeParam) -> Result<MatchmakeSession, ErrorCode>;
async fn auto_matchmake_with_param_postpone(
&self,
session: AutoMatchmakeParam,
) -> Result<MatchmakeSession, ErrorCode>;
#[method_id(41)]
async fn find_matchmake_session_by_gathering_id_detail(&self, gid: u32) -> Result<MatchmakeSession, ErrorCode>;
async fn find_matchmake_session_by_gathering_id_detail(
&self,
gid: u32,
) -> Result<MatchmakeSession, ErrorCode>;
}

View file

@ -5,8 +5,12 @@ use rnex_core::PID;
pub mod notification_types {
pub const OWNERSHIP_CHANGED: u32 = 4000;
pub const HOST_CHANGED: u32 = 110000;
pub const REQUEST_JOIN_GATHERING: u32 = 101;
pub const END_GATHERING: u32 = 102;
}
cfg_if::cfg_if! {
if #[cfg(feature = "v3-5-0")]{
#[derive(RmcSerialize, Debug, Default, Clone)]
#[rmc_struct(0)]
pub struct NotificationEvent {
@ -17,6 +21,18 @@ pub struct NotificationEvent {
pub str_param: String,
pub param_3: PID,
}
} else {
#[derive(RmcSerialize, Debug, Default, Clone)]
#[rmc_struct(0)]
pub struct NotificationEvent {
pub pid_source: PID,
pub notif_type: u32,
pub param_1: PID,
pub param_2: PID,
pub str_param: String,
}
}
}
#[rmc_proto(14, NoReturn)]
pub trait Notification {

View file

@ -1,3 +1,4 @@
use cfg_if::cfg_if;
use macros::RmcSerialize;
use rnex_core::kerberos::KerberosDateTime;
use rnex_core::rmc::structures::variant::Variant;
@ -27,29 +28,49 @@ pub struct MatchmakeParam {
pub params: Vec<(String, Variant)>,
}
// rmc structure
#[derive(RmcSerialize, Debug, Clone, Default)]
#[rmc_struct(3)]
pub struct MatchmakeSession {
//inherits from
#[extends]
pub gathering: Gathering,
cfg_if! {
if #[cfg(feature = "v3-5-0")]{
#[derive(RmcSerialize, Debug, Clone, Default)]
#[rmc_struct(3)]
pub struct MatchmakeSession {
//inherits from
#[extends]
pub gathering: Gathering,
pub gamemode: u32,
pub attributes: Vec<u32>,
pub open_participation: bool,
pub matchmake_system_type: u32,
pub application_buffer: Vec<u8>,
pub participation_count: u32,
pub progress_score: u8,
pub session_key: Vec<u8>,
pub option0: u32,
pub matchmake_param: MatchmakeParam,
pub datetime: KerberosDateTime,
pub user_password: String,
pub refer_gid: u32,
pub user_password_enabled: bool,
pub system_password_enabled: bool,
pub gamemode: u32,
pub attributes: Vec<u32>,
pub open_participation: bool,
pub matchmake_system_type: u32,
pub application_buffer: Vec<u8>,
pub participation_count: u32,
pub progress_score: u8,
pub session_key: Vec<u8>,
pub option0: u32,
pub matchmake_param: MatchmakeParam,
pub datetime: KerberosDateTime,
pub user_password: String,
pub refer_gid: u32,
pub user_password_enabled: bool,
pub system_password_enabled: bool,
}
} else {
#[derive(RmcSerialize, Debug, Clone, Default)]
#[rmc_struct(0)]
pub struct MatchmakeSession {
//inherits from
#[extends]
pub gathering: Gathering,
pub gamemode: u32,
pub attributes: Vec<u32>,
pub open_participation: bool,
pub matchmake_system_type: u32,
pub application_buffer: Vec<u8>,
pub participation_count: u32,
pub progress_score: u8,
pub session_key: Vec<u8>,
}
}
}
#[derive(RmcSerialize, Debug, Clone)]