This commit is contained in:
Maple 2025-09-21 15:59:27 +02:00
commit aab4414904
71 changed files with 293 additions and 4316 deletions

View file

@ -0,0 +1,39 @@
use macros::RmcSerialize;
#[derive(RmcSerialize)]
#[derive(Clone)]
pub struct Account{
pub pid: u32,
pub username: String,
pub kerbros_password: [u8; 16],
}
impl Account{
pub fn new(pid: u32, username: &str, passwd: &str) -> Self{
let passwd_data = passwd.as_bytes();
let mut passwd = [0u8; 16];
for (idx, byte) in passwd_data.iter().enumerate(){
passwd[idx] = *byte;
}
Self{
kerbros_password: passwd,
username: username.into(),
pid
}
}
pub fn new_raw_password(pid: u32, username: &str, passwd: [u8; 16]) -> Self{
Self{
kerbros_password: passwd,
username: username.into(),
pid
}
}
pub fn get_login_data(&self) -> (u32, [u8; 16]){
(self.pid, self.kerbros_password)
}
}

View file

@ -0,0 +1,187 @@
use std::hash::{DefaultHasher, Hasher};
use std::net::SocketAddrV4;
use std::sync::Arc;
use crate::grpc::account;
use rnex_core::kerberos::{derive_key, KerberosDateTime, Ticket};
use rnex_core::nex::account::Account;
use rnex_core::rmc::protocols::auth::{Auth, RawAuth, RawAuthInfo, RemoteAuth};
use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::response::ErrorCode::Core_Unknown;
use rnex_core::rmc::structures::any::Any;
use rnex_core::rmc::structures::connection_data::ConnectionData;
use rnex_core::rmc::structures::qresult::QResult;
use crate::{define_rmc_proto, kerberos};
use macros::rmc_struct;
use crate::reggie::{RemoteEdgeNodeHolder, RemoteEdgeNodeManagement};
use rnex_core::rmc::protocols::OnlyRemote;
define_rmc_proto!(
proto AuthClientProtocol{
Auth
}
);
#[rmc_struct(AuthClientProtocol)]
pub struct AuthHandler {
pub destination_server_acct: &'static Account,
pub build_name: &'static str,
//pub station_url: &'static str,
pub control_server: Arc<OnlyRemote<RemoteEdgeNodeHolder>>,
}
pub fn generate_ticket(
source_act_login_data: (u32, [u8; 16]),
dest_act_login_data: (u32, [u8; 16]),
) -> Box<[u8]> {
let source_key = derive_key(source_act_login_data.0, source_act_login_data.1);
let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1);
let internal_data = kerberos::TicketInternalData::new(source_act_login_data.0);
let encrypted_inner = internal_data.encrypt(dest_key);
let encrypted_session_ticket = Ticket {
pid: dest_act_login_data.0,
session_key: internal_data.session_key,
}
.encrypt(source_key, &encrypted_inner);
encrypted_session_ticket
}
async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> {
let Ok(mut client) = account::Client::new().await else {
return None;
};
let Ok(passwd) = client.get_nex_password(pid).await else {
return None;
};
Some((pid, passwd))
}
fn station_url_from_sock_addr(sock_addr: SocketAddrV4) -> String{
format!(
"prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1",
sock_addr.ip(), sock_addr.port()
)
}
impl Auth for AuthHandler {
async fn login(&self, _name: String) -> Result<(), ErrorCode> {
todo!()
}
async fn login_ex(
&self,
name: String,
_extra_data: Any,
) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode> {
let Ok(pid) = name.parse() else {
return Err(ErrorCode::Core_InvalidArgument);
};
let Ok(mut client) = account::Client::new().await else {
return Err(ErrorCode::Core_Exception);
};
let Ok(passwd) = client.get_nex_password(pid).await else {
return Err(ErrorCode::Core_Exception);
};
let source_login_data = (pid, passwd);
let destination_login_data = self.destination_server_acct.get_login_data();
let ticket = generate_ticket(source_login_data, destination_login_data);
let result = QResult::success(Core_Unknown);
let mut hasher = DefaultHasher::new();
hasher.write(name.as_bytes());
let Ok(addr) = self.control_server.get_url(hasher.finish()).await else {
return Err(ErrorCode::Core_Exception);
};
let connection_data = ConnectionData {
station_url: station_url_from_sock_addr(addr),
special_station_url: "".to_string(),
//date_time: KerberosDateTime::new(1,1,1,1,1,1),
date_time: KerberosDateTime::now(),
special_protocols: Vec::new(),
};
Ok((
result,
source_login_data.0,
ticket.into(),
connection_data,
self.build_name.to_string() //format!("{}; Rust NEX Version {} by DJMrTV", self.build_name, env!("CARGO_PKG_VERSION")),
))
}
async fn request_ticket(
&self,
source_pid: u32,
destination_pid: u32,
) -> Result<(QResult, Vec<u8>), ErrorCode> {
let Some(source_login_data) = get_login_data_by_pid(source_pid).await else {
return Err(ErrorCode::Core_Exception);
};
let desgination_login_data = if destination_pid == self.destination_server_acct.pid {
self.destination_server_acct.get_login_data()
} else {
let Some(login) = get_login_data_by_pid(destination_pid).await else {
return Err(ErrorCode::Core_Exception);
};
login
};
let result = QResult::success(Core_Unknown);
let ticket = generate_ticket(source_login_data, desgination_login_data);
Ok((result, ticket.into()))
}
async fn get_pid(&self, _username: String) -> Result<u32, ErrorCode> {
Err(ErrorCode::Core_Exception)
}
async fn get_name(&self, _pid: u32) -> Result<String, ErrorCode> {
Err(ErrorCode::Core_Exception)
}
}
#[cfg(test)]
mod test {
use rnex_core::rmc::structures::connection_data::ConnectionData;
use rnex_core::rmc::structures::qresult::QResult;
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::rmc::response::RMCResponse;
use std::io::Cursor;
#[test]
fn test() {
let stuff = hex::decode("200100000a0106000000028000000100010051b3995774000000a6321c7f78847c1c5e9fb825eb26bd91841f1a40d92fc694159666119cb13527f1463ac48ad42a63e6613ede67041554b1770978112e6f1f3e177a2bfc75933216dbe38f70133a1eb28e2ae32a4b5c4b0c3e3efd4c02907992e259b257270b57a9dbe7792f4721b07f8fafb9e32d50f2555c616a015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d3100000000000100002c153ba51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap();
let stuff = RMCResponse::new(&mut Cursor::new(stuff)).unwrap();
let rnex_core::rmc::response::RMCResponseResult::Success { call_id, method_id, data: stuff} = stuff.response_result else {
panic!()
};
// let stuff = hex::decode("0100010051B399577400000085F1736FCFBE93660275A3FE36FED6C2EFC57222AC99A9219CF54170A415B02DF1463AC48AD42A6307813FDE67041554B177097832ED000F892D9551A09F88E9CB0388DC1BC9527CC7384556A3287B2A349ABBF7E34A5A3EC14C2287CC7F78DA616BC3B03A035347FBD2E9A505C8EF42447CD809015F0000004E007072756470733A2F73747265616D3D31303B747970653D323B616464726573733D3139322E3136382E3137382E3132303B706F72743D31303030313B4349443D313B5049443D323B7369643D310000000000010000CDF53AA51F00000033006272616E63683A6F726967696E2F70726F6A6563742F7775702D61676D6A206275696C643A335F385F31355F323030345F3000").unwrap();
// let stuff = hex::decode("0100010051b399577400000037d3d4814d2b16dd546c94a75d32637b45f856b5abe73cf26cfaa235c5f2c1cef1463ac48ad42a637d873fde67041554b177097880cfa7e10bb810eaf686bfb0a0cf3d65b1f476ebc046d0855327986f557dca14fbb8594883c186b863f2206f22baa0309dbcc81da2f883cb2cdc12628ec7fced015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d310000000000010000b7f33aa51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap();
let data = <(QResult, u32, Vec<u8>, ConnectionData, String) as RmcSerialize>::deserialize(
&mut Cursor::new(stuff),
).unwrap();
println!("data: {:?}", data);
}
}

View file

@ -0,0 +1,388 @@
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::{Arc, Weak};
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering::Relaxed;
use std::time::Duration;
use log::info;
use rand::random;
use tokio::sync::{Mutex, RwLock};
use tokio::time::sleep;
use rnex_core::kerberos::KerberosDateTime;
use crate::nex::user::User;
use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification};
use rnex_core::rmc::protocols::notifications::notification_types::{HOST_CHANGED, OWNERSHIP_CHANGED};
use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::response::ErrorCode::{Core_InvalidArgument, RendezVous_SessionVoid};
use rnex_core::rmc::structures::matchmake::{Gathering, MatchmakeParam, MatchmakeSession, MatchmakeSessionSearchCriteria};
use rnex_core::rmc::structures::matchmake::gathering_flags::PERSISTENT_GATHERING;
use rnex_core::rmc::structures::variant::Variant;
pub struct MatchmakeManager{
pub gid_counter: AtomicU32,
pub sessions: RwLock<HashMap<u32, Arc<Mutex<ExtendedMatchmakeSession>>>>,
pub rv_cid_counter: AtomicU32,
pub users: RwLock<HashMap<u32, Weak<User>>>
}
impl MatchmakeManager{
pub fn next_gid(&self) -> u32{
self.gid_counter.fetch_add(1, Relaxed)
}
pub fn next_cid(&self) -> u32{
self.rv_cid_counter.fetch_add(1, Relaxed)
}
pub async fn get_session(&self, gid: u32) -> Result<Arc<Mutex<ExtendedMatchmakeSession>>, ErrorCode>{
let sessions = self.sessions.read().await;
let Some(session) = sessions.get(&gid) else {
return Err(RendezVous_SessionVoid);
};
let session = session.clone();
drop(sessions);
Ok(session)
}
async fn garbage_collect(&self){
info!("running rnex garbage collector over all sessions and users");
let mut idx = 0;
let mut to_be_deleted_gids = Vec::new();
// i am very well aware of how inefficient doing it like this is but this is the only
// way which i could think of to do this without potentially causing a deadlock of
// the entire server
while let Some((gid, session)) = {
let sessions = self.sessions.read().await;
let session_pair = sessions.iter().nth(idx).map(|s| (*s.0, s.1.clone()));
drop(sessions);
session_pair
}{
let session = session.lock().await;
if !session.is_reachable(){
to_be_deleted_gids.push(gid);
}
idx += 1;
}
let mut sessions = self.sessions.write().await;
for gid in to_be_deleted_gids{
sessions.remove(&gid);
}
}
pub async fn initialize_garbage_collect_thread(this: Weak<Self>){
tokio::spawn(async move {
while let Some(this) = this.upgrade(){
this.garbage_collect().await;
// every 30 minutes
sleep(Duration::from_secs(60 * 30)).await;
}
});
}
}
#[derive(Default, Debug)]
pub struct ExtendedMatchmakeSession{
pub session: MatchmakeSession,
pub connected_players: Vec<Weak<User>>,
}
fn read_bounds_string<T: FromStr>(str: &str) -> Option<(T,T)>{
let bounds = str.split_once(",")?;
Some((T::from_str(bounds.0).ok()?, T::from_str(bounds.1).ok()?))
}
fn check_bounds_str<T: FromStr + PartialOrd>(compare: T, str: &str) -> Option<bool>{
let bounds: (T, T) = read_bounds_string(str)?;
Some(bounds.0 <= compare && compare <= bounds.1)
}
pub async fn broadcast_notification<T: AsRef<User>>(players: &[T], notification_event: &NotificationEvent){
for player in players{
let player = player.as_ref();
player.remote.process_notification_event(notification_event.clone()).await;
}
}
impl ExtendedMatchmakeSession{
#[inline(always)]
pub fn get_active_players(&self) -> Vec<Arc<User>>{
self.connected_players.iter().filter_map(|u| u.upgrade()).collect()
}
#[inline(always)]
pub async fn broadcast_notification(&self, notification_event: &NotificationEvent){
broadcast_notification(&self.get_active_players(), notification_event).await;
}
pub async fn from_matchmake_session(gid: u32, session: MatchmakeSession, host: &Weak<User>) -> Self{
let Some(host) = host.upgrade() else{
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
};
Self{
session: mm_session,
connected_players: Default::default()
}
}
pub async fn add_players(&mut self, conns: &[Weak<User>], join_msg: String) {
let Some(initiating_user) = conns[0].upgrade() else {
return
};
let initiating_pid = initiating_user.pid;
let old_particip = self.connected_players.clone();
for conn in conns {
self.connected_players.push(conn.clone());
}
self.session.participation_count = self.connected_players.len() as u32;
for other_connection in &conns[1..]{
let Some(other_conn) = other_connection.upgrade() else {
continue;
};
let other_pid = other_conn.pid;
/*if other_pid == self.session.gathering.owner_pid &&
joining_pid == self.session.gathering.owner_pid{
continue;
}*/
other_conn.remote.process_notification_event(NotificationEvent{
pid_source: initiating_pid,
notif_type: 122000,
param_1: self.session.gathering.self_gid,
param_2: other_pid,
str_param: "".into(),
param_3: 0
}).await;
}
let list_of_connected_pids: Vec<_> = self.connected_players.iter().filter_map(|p| p.upgrade()).map(|p| p.pid).collect();
for other_connection in conns{
let Some(other_conn) = other_connection.upgrade() else {
continue;
};
let other_pid = other_conn.pid;
/*if other_pid == self.session.gathering.owner_pid &&
joining_pid == self.session.gathering.owner_pid{
continue;
}*/
for pid in &list_of_connected_pids {
other_conn.remote.process_notification_event(NotificationEvent {
pid_source: initiating_pid,
notif_type: 3001,
param_1: self.session.gathering.self_gid,
param_2: *pid,
str_param: join_msg.clone(),
param_3: self.connected_players.len() as _
}).await;
}
}
for old_conns in &old_particip{
let Some(old_conns) = old_conns.upgrade() else {
continue;
};
let older_pid = old_conns.pid;
initiating_user.remote.process_notification_event(NotificationEvent{
pid_source: initiating_pid,
notif_type: 3001,
param_1: self.session.gathering.self_gid,
param_2: older_pid,
str_param: join_msg.clone(),
param_3: self.connected_players.len() as _
}).await;
}
}
#[inline]
pub fn is_reachable(&self) -> bool{
(if self.session.gathering.flags & PERSISTENT_GATHERING != 0{
if !self.connected_players.is_empty(){
true
} else {
self.session.open_participation
}
} else {
!self.connected_players.is_empty()
}) & !self.connected_players.is_empty()
}
#[inline]
pub fn is_joinable(&self) -> bool{
self.is_reachable() && self.session.open_participation
}
pub fn matches_criteria(&self, search_criteria: &MatchmakeSessionSearchCriteria) -> Result<bool, ErrorCode>{
// todo: implement the rest of the search criteria
if search_criteria.vacant_only {
if (self.connected_players.len() as u16 + search_criteria.vacant_participants) > self.session.gathering.maximum_participants{
return Ok(false);
}
}
if search_criteria.exclude_locked{
if !self.session.open_participation{
return Ok(false);
}
}
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 !check_bounds_str(self.session.gathering.minimum_participants, &search_criteria.minimum_participants).ok_or(Core_InvalidArgument)? {
return Ok(false);
}
if !check_bounds_str(self.session.gathering.maximum_participants, &search_criteria.maximum_participants).ok_or(Core_InvalidArgument)? {
return Ok(false);
}
let game_mode: u32 = search_criteria.game_mode.parse().map_err(|_| Core_InvalidArgument)?;
if self.session.gamemode != game_mode{
return Ok(false);
}
let mm_sys_type: u32 = search_criteria.matchmake_system_type.parse().map_err(|_| Core_InvalidArgument)?;
if self.session.matchmake_system_type != mm_sys_type{
return Ok(false);
}
if search_criteria.attribs.get(0).map(|str| str.parse().ok()).flatten() != self.session.attributes.get(0).map(|v| *v){
return Ok(false);
}
if search_criteria.attribs.get(2).map(|str| str.parse().ok()).flatten() != self.session.attributes.get(2).map(|v| *v){
return Ok(false);
}
if search_criteria.attribs.get(3).map(|str| str.parse().ok()).flatten() != self.session.attributes.get(3).map(|v| *v){
return Ok(false);
}
Ok(true)
}
pub async fn migrate_ownership(&mut self, initiator_pid: u32) -> Result<(), ErrorCode>{
let players: Vec<_> = self.connected_players.iter().filter_map(|p| p.upgrade()).collect();
let Some(new_owner) = players.iter().find(|p| p.pid != self.session.gathering.owner_pid) else {
self.session.gathering.owner_pid = 0;
return Ok(());
};
self.session.gathering.owner_pid = new_owner.pid;
self.broadcast_notification(&NotificationEvent{
pid_source: initiator_pid,
notif_type: OWNERSHIP_CHANGED,
param_1: self.session.gathering.self_gid,
param_2: new_owner.pid,
..Default::default()
}).await;
Ok(())
}
pub async fn migrate_host(&mut self, initiator_pid: u32) -> Result<(), ErrorCode>{
let players: Vec<_> = self.connected_players.iter().filter_map(|p| p.upgrade()).collect();
self.session.gathering.host_pid = self.session.gathering.owner_pid;
self.broadcast_notification(&NotificationEvent{
pid_source: initiator_pid,
notif_type: HOST_CHANGED,
param_1: self.session.gathering.self_gid,
..Default::default()
}).await;
Ok(())
}
pub async fn remove_player_from_session(&mut self, pid: u32, message: &str) -> Result<(), ErrorCode>{
self.connected_players.retain(|u| u.upgrade().is_some_and(|u| u.pid != pid));
self.session.participation_count = (self.connected_players.len() & u32::MAX as usize) as u32;
if pid == self.session.gathering.owner_pid {
self.migrate_ownership(pid).await?;
}
if pid == self.session.gathering.host_pid {
self.migrate_host(pid).await?;
}
// todo: support DisconnectChangeOwner
// todo: finish the rest of this
for player in self.connected_players.iter().filter_map(|p| p.upgrade()){
player.remote.process_notification_event(NotificationEvent{
notif_type: 3008,
pid_source: pid,
param_1: self.session.gathering.self_gid,
param_2: pid,
str_param: message.to_owned(),
.. Default::default()
}).await;
}
Ok(())
}
}

5
rnex-core/src/nex/mod.rs Normal file
View file

@ -0,0 +1,5 @@
pub mod account;
pub mod auth_handler;
pub mod user;
pub mod remote_console;
pub mod matchmake;

View file

@ -0,0 +1,21 @@
use crate::rmc::protocols::notifications::{Notification, RawNotification, RawNotificationInfo, RemoteNotification};
use crate::rmc::protocols::nat_traversal::{NatTraversalConsole, RemoteNatTraversalConsole, RawNatTraversalConsoleInfo, RawNatTraversalConsole};
use crate::define_rmc_proto;
define_rmc_proto!(
proto Console{
Notification,
NatTraversalConsole
}
);
/*
#[rmc_struct(Console)]
pub struct TestRemoteConsole{
pub remote: RemoteUserProtocol,
}
impl Notification for TestRemoteConsole{
async fn process_notification_event(&self, event: NotificationEvent) {
println!("NOTIF RECIEVED: {:?}", event);
}
}*/

568
rnex-core/src/nex/user.rs Normal file
View file

@ -0,0 +1,568 @@
use crate::define_rmc_proto;
use crate::nex::matchmake::{ExtendedMatchmakeSession, MatchmakeManager};
use crate::nex::remote_console::RemoteConsole;
use rnex_core::prudp::station_url::StationUrl;
use rnex_core::prudp::station_url::UrlOptions::{
Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID,
};
use crate::rmc::protocols::matchmake::{
Matchmake, RawMatchmake, RawMatchmakeInfo, RemoteMatchmake,
};
use rnex_core::rmc::protocols::ranking::{Ranking, RawRanking, RawRankingInfo, RemoteRanking};
use rnex_core::rmc::protocols::matchmake_extension::{
MatchmakeExtension, RawMatchmakeExtension, RawMatchmakeExtensionInfo, RemoteMatchmakeExtension,
};
use crate::rmc::protocols::nat_traversal::{NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal, RemoteNatTraversalConsole};
use rnex_core::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure};
use rnex_core::rmc::protocols::matchmake_ext::{MatchmakeExt, RawMatchmakeExt, RawMatchmakeExtInfo, RemoteMatchmakeExt};
use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession};
use rnex_core::rmc::structures::qresult::QResult;
use macros::rmc_struct;
use std::sync::{Arc, Weak};
use log::info;
use tokio::sync::{Mutex, RwLock};
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
use rnex_core::prudp::station_url::nat_types::PUBLIC;
use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification};
use rnex_core::rmc::response::ErrorCode::{Core_Exception, Core_InvalidArgument, RendezVous_AccountExpired};
define_rmc_proto!(
proto UserProtocol{
Secure,
MatchmakeExtension,
MatchmakeExt,
Matchmake,
NatTraversal,
Ranking
}
);
#[rmc_struct(UserProtocol)]
pub struct User {
pub pid: u32,
pub ip: PRUDPSockAddr,
pub this: Weak<User>,
pub remote: RemoteConsole,
pub station_url: RwLock<Vec<StationUrl>>,
pub matchmake_manager: Arc<MatchmakeManager>,
}
impl Secure for User {
async fn register(
&self,
station_urls: Vec<StationUrl>,
) -> Result<(QResult, u32, StationUrl), ErrorCode> {
let cid = self.matchmake_manager.next_cid();
println!("{:?}", station_urls);
let mut users = self.matchmake_manager.users.write().await;
users.insert(cid, self.this.clone());
drop(users);
let mut public_station: Option<StationUrl> = None;
let mut private_station: Option<StationUrl> = None;
for station in station_urls {
let is_public = station.options.iter().any(|v| {
if let NatType(v) = v {
if *v & PUBLIC != 0 {
return true;
}
}
false
});
let Some(nat_filtering) = station.options.iter().find_map(|v| match v {
NatFiltering(v) => Some(v),
_ => None
}) else {
return Err(Core_Exception);
};
let Some(nat_mapping) = station.options.iter().find_map(|v| match v {
NatMapping(v) => Some(v),
_ => None
}) else {
return Err(Core_Exception);
};
if !is_public || (*nat_filtering == 0 && *nat_mapping == 0) {
private_station = Some(station.clone());
}
if is_public {
public_station = Some(station);
}
}
let Some(mut private_station) = private_station else {
return Err(Core_Exception);
};
let mut public_station = if let Some(public_station) = public_station {
public_station
} else {
let mut public_station = private_station.clone();
public_station.options.retain(|v| {
match v {
Address(_) | Port(_) | NatFiltering(_) | NatMapping(_) | NatType(_) => false,
_ => true
}
});
public_station.options.push(Address(*self.ip.regular_socket_addr.ip()));
public_station.options.push(Port(self.ip.regular_socket_addr.port()));
public_station.options.push(NatFiltering(0));
public_station.options.push(NatMapping(0));
public_station.options.push(NatType(3));
public_station
};
let both = [&mut public_station, &mut private_station];
for station in both {
station.options.retain(|v| {
match v {
PrincipalID(_) | RVConnectionID(_) => false,
_ => true
}
});
station.options.push(PrincipalID(self.pid));
station.options.push(RVConnectionID(cid));
}
let mut lock = self.station_url.write().await;
*lock = vec![
public_station.clone(),
// private_station.clone()
];
drop(lock);
let result = QResult::success(ErrorCode::Core_Unknown);
let out = public_station.to_string();
println!("out: {}", out);
Ok((result, cid, public_station))
}
async fn replace_url(&self, target_url: StationUrl, dest: StationUrl) -> Result<(), ErrorCode> {
let mut lock = self.station_url.write().await;
let Some(target_addr) = target_url.options.iter().find(|v| matches!(v, Address(_))) else {
return Err(ErrorCode::Core_InvalidArgument);
};
let Some(target_port) = target_url.options.iter().find(|v| matches!(v, Port(_))) else {
return Err(ErrorCode::Core_InvalidArgument);
};
let Some(replacement_target) = lock.iter_mut().find(|url| {
url.options.iter().any(|o| o == target_addr) &&
url.options.iter().any(|o| o == target_port)
}) else {
return Err(ErrorCode::Core_InvalidArgument);
};
*replacement_target = dest;
drop(lock);
Ok(())
}
}
impl MatchmakeExtension for User {
async fn close_participation(&self, gid: u32) -> Result<(), ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?;
let mut session = session.lock().await;
session.session.open_participation = false;
Ok(())
}
async fn open_participation(&self, gid: u32) -> Result<(), ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?;
let mut session = session.lock().await;
session.session.open_participation = true;
Ok(())
}
async fn get_playing_session(&self, _pids: Vec<u32>) -> Result<Vec<()>, ErrorCode> {
Ok(Vec::new())
}
async fn update_progress_score(&self, gid: u32, progress: u8) -> Result<(), ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?;
let mut session = session.lock().await;
session.session.progress_score = progress;
Ok(())
}
async fn create_matchmake_session_with_param(
&self,
create_session_param: CreateMatchmakeSessionParam,
) -> Result<MatchmakeSession, ErrorCode> {
println!("{:?}", create_session_param);
let gid = self.matchmake_manager.next_gid();
let mut new_session = ExtendedMatchmakeSession::from_matchmake_session(
gid,
create_session_param.matchmake_session,
&self.this.clone(),
)
.await;
let mut joining_players = vec![self.this.clone()];
let users = self.matchmake_manager.users.read().await;
if let Ok(old_gathering) = self.matchmake_manager.get_session(create_session_param.gid_for_participation_check).await {
let old_gathering = old_gathering.lock().await;
let players = old_gathering.connected_players.iter().filter_map(|v| v.upgrade()).filter(|u| create_session_param.additional_participants.iter().any(|p| *p == u.pid));
for player in players {
joining_players.push(Arc::downgrade(&player));
}
}
drop(users);
new_session.session.participation_count = create_session_param.participation_count as u32;
new_session
.add_players(&joining_players, create_session_param.join_message)
.await;
let session = new_session.session.clone();
let mut sessions = self.matchmake_manager.sessions.write().await;
sessions.insert(gid, Arc::new(Mutex::new(new_session)));
drop(sessions);
Ok(session)
}
async fn join_matchmake_session_with_param(
&self,
join_session_param: JoinMatchmakeSessionParam,
) -> Result<MatchmakeSession, ErrorCode> {
let session = self.matchmake_manager.get_session(join_session_param.gid).await?;
let mut session = session.lock().await;
if session.session.user_password_enabled{
if join_session_param.user_password != session.session.user_password{
return Err(ErrorCode::RendezVous_InvalidPassword)
}
}
session.connected_players.retain(|v| v.upgrade().is_some_and(|v| v.pid != self.pid));
let mut joining_players = vec![self.this.clone()];
let users = self.matchmake_manager.users.read().await;
if let Ok(old_gathering) = self.matchmake_manager.get_session(join_session_param.gid_for_participation_check).await {
let old_gathering = old_gathering.lock().await;
let players = old_gathering.connected_players.iter().filter_map(|v| v.upgrade()).filter(|u| join_session_param.additional_participants.iter().any(|p| *p == u.pid));
for player in players {
joining_players.push(Arc::downgrade(&player));
}
}
drop(users);
session
.add_players(&joining_players, join_session_param.join_message)
.await;
let mm_session = session.session.clone();
Ok(mm_session)
}
async fn auto_matchmake_with_param_postpone(&self, param: AutoMatchmakeParam) -> Result<MatchmakeSession, ErrorCode> {
println!("{:?}", param);
let mut joining_players = vec![self.this.clone()];
let users = self.matchmake_manager.users.read().await;
if let Ok(old_gathering) = self.matchmake_manager.get_session(param.gid_for_participation_check).await {
let old_gathering = old_gathering.lock().await;
let players = old_gathering.connected_players.iter().filter_map(|v| v.upgrade()).filter(|u| param.additional_participants.iter().any(|p| *p == u.pid));
for player in players {
joining_players.push(Arc::downgrade(&player));
}
}
drop(users);
let sessions = self.matchmake_manager.sessions.read().await;
for session in sessions.values() {
let mut session = session.lock().await;
println!("checking session!");
if !session.is_joinable() {
continue;
}
let mut bool_matched_criteria = false;
for criteria in &param.search_criteria {
if session.matches_criteria(criteria)? {
bool_matched_criteria = true;
}
}
if bool_matched_criteria {
session.add_players(&joining_players, param.join_message).await;
return Ok(session.session.clone());
}
}
drop(sessions);
println!("making new session!");
let AutoMatchmakeParam {
join_message,
participation_count,
gid_for_participation_check,
matchmake_session,
additional_participants,
..
} = param;
self.create_matchmake_session_with_param(CreateMatchmakeSessionParam {
join_message,
participation_count,
gid_for_participation_check,
create_matchmake_session_option: 0,
matchmake_session,
additional_participants,
}).await
}
async fn find_matchmake_session_by_gathering_id_detail(&self, gid: u32) -> Result<MatchmakeSession, ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?;
let session = session.lock().await;
Ok(session.session.clone())
}
async fn modify_current_game_attribute(&self, gid: u32, attrib_index: u32, attrib_val: u32) -> Result<(), ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?;
let mut session = session.lock().await;
session.session.attributes[attrib_index as usize] = attrib_val;
Ok(())
}
}
impl Matchmake for User {
async fn unregister_gathering(&self, _gid: u32) -> Result<bool, ErrorCode> {
Ok(true)
}
async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?;
let session = session.lock().await;
let urls: Vec<_> =
session
.connected_players
.iter()
.filter_map(|v| v.upgrade())
.filter(|u| u.pid == session.session.gathering.host_pid)
.map(|u| async move {
u.station_url.read().await.clone()
})
.next()
.ok_or(ErrorCode::RendezVous_SessionClosed)?
.await;
println!("{:?}", urls);
if urls.is_empty(){
return Err(ErrorCode::RendezVous_NotParticipatedGathering)
}
Ok(urls)
}
async fn update_session_host(&self, gid: u32, change_session_owner: bool) -> Result<(), ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?;
let mut session = session.lock().await;
session.session.gathering.host_pid = self.pid;
for player in &session.connected_players {
let Some(player) = player.upgrade() else {
continue;
};
player.remote.process_notification_event(NotificationEvent {
notif_type: 110000,
pid_source: self.pid,
param_1: gid,
param_2: self.pid,
param_3: 0,
str_param: "".to_string(),
}).await;
}
if change_session_owner {
session.session.gathering.owner_pid = self.pid;
for player in &session.connected_players {
let Some(player) = player.upgrade() else {
continue;
};
player.remote.process_notification_event(NotificationEvent {
notif_type: 4000,
pid_source: self.pid,
param_1: gid,
param_2: self.pid,
param_3: 0,
str_param: "".to_string(),
}).await;
}
}
Ok(())
}
async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec<u32>, _participants_only: bool) -> Result<(), ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?;
let mut session = session.lock().await;
let candidate = candidates.get(0).ok_or(Core_InvalidArgument)?;
session.session.gathering.owner_pid = *candidate;
for player in &session.connected_players {
let Some(player) = player.upgrade() else {
continue;
};
player.remote.process_notification_event(NotificationEvent {
notif_type: 4000,
pid_source: self.pid,
param_1: gid,
param_2: *candidate,
param_3: 0,
str_param: "".to_string(),
}).await;
}
Ok(())
}
}
impl MatchmakeExt for User {
async fn end_participation(&self, gid: u32, message: String) -> Result<bool, ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?;
let mut session = session.lock().await;
session.remove_player_from_session(self.pid, &message).await?;
Ok(true)
}
}
impl NatTraversal for User {
async fn report_nat_properties(
&self,
nat_mapping: u32,
nat_filtering: u32,
_rtt: u32,
) -> Result<(), ErrorCode> {
let mut urls = self.station_url.write().await;
for station_url in urls.iter_mut() {
station_url.options.retain(|o| match o {
NatMapping(_) | NatFiltering(_) => false,
_ => true
});
station_url.options.push(NatMapping(nat_mapping as u8));
station_url.options.push(NatFiltering(nat_filtering as u8));
}
Ok(())
}
async fn report_nat_traversal_result(&self, _cid: u32, _result: bool, _rtt: u32) -> Result<(), ErrorCode> {
Ok(())
}
async fn request_probe_initiation(&self, _station_to_probe: String) -> Result<(), ErrorCode> {
info!("NO!");
Err(RendezVous_AccountExpired)
}
async fn request_probe_initialization_ext(&self, target_list: Vec<String>, station_to_probe: String) -> Result<(), ErrorCode> {
let users = self.matchmake_manager.users.read().await;
println!("requesting station probe for {:?} to {:?}", target_list, station_to_probe);
for target in target_list {
let Ok(url) = StationUrl::try_from(target.as_ref()) else {
continue;
};
let Some(RVConnectionID(v)) = url.options.into_iter().find(|o| { matches!(o, &RVConnectionID(_)) }) else {
continue;
};
let Some(v) = users.get(&v) else {
continue;
};
let Some(user) = v.upgrade() else {
continue;
};
user.remote.request_probe_initiation(station_to_probe.clone()).await;
}
info!("finished probing");
Ok(())
}
}
impl Ranking for User{
}