refactor
This commit is contained in:
parent
a4ccc96ed0
commit
aab4414904
71 changed files with 293 additions and 4316 deletions
37
rnex-core/src/common.rs
Normal file
37
rnex-core/src/common.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use std::fs;
|
||||
use std::fs::File;
|
||||
use chrono::{Local, SecondsFormat};
|
||||
use log::LevelFilter;
|
||||
use simplelog::{ColorChoice, CombinedLogger, Config, TermLogger, TerminalMode, WriteLogger};
|
||||
|
||||
pub fn setup(){
|
||||
CombinedLogger::init(vec![
|
||||
TermLogger::new(
|
||||
LevelFilter::Info,
|
||||
Config::default(),
|
||||
TerminalMode::Mixed,
|
||||
ColorChoice::Auto,
|
||||
),
|
||||
WriteLogger::new(LevelFilter::max(), Config::default(), {
|
||||
fs::create_dir_all("log").unwrap();
|
||||
let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false);
|
||||
// this fixes windows being windows
|
||||
let date = date.replace(":", "-");
|
||||
let filename = format!("{}.log", date);
|
||||
if cfg!(windows) {
|
||||
File::create(format!("log\\{}", filename)).unwrap()
|
||||
} else {
|
||||
File::create(format!("log/{}", filename)).unwrap()
|
||||
}
|
||||
}),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
/*ctrlc::set_handler(||{
|
||||
FORCE_EXIT.call_once_force(|_|{
|
||||
println!("attempting exit");
|
||||
});
|
||||
}).unwrap();*/
|
||||
|
||||
dotenv::dotenv().ok();
|
||||
}
|
||||
91
rnex-core/src/executables/backend_server_insecure.rs
Normal file
91
rnex-core/src/executables/backend_server_insecure.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
use rnex_core::reggie::{RemoteEdgeNodeHolder, UnitPacketRead};
|
||||
use log::{error, info};
|
||||
use once_cell::sync::Lazy;
|
||||
use rustls::client::danger::HandshakeSignatureValid;
|
||||
use rustls::pki_types::{CertificateDer, TrustAnchor, UnixTime};
|
||||
use rustls::server::danger::{ClientCertVerified, ClientCertVerifier};
|
||||
use rustls::server::{ClientCertVerifierBuilder, WebPkiClientVerifier};
|
||||
use rustls::{
|
||||
DigitallySignedStruct, DistinguishedName, Error, RootCertStore, ServerConfig, ServerConnection,
|
||||
SignatureScheme,
|
||||
};
|
||||
use rustls_pki_types::PrivateKeyDer;
|
||||
use rnex_core::common::setup;
|
||||
use std::borrow::ToOwned;
|
||||
use std::{env, fs};
|
||||
use std::io::Cursor;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::sync::Arc;
|
||||
use macros::{method_id, rmc_proto, rmc_struct};
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::net::{TcpListener, TcpSocket, TcpStream};
|
||||
use tokio::task;
|
||||
use tokio_rustls::TlsAcceptor;
|
||||
use rnex_core::define_rmc_proto;
|
||||
use rnex_core::executables::common::{OWN_IP_PRIVATE, SECURE_SERVER_ACCOUNT, SERVER_PORT};
|
||||
use rnex_core::nex::auth_handler::AuthHandler;
|
||||
use rnex_core::reggie::EdgeNodeHolderConnectOption::DontRegister;
|
||||
use rnex_core::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote};
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
use rnex_core::rmc::structures::RmcSerialize;
|
||||
use rnex_core::rnex_proxy_common::ConnectionInitData;
|
||||
use rnex_core::util::SplittableBufferConnection;
|
||||
|
||||
|
||||
pub static FORWARD_EDGE_NODE_HOLDER: Lazy<SocketAddrV4> = Lazy::new(||{
|
||||
env::var("FORWARD_EDGE_NODE_HOLDER")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.expect("SECURE_EDGE_NODE_HOLDER not set")
|
||||
});
|
||||
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
setup();
|
||||
|
||||
let conn = TcpStream::connect(&*FORWARD_EDGE_NODE_HOLDER).await.unwrap();
|
||||
|
||||
let conn: SplittableBufferConnection = conn.into();
|
||||
|
||||
conn.send(DontRegister.to_data()).await;
|
||||
|
||||
let conn = new_rmc_gateway_connection(conn, |r| Arc::new(OnlyRemote::<RemoteEdgeNodeHolder>::new(r)));
|
||||
|
||||
let listen = TcpListener::bind(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT)).await.unwrap();
|
||||
|
||||
|
||||
|
||||
while let Ok((mut stream, addr)) = listen.accept().await {
|
||||
let buffer = match stream.read_buffer().await{
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("an error ocurred whilest reading connection data buffer: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let user_connection_data = ConnectionInitData::deserialize(&mut Cursor::new(buffer));
|
||||
|
||||
let user_connection_data = match user_connection_data{
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("an error ocurred whilest reading connection data: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let controller = conn.clone();
|
||||
task::spawn(async move {
|
||||
info!("connection to secure backend established");
|
||||
new_rmc_gateway_connection(stream.into(), |_| {
|
||||
Arc::new(AuthHandler {
|
||||
destination_server_acct: &SECURE_SERVER_ACCOUNT,
|
||||
build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0",
|
||||
control_server: controller
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
74
rnex-core/src/executables/backend_server_secure.rs
Normal file
74
rnex-core/src/executables/backend_server_secure.rs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
use std::io::Cursor;
|
||||
use rnex_core::rmc::structures::RmcSerialize;
|
||||
use rnex_core::reggie::{RemoteEdgeNodeHolder, UnitPacketRead};
|
||||
use std::net::SocketAddrV4;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use log::{error, info};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::task;
|
||||
use rnex_core::common::setup;
|
||||
use rnex_core::executables::common::{OWN_IP_PRIVATE, SERVER_PORT};
|
||||
use rnex_core::nex::matchmake::MatchmakeManager;
|
||||
use rnex_core::nex::remote_console::RemoteConsole;
|
||||
use rnex_core::nex::user::User;
|
||||
use rnex_core::reggie::EdgeNodeHolderConnectOption::DontRegister;
|
||||
use rnex_core::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote};
|
||||
use rnex_core::rnex_proxy_common::ConnectionInitData;
|
||||
use rnex_core::rmc::protocols::RemoteInstantiatable;
|
||||
use rnex_core::util::SplittableBufferConnection;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
setup();
|
||||
|
||||
let listen = TcpListener::bind(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT)).await.unwrap();
|
||||
|
||||
let mmm = Arc::new(MatchmakeManager{
|
||||
gid_counter: AtomicU32::new(1),
|
||||
sessions: Default::default(),
|
||||
users: Default::default(),
|
||||
rv_cid_counter: AtomicU32::new(1),
|
||||
});
|
||||
|
||||
let weak_mmm = Arc::downgrade(&mmm);
|
||||
|
||||
MatchmakeManager::initialize_garbage_collect_thread(weak_mmm).await;
|
||||
|
||||
while let Ok((mut stream, addr)) = listen.accept().await {
|
||||
let buffer = match stream.read_buffer().await{
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("an error ocurred whilest reading connection data buffer: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let user_connection_data = ConnectionInitData::deserialize(&mut Cursor::new(buffer));
|
||||
|
||||
let user_connection_data = match user_connection_data{
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("an error ocurred whilest reading connection data: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let mmm = mmm.clone();
|
||||
task::spawn(async move {
|
||||
info!("connection to secure backend established");
|
||||
new_rmc_gateway_connection(stream.into(), |r| {
|
||||
Arc::new_cyclic(|this| User{
|
||||
this: this.clone(),
|
||||
ip: user_connection_data.prudpsock_addr,
|
||||
pid: user_connection_data.pid,
|
||||
remote: RemoteConsole::new(r),
|
||||
matchmake_manager: mmm,
|
||||
station_url: Default::default()
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
44
rnex-core/src/executables/common.rs
Normal file
44
rnex-core/src/executables/common.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use std::env;
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use macros::{method_id, rmc_proto, RmcSerialize};
|
||||
use once_cell::sync::Lazy;
|
||||
use tonic::transport::Server;
|
||||
use crate::define_rmc_proto;
|
||||
use crate::prudp::station_url::StationUrl;
|
||||
use crate::nex::account::Account;
|
||||
use crate::rmc::response::ErrorCode;
|
||||
|
||||
pub static OWN_IP_PRIVATE: Lazy<Ipv4Addr> = Lazy::new(|| {
|
||||
env::var("SERVER_IP")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.expect("SERVER_IP not specified")
|
||||
});
|
||||
|
||||
pub static OWN_IP_PUBLIC: Lazy<Ipv4Addr> = Lazy::new(|| {
|
||||
env::var("SERVER_IP_PUBLIC")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.expect("SERVER_IP_PUBLIC not specified")
|
||||
});
|
||||
|
||||
pub static SERVER_PORT: Lazy<u16> = Lazy::new(|| {
|
||||
env::var("SERVER_PORT")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(10000)
|
||||
});
|
||||
|
||||
pub static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(|| {
|
||||
env::var("AUTH_SERVER_PASSWORD")
|
||||
.ok()
|
||||
.unwrap_or("password".to_owned())
|
||||
});
|
||||
|
||||
pub static AUTH_SERVER_ACCOUNT: Lazy<Account> =
|
||||
Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD));
|
||||
pub static SECURE_SERVER_ACCOUNT: Lazy<Account> =
|
||||
Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD));
|
||||
|
||||
|
||||
|
||||
92
rnex-core/src/executables/edge_node_holder_server.rs
Normal file
92
rnex-core/src/executables/edge_node_holder_server.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
use std::io::Cursor;
|
||||
use std::net::SocketAddrV4;
|
||||
use std::sync::{Arc, Weak};
|
||||
use log::error;
|
||||
use macros::rmc_struct;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::sync::RwLock;
|
||||
use rnex_core::common::setup;
|
||||
use rnex_core::executables::common::{OWN_IP_PRIVATE, SERVER_PORT};
|
||||
use rnex_core::reggie::{EdgeNodeHolderConnectOption, EdgeNodeManagement, LocalEdgeNodeHolder};
|
||||
use rnex_core::rmc::protocols::new_rmc_gateway_connection;
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
use rnex_core::util::SplittableBufferConnection;
|
||||
use rnex_core::rmc::structures::RmcSerialize;
|
||||
|
||||
#[rmc_struct(EdgeNodeHolder)]
|
||||
struct EdgeNode{
|
||||
data_holder: Arc<DataHolder>,
|
||||
address: SocketAddrV4
|
||||
}
|
||||
|
||||
impl EdgeNodeManagement for EdgeNode{
|
||||
async fn get_url(&self, seed: u64) -> Result<SocketAddrV4, ErrorCode> {
|
||||
self.data_holder.get_url(seed).await
|
||||
}
|
||||
}
|
||||
|
||||
#[rmc_struct(EdgeNodeHolder)]
|
||||
#[derive(Default)]
|
||||
struct DataHolder{
|
||||
edge_nodes: RwLock<Vec<Weak<EdgeNode>>>
|
||||
}
|
||||
|
||||
impl EdgeNodeManagement for DataHolder{
|
||||
async fn get_url(&self, seed: u64) -> Result<SocketAddrV4, ErrorCode> {
|
||||
let nodes = self.edge_nodes.read().await;
|
||||
|
||||
let nodes: Vec<_> = nodes.iter().filter_map(|n| n.upgrade()).collect();
|
||||
|
||||
// avoid a devide by zero
|
||||
if nodes.len() == 0{
|
||||
return Err(ErrorCode::Core_InvalidIndex);
|
||||
};
|
||||
|
||||
let node = &nodes[seed as usize % nodes.len()];
|
||||
|
||||
Ok(node.address)
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
setup();
|
||||
|
||||
let listen = TcpListener::bind(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT)).await.unwrap();
|
||||
|
||||
let holder: Arc<DataHolder> = Default::default();
|
||||
|
||||
while let Ok((mut stream, addr)) = listen.accept().await {
|
||||
let mut conn: SplittableBufferConnection = stream.into();
|
||||
|
||||
let Some(data) = conn.recv().await else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(data) = EdgeNodeHolderConnectOption::deserialize(&mut Cursor::new(data)) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let holder = holder.clone();
|
||||
|
||||
match data{
|
||||
EdgeNodeHolderConnectOption::DontRegister => {
|
||||
|
||||
new_rmc_gateway_connection(conn, |_| holder);
|
||||
},
|
||||
EdgeNodeHolderConnectOption::Register(address) => {
|
||||
let edge_node = EdgeNode{
|
||||
address,
|
||||
data_holder: holder.clone()
|
||||
};
|
||||
|
||||
let node = new_rmc_gateway_connection(conn, move |_| Arc::new(edge_node));
|
||||
|
||||
let mut nodes = holder.edge_nodes.write().await;
|
||||
nodes.push(Arc::downgrade(&node));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
1
rnex-core/src/executables/mod.rs
Normal file
1
rnex-core/src/executables/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod common;
|
||||
156
rnex-core/src/grpc/account.rs
Normal file
156
rnex-core/src/grpc/account.rs
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
use std::{env, result};
|
||||
use std::array::TryFromSliceError;
|
||||
use std::str::FromStr;
|
||||
use json::{object, JsonValue};
|
||||
use once_cell::sync::Lazy;
|
||||
use reqwest::{Body, Method, Url};
|
||||
use reqwest::header::HeaderValue;
|
||||
use thiserror::Error;
|
||||
use crate::grpc::account::Error::SomethingHappened;
|
||||
static API_KEY: Lazy<String> = Lazy::new(||{
|
||||
let key = env::var("ACCOUNT_GQL_API_KEY")
|
||||
.expect("no graphql ip specified");
|
||||
|
||||
key
|
||||
});
|
||||
|
||||
static CLIENT_URI: Lazy<String> = Lazy::new(||{
|
||||
env::var("ACCOUNT_GQL_URL")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.expect("no graphql ip specified")
|
||||
});
|
||||
|
||||
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error{
|
||||
#[error(transparent)]
|
||||
Creation(#[from] reqwest::Error),
|
||||
#[error(transparent)]
|
||||
Json(#[from] json::Error),
|
||||
#[error(transparent)]
|
||||
Status(#[from] tonic::Status),
|
||||
#[error("invalid password size: {0}")]
|
||||
PasswordConversion(#[from] TryFromSliceError),
|
||||
#[error("something happened")]
|
||||
SomethingHappened
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
pub struct Client(reqwest::Client);
|
||||
|
||||
impl Client{
|
||||
pub async fn new() -> Result<Self> {
|
||||
Ok(Self(reqwest::ClientBuilder::new().build()?))
|
||||
}
|
||||
|
||||
async fn do_request(&self, request_data: JsonValue) -> Result<JsonValue>{
|
||||
let mut request = reqwest::Request::new(Method::POST, Url::from_str(CLIENT_URI.as_str()).unwrap());
|
||||
|
||||
*(request.body_mut()) = Some(Body::from(request_data.to_string()));
|
||||
request.headers_mut().insert("X-API-Key", HeaderValue::from_str(&API_KEY).unwrap());
|
||||
request.headers_mut().insert("Content-Type", HeaderValue::from_str("application/json").unwrap());
|
||||
|
||||
let response = self.0.execute(request).await?;
|
||||
|
||||
Ok(json::parse(&response.text().await?)?)
|
||||
}
|
||||
|
||||
pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{
|
||||
let req = self.do_request(object!{
|
||||
"query": r"query($pid: Int!){
|
||||
userByPid(pid: $pid){
|
||||
nexPassword
|
||||
}
|
||||
}",
|
||||
"variables": {
|
||||
"pid": pid
|
||||
}
|
||||
}).await?;
|
||||
|
||||
let Some(val) = req.entries()
|
||||
.find(|v| v.0 == "data")
|
||||
.ok_or(SomethingHappened)?.1
|
||||
.entries()
|
||||
.find(|v| v.0 == "userByPid")
|
||||
.ok_or(SomethingHappened)?.1
|
||||
.entries()
|
||||
.find(|v| v.0 == "nexPassword")
|
||||
.ok_or(SomethingHappened)?.1
|
||||
.as_str() else {
|
||||
return Err(SomethingHappened);
|
||||
};
|
||||
|
||||
Ok(val.as_bytes().try_into().map_err(|_| SomethingHappened)?)
|
||||
}
|
||||
|
||||
/*pub async fn get_user_data(&mut self , pid: u32) -> Result<GetUserDataResponse>{
|
||||
let req = Request::new(GetUserDataRequest{
|
||||
pid
|
||||
});
|
||||
|
||||
let response = self.0.get_user_data(req).await?.into_inner();
|
||||
|
||||
Ok(response)
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
pub struct Client(AccountClient<InterceptedService<Channel, InterceptorFunc>>);
|
||||
|
||||
impl Client{
|
||||
pub async fn new() -> Result<Self>{
|
||||
let channel = Channel::from_static(&*CLIENT_URI).connect().await?;
|
||||
|
||||
let func = Box::new(&|mut req: Request<()>|{
|
||||
req.metadata_mut().insert("x-api-key", API_KEY.clone());
|
||||
Ok(req)
|
||||
}) as InterceptorFunc;
|
||||
|
||||
let client = AccountClient::with_interceptor(channel, func);
|
||||
Ok(Self(client))
|
||||
}
|
||||
|
||||
pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{
|
||||
let req = Request::new(GetNexPasswordRequest{
|
||||
pid
|
||||
});
|
||||
|
||||
let response = self.0.get_nex_password(req).await?.into_inner();
|
||||
|
||||
Ok(response.password.as_bytes().try_into()?)
|
||||
}
|
||||
|
||||
pub async fn get_user_data(&mut self , pid: u32) -> Result<GetUserDataResponse>{
|
||||
let req = Request::new(GetUserDataRequest{
|
||||
pid
|
||||
});
|
||||
|
||||
let response = self.0.get_user_data(req).await?.into_inner();
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
*/
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use crate::grpc::account::Client;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test(){
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
let mut client = Client::new().await.unwrap();
|
||||
|
||||
let cli = client.get_nex_password(1699562916).await.unwrap();
|
||||
|
||||
println!("{:?}", cli);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
8
rnex-core/src/grpc/mod.rs
Normal file
8
rnex-core/src/grpc/mod.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
//! Legacy grpc communication server for being able to use this with pretendos infrastructure
|
||||
//! before account rs is finished.
|
||||
//!
|
||||
//! This WILL be deprecated as soon as account rs is in a stable state.
|
||||
use tonic::{Request, Status};
|
||||
|
||||
type InterceptorFunc = Box<(dyn Fn(Request<()>) -> Result<Request<()>, Status> + Send)>;
|
||||
pub mod account;
|
||||
170
rnex-core/src/kerberos/mod.rs
Normal file
170
rnex-core/src/kerberos/mod.rs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
use std::io::{Read, Write};
|
||||
use bytemuck::{bytes_of, Pod, Zeroable};
|
||||
use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
|
||||
use hmac::Hmac;
|
||||
use md5::{Digest, Md5};
|
||||
use rc4::{Rc4, Rc4Core, StreamCipher};
|
||||
use rc4::cipher::StreamCipherCoreWrapper;
|
||||
use rc4::consts::U16;
|
||||
use hmac::Mac;
|
||||
use rc4::KeyInit;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
type Md5Hmac = Hmac<md5::Md5>;
|
||||
|
||||
pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{
|
||||
let iteration_count = 65000 + pid%1024;
|
||||
|
||||
let mut key = password;
|
||||
|
||||
for _ in 0..iteration_count {
|
||||
let mut md5 = Md5::new();
|
||||
md5.update(key);
|
||||
key = md5.finalize().try_into().unwrap();
|
||||
}
|
||||
|
||||
key
|
||||
}
|
||||
#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct KerberosDateTime(pub u64);
|
||||
|
||||
impl KerberosDateTime{
|
||||
pub fn new(second: u64, minute: u64, hour: u64, day: u64, month: u64, year:u64 ) -> Self {
|
||||
Self(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26))
|
||||
}
|
||||
|
||||
pub fn now() -> Self{
|
||||
let now = chrono::Utc::now();
|
||||
Self::new(
|
||||
now.second() as u64,
|
||||
now.minute() as u64,
|
||||
now.hour() as u64,
|
||||
now.day() as u64,
|
||||
now.month() as u64,
|
||||
now.year() as u64,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_seconds(&self) -> u8{
|
||||
(self.0 & 0b111111) as u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_minutes(&self) -> u8{
|
||||
((self.0 >> 6) & 0b111111) as u8
|
||||
}
|
||||
#[inline]
|
||||
pub fn get_hours(&self) -> u8{
|
||||
((self.0 >> 12) & 0b111111) as u8
|
||||
}
|
||||
#[inline]
|
||||
pub fn get_days(&self) -> u8{
|
||||
((self.0 >> 17) & 0b111111) as u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_month(&self) -> u8{
|
||||
((self.0 >> 22) & 0b1111) as u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_year(&self) -> u64{
|
||||
(self.0 >> 26) & 0xFFFFFFFF
|
||||
}
|
||||
|
||||
pub fn to_regular_time(&self) -> chrono::DateTime<Utc>{
|
||||
NaiveDateTime::new(
|
||||
NaiveDate::from_ymd_opt(self.get_year() as i32, self.get_month() as u32, self.get_days() as u32).unwrap(),
|
||||
NaiveTime::from_hms_opt(self.get_hours() as u32, self.get_minutes() as u32, self.get_seconds() as u32).unwrap()
|
||||
).and_utc()
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for KerberosDateTime{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(self.0.serialize(writer)?)
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(Self(u64::deserialize(reader)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Pod, Zeroable, Copy, Clone)]
|
||||
#[repr(C, packed)]
|
||||
pub struct TicketInternalData{
|
||||
pub issued_time: KerberosDateTime,
|
||||
pub pid: u32,
|
||||
pub session_key: [u8; 32],
|
||||
}
|
||||
|
||||
impl TicketInternalData{
|
||||
pub(crate) fn new(pid: u32) -> Self{
|
||||
Self{
|
||||
issued_time: KerberosDateTime::now(),
|
||||
pid,
|
||||
session_key: rand::random()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn encrypt(&self, key: [u8; 16]) -> Box<[u8]>{
|
||||
let mut data = bytes_of(self).to_vec();
|
||||
|
||||
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
|
||||
rc4.apply_keystream(&mut data);
|
||||
|
||||
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
|
||||
|
||||
hmac.write_all(&data[..]).expect("failed to write data to hmac");
|
||||
|
||||
let hmac_result = &hmac.finalize().into_bytes()[..];
|
||||
|
||||
data.write_all(&hmac_result).expect("failed to write data to vec");
|
||||
|
||||
data.into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Pod, Zeroable, Copy, Clone)]
|
||||
#[repr(C, packed)]
|
||||
pub struct Ticket{
|
||||
pub session_key: [u8; 32],
|
||||
pub pid: u32,
|
||||
}
|
||||
|
||||
impl Ticket{
|
||||
pub fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]>{
|
||||
let mut data = bytes_of(self).to_vec();
|
||||
|
||||
internal_data.serialize(&mut data).expect("unable to write to vec");
|
||||
|
||||
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
|
||||
rc4.apply_keystream(&mut data);
|
||||
|
||||
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
|
||||
|
||||
hmac.write_all(&data[..]).expect("failed to write data to hmac");
|
||||
|
||||
let hmac_result = &hmac.finalize().into_bytes()[..];
|
||||
|
||||
data.write_all(&hmac_result).expect("failed to write data to vec");
|
||||
|
||||
data.into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use chrono::{Datelike, Utc};
|
||||
use crate::kerberos::KerberosDateTime;
|
||||
|
||||
#[test]
|
||||
fn kerberos_time_convert_test(){
|
||||
let time = KerberosDateTime(135904948834);
|
||||
|
||||
println!("{}", time.to_regular_time().to_rfc2822());
|
||||
}
|
||||
}
|
||||
26
rnex-core/src/lib.rs
Normal file
26
rnex-core/src/lib.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#![allow(dead_code)]
|
||||
// rnex makes extensive use of async functions in public traits
|
||||
// this is however fine because these traits should never(and i mean NEVER) be used dynamically
|
||||
#![allow(async_fn_in_trait)]
|
||||
//#![warn(missing_docs)]
|
||||
|
||||
|
||||
|
||||
extern crate self as rnex_core;
|
||||
|
||||
pub mod prudp;
|
||||
pub mod rmc;
|
||||
//mod protocols;
|
||||
|
||||
pub mod grpc;
|
||||
pub mod kerberos;
|
||||
pub mod nex;
|
||||
pub mod result;
|
||||
pub mod versions;
|
||||
pub mod web;
|
||||
pub mod common;
|
||||
pub mod reggie;
|
||||
pub mod rnex_proxy_common;
|
||||
pub mod util;
|
||||
pub mod executables;
|
||||
pub use macros::*;
|
||||
419
rnex-core/src/main.rs
Normal file
419
rnex-core/src/main.rs
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
#![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 crate::nex::account::Account;
|
||||
use crate::nex::auth_handler::{AuthHandler, RemoteAuthClientProtocol};
|
||||
use crate::nex::remote_console::RemoteConsole;
|
||||
use crate::nex::user::{RemoteUserProtocol, User};
|
||||
use crate::rmc::protocols::auth::Auth;
|
||||
use crate::rmc::protocols::auth::RawAuth;
|
||||
use crate::rmc::protocols::auth::RawAuthInfo;
|
||||
use crate::rmc::protocols::auth::RemoteAuth;
|
||||
use crate::rmc::protocols::matchmake_extension::RemoteMatchmakeExtension;
|
||||
use crate::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote, RemoteInstantiatable};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
use crate::rmc::structures::any::Any;
|
||||
use crate::rmc::structures::connection_data::ConnectionData;
|
||||
use crate::rmc::structures::matchmake::{CreateMatchmakeSessionParam, Gathering, MatchmakeParam, MatchmakeSession};
|
||||
use crate::rmc::structures::qresult::QResult;
|
||||
use chrono::{Local, SecondsFormat};
|
||||
use log::{error, info};
|
||||
use macros::rmc_struct;
|
||||
use once_cell::sync::Lazy;
|
||||
use simplelog::{
|
||||
ColorChoice, CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode, WriteLogger,
|
||||
};
|
||||
use std::fs::File;
|
||||
use std::marker::PhantomData;
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use std::ops::{BitAnd, BitOr};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Once, Weak};
|
||||
use std::time::Duration;
|
||||
use std::{env, fs};
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use tokio::task::JoinHandle;
|
||||
use crate::kerberos::KerberosDateTime;
|
||||
use crate::nex::matchmake::MatchmakeManager;
|
||||
use crate::rmc::protocols::secure::RemoteSecure;
|
||||
|
||||
mod prudp;
|
||||
pub mod rmc;
|
||||
//mod protocols;
|
||||
|
||||
mod grpc;
|
||||
mod kerberos;
|
||||
mod nex;
|
||||
mod result;
|
||||
mod versions;
|
||||
mod web;
|
||||
pub mod reggie;
|
||||
pub mod util;
|
||||
pub mod common;
|
||||
|
||||
|
||||
|
||||
static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(|| {
|
||||
env::var("AUTH_SERVER_PASSWORD")
|
||||
.ok()
|
||||
.unwrap_or("password".to_owned())
|
||||
});
|
||||
|
||||
static AUTH_SERVER_ACCOUNT: Lazy<Account> =
|
||||
Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD));
|
||||
static SECURE_SERVER_ACCOUNT: Lazy<Account> =
|
||||
Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD));
|
||||
|
||||
static AUTH_SERVER_PORT: Lazy<u16> = Lazy::new(|| {
|
||||
env::var("AUTH_SERVER_PORT")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(10000)
|
||||
});
|
||||
static SECURE_SERVER_PORT: Lazy<u16> = Lazy::new(|| {
|
||||
env::var("SECURE_SERVER_PORT")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(10001)
|
||||
});
|
||||
|
||||
static OWN_IP_PRIVATE: Lazy<Ipv4Addr> = Lazy::new(|| {
|
||||
env::var("SERVER_IP")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.expect("no private ip specified")
|
||||
});
|
||||
|
||||
static OWN_IP_PUBLIC: Lazy<String> =
|
||||
Lazy::new(|| env::var("SERVER_IP_PUBLIC").unwrap_or(OWN_IP_PRIVATE.to_string()));
|
||||
|
||||
static SECURE_STATION_URL: Lazy<String> = Lazy::new(|| {
|
||||
format!(
|
||||
"prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1",
|
||||
*OWN_IP_PUBLIC, *SECURE_SERVER_PORT
|
||||
)
|
||||
});
|
||||
|
||||
static FORCE_EXIT: Once = Once::new();
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
CombinedLogger::init(vec![
|
||||
TermLogger::new(
|
||||
LevelFilter::Info,
|
||||
Config::default(),
|
||||
TerminalMode::Mixed,
|
||||
ColorChoice::Auto,
|
||||
),
|
||||
WriteLogger::new(LevelFilter::max(), Config::default(), {
|
||||
fs::create_dir_all("log").unwrap();
|
||||
let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false);
|
||||
// this fixes windows being windows
|
||||
let date = date.replace(":", "-");
|
||||
let filename = format!("{}.log", date);
|
||||
if cfg!(windows) {
|
||||
File::create(format!("log\\{}", filename)).unwrap()
|
||||
} else {
|
||||
File::create(format!("log/{}", filename)).unwrap()
|
||||
}
|
||||
}),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
ctrlc::set_handler(||{
|
||||
FORCE_EXIT.call_once_force(|_|{
|
||||
println!("attempting exit");
|
||||
});
|
||||
}).unwrap();
|
||||
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
//start_servers().await;
|
||||
}
|
||||
/*
|
||||
|
||||
struct AuthServer{
|
||||
router: Arc<Router>,
|
||||
join_handle: JoinHandle<()>,
|
||||
socket: Socket
|
||||
}
|
||||
|
||||
async fn start_auth_server() -> AuthServer{
|
||||
info!("starting auth server on {}:{}", *OWN_IP_PRIVATE, *AUTH_SERVER_PORT);
|
||||
|
||||
let (router, join_handle) =
|
||||
Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT)).await
|
||||
.expect("unable to startauth server");
|
||||
|
||||
info!("setting up endpoints");
|
||||
|
||||
// dont assign it to the name _ as that will make it drop right here and now
|
||||
|
||||
let auth_protocol_config = AuthProtocolConfig{
|
||||
secure_server_account: &SECURE_SERVER_ACCOUNT,
|
||||
build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0",
|
||||
station_url: &SECURE_STATION_URL
|
||||
};
|
||||
|
||||
let rmcserver = RMCProtocolServer::new(Box::new([
|
||||
Box::new(auth::bound_protocol(auth_protocol_config))
|
||||
]));
|
||||
|
||||
let socket =
|
||||
Socket::new(
|
||||
router.clone(),
|
||||
VirtualPort::new(1,10),
|
||||
"6f599f81",
|
||||
Box::new(|_, count|{
|
||||
Box::pin(
|
||||
async move {
|
||||
|
||||
|
||||
let encryption_pairs = Vec::from_iter((0..=count).map(|_v| {
|
||||
let rc4: Rc4<U5> = Rc4::new_from_slice( "CD&ML".as_bytes()).unwrap();
|
||||
let cypher = Box::new(rc4);
|
||||
let server_cypher: Box<dyn StreamCipher + Send> = cypher;
|
||||
|
||||
let rc4: Rc4<U5> = Rc4::new_from_slice( "CD&ML".as_bytes()).unwrap();
|
||||
let cypher = Box::new(rc4);
|
||||
let client_cypher: Box<dyn StreamCipher + Send> = cypher;
|
||||
|
||||
EncryptionPair{
|
||||
recv: client_cypher,
|
||||
send: server_cypher
|
||||
}
|
||||
}));
|
||||
|
||||
Some((Vec::new(), encryption_pairs, None))
|
||||
}
|
||||
)
|
||||
}),
|
||||
Box::new(move |packet, socket, connection|{
|
||||
let rmcserver = rmcserver.clone();
|
||||
Box::pin(async move { rmcserver.process_message(packet, socket, connection).await; })
|
||||
})
|
||||
).await.expect("unable to create socket");
|
||||
|
||||
AuthServer{
|
||||
join_handle,
|
||||
router,
|
||||
socket,
|
||||
}
|
||||
}
|
||||
|
||||
struct SecureServer{
|
||||
router: Arc<Router>,
|
||||
join_handle: JoinHandle<()>,
|
||||
socket: Socket
|
||||
}
|
||||
|
||||
async fn start_secure_server() -> SecureServer{
|
||||
info!("starting secure server on {}:{}", *OWN_IP_PRIVATE, *SECURE_SERVER_PORT);
|
||||
|
||||
let (router, join_handle) =
|
||||
Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT)).await
|
||||
.expect("unable to startauth server");
|
||||
|
||||
info!("setting up endpoints");
|
||||
|
||||
let matchmake_data = Arc::new(RwLock::new(
|
||||
MatchmakeData{
|
||||
matchmake_sessions: BTreeMap::new()
|
||||
}
|
||||
));
|
||||
|
||||
let rmcserver = RMCProtocolServer::new(Box::new([
|
||||
Box::new(block_if_maintenance),
|
||||
Box::new(protocols::secure::bound_protocol()),
|
||||
Box::new(protocols::matchmake::bound_protocol(matchmake_data.clone())),
|
||||
Box::new(protocols::matchmake_extension::bound_protocol(matchmake_data)),
|
||||
Box::new(protocols::nat_traversal::bound_protocol())
|
||||
]));
|
||||
|
||||
let socket =
|
||||
Socket::new(
|
||||
router.clone(),
|
||||
VirtualPort::new(1,10),
|
||||
"6f599f81",
|
||||
Box::new(|p, count|{
|
||||
Box::pin(
|
||||
async move {
|
||||
let (session_key, pid, check_value) = read_secure_connection_data(&p.payload, &SECURE_SERVER_ACCOUNT)?;
|
||||
|
||||
let check_value_response = check_value + 1;
|
||||
|
||||
let data = bytemuck::bytes_of(&check_value_response);
|
||||
|
||||
let mut response = Vec::new();
|
||||
|
||||
data.serialize(&mut response).ok()?;
|
||||
|
||||
let encryption_pairs = generate_secure_encryption_pairs(session_key, count);
|
||||
|
||||
Some((response, encryption_pairs, Some(
|
||||
ActiveSecureConnectionData{
|
||||
pid,
|
||||
session_key
|
||||
}
|
||||
)))
|
||||
}
|
||||
)
|
||||
}),
|
||||
Box::new(move |packet, socket, connection|{
|
||||
let rmcserver = rmcserver.clone();
|
||||
Box::pin(async move { rmcserver.process_message(packet, socket, connection).await; })
|
||||
})
|
||||
).await.expect("unable to create socket");
|
||||
|
||||
SecureServer{
|
||||
join_handle,
|
||||
router,
|
||||
socket,
|
||||
}
|
||||
}*/
|
||||
/*
|
||||
async fn start_auth() -> JoinHandle<()> {
|
||||
tokio::spawn(async {
|
||||
let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT))
|
||||
.await
|
||||
.expect("unable to start router");
|
||||
|
||||
let mut socket_secure = router_secure
|
||||
.add_socket(VirtualPort::new(1, 10), Unsecure(
|
||||
"6f599f81"
|
||||
))
|
||||
.await
|
||||
.expect("unable to add socket");
|
||||
|
||||
// let conn = socket_secure.connect(auth_sockaddr).await.unwrap();
|
||||
|
||||
while !FORCE_EXIT.is_completed() {
|
||||
let Some(conn) = socket_secure.accept().await else {
|
||||
error!("server crashed");
|
||||
return;
|
||||
};
|
||||
|
||||
info!("new connected user!");
|
||||
|
||||
let _ = new_rmc_gateway_connection(conn, |_| {
|
||||
Arc::new(AuthHandler {
|
||||
destination_server_acct: &SECURE_SERVER_ACCOUNT,
|
||||
build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0",
|
||||
station_url: &SECURE_STATION_URL,
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn start_secure() -> JoinHandle<()> {
|
||||
tokio::spawn(async {
|
||||
let mmm = Arc::new(MatchmakeManager{
|
||||
gid_counter: AtomicU32::new(1),
|
||||
sessions: Default::default(),
|
||||
users: Default::default(),
|
||||
rv_cid_counter: AtomicU32::new(1),
|
||||
});
|
||||
|
||||
let weak_mmm = Arc::downgrade(&mmm);
|
||||
|
||||
MatchmakeManager::initialize_garbage_collect_thread(weak_mmm).await;
|
||||
|
||||
let web_server = web::start_web(mmm.clone()).await;
|
||||
|
||||
let (router_secure, _) =
|
||||
Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT))
|
||||
.await
|
||||
.expect("unable to start router");
|
||||
|
||||
let mut socket_secure = router_secure
|
||||
.add_socket(
|
||||
VirtualPort::new(1, 10),
|
||||
Secure(
|
||||
"6f599f81",
|
||||
&SECURE_SERVER_ACCOUNT
|
||||
),
|
||||
)
|
||||
.await
|
||||
.expect("unable to add socket");
|
||||
|
||||
// let conn = socket_secure.connect(auth_sockaddr).await.unwrap();
|
||||
|
||||
while !FORCE_EXIT.is_completed() {
|
||||
let Some(conn) = socket_secure.accept().await else {
|
||||
error!("server crashed");
|
||||
return;
|
||||
};
|
||||
|
||||
info!("new connected user on secure :D!");
|
||||
|
||||
let ip = conn.socket_addr;
|
||||
let pid = conn.user_id;
|
||||
|
||||
let _ = new_rmc_gateway_connection(conn, |r| {
|
||||
Arc::new_cyclic(|w| User {
|
||||
ip,
|
||||
pid,
|
||||
this: w.clone(),
|
||||
remote: RemoteConsole::new(r),
|
||||
station_url: Default::default(),
|
||||
matchmake_manager: mmm.clone()
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn start_test() {
|
||||
let addr = SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT);
|
||||
|
||||
let virt_addr = VirtualPort::new(1, 10);
|
||||
let prudp_addr = PRUDPSockAddr::new(addr, virt_addr);
|
||||
|
||||
let (router_test, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, 26969))
|
||||
.await
|
||||
.expect("unable to start router");
|
||||
|
||||
let mut socket_secure = router_test
|
||||
.add_socket(VirtualPort::new(1, 10), Unsecure("6f599f81"))
|
||||
.await
|
||||
.expect("unable to add socket");
|
||||
|
||||
let conn = socket_secure.connect(prudp_addr).await.unwrap();
|
||||
|
||||
let remote = new_rmc_gateway_connection(conn, |r| {
|
||||
Arc::new(OnlyRemote::<RemoteUserProtocol>::new(r))
|
||||
});
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
let urls = vec!["prudp:/address=192.168.178.45;port=60146;Pl=2;natf=0;natm=0;pmp=0;sid=15;upnp=0".to_owned()];
|
||||
}
|
||||
|
||||
async fn start_servers() {
|
||||
#[cfg(feature = "auth")]
|
||||
let auth_server = start_auth().await;
|
||||
#[cfg(feature = "secure")]
|
||||
let secure_server = start_secure().await;
|
||||
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
|
||||
//start_test().await;
|
||||
|
||||
|
||||
|
||||
#[cfg(feature = "auth")]
|
||||
auth_server.await.expect("auth server crashed");
|
||||
#[cfg(feature = "secure")]
|
||||
secure_server.await.expect("auth server crashed");
|
||||
}
|
||||
*/
|
||||
39
rnex-core/src/nex/account.rs
Normal file
39
rnex-core/src/nex/account.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
187
rnex-core/src/nex/auth_handler.rs
Normal file
187
rnex-core/src/nex/auth_handler.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
388
rnex-core/src/nex/matchmake.rs
Normal file
388
rnex-core/src/nex/matchmake.rs
Normal 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
5
rnex-core/src/nex/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub mod account;
|
||||
pub mod auth_handler;
|
||||
pub mod user;
|
||||
pub mod remote_console;
|
||||
pub mod matchmake;
|
||||
21
rnex-core/src/nex/remote_console.rs
Normal file
21
rnex-core/src/nex/remote_console.rs
Normal 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
568
rnex-core/src/nex/user.rs
Normal 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 ¶m.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{
|
||||
|
||||
}
|
||||
3
rnex-core/src/prudp/mod.rs
Normal file
3
rnex-core/src/prudp/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod virtual_port;
|
||||
pub mod station_url;
|
||||
pub mod socket_addr;
|
||||
38
rnex-core/src/prudp/socket_addr.rs
Normal file
38
rnex-core/src/prudp/socket_addr.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
use std::io::Write;
|
||||
use std::net::SocketAddrV4;
|
||||
use hmac::{Hmac};
|
||||
use md5::digest::Mac;
|
||||
use macros::RmcSerialize;
|
||||
use rnex_core::prudp::virtual_port::VirtualPort;
|
||||
|
||||
type Md5Hmac = Hmac<md5::Md5>;
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Ord, PartialOrd, RmcSerialize)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct PRUDPSockAddr{
|
||||
pub regular_socket_addr: SocketAddrV4,
|
||||
pub virtual_port: VirtualPort
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl PRUDPSockAddr{
|
||||
|
||||
pub fn new(regular_socket_addr: SocketAddrV4, virtual_port: VirtualPort) -> Self{
|
||||
Self{
|
||||
regular_socket_addr,
|
||||
virtual_port
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculate_connection_signature(&self) -> [u8; 16] {
|
||||
let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("fuck");
|
||||
|
||||
let mut data = self.regular_socket_addr.ip().octets().to_vec();
|
||||
//data.extend_from_slice(&self.regular_socket_addr.port().to_be_bytes());
|
||||
|
||||
hmac.write_all(&data).expect("figuring this out was complete ass");
|
||||
let result: [u8; 16] = hmac.finalize().into_bytes()[0..16].try_into().expect("fuck");
|
||||
result
|
||||
}
|
||||
}
|
||||
198
rnex-core/src/prudp/station_url.rs
Normal file
198
rnex-core/src/prudp/station_url.rs
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
use std::net::Ipv4Addr;
|
||||
use log::error;
|
||||
use std::fmt::{Debug, Display, Formatter, Write};
|
||||
use std::io::Read;
|
||||
use crate::prudp::station_url::Type::{PRUDP, PRUDPS, UDP};
|
||||
use crate::prudp::station_url::UrlOptions::{Address, ConnectionID, NatFiltering, NatMapping, NatType, Platform, PMP, Port, PrincipalID, RVConnectionID, StreamID, StreamType, UPNP, PID};
|
||||
use crate::rmc::structures::Error::StationUrlInvalid;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Type{
|
||||
UDP,
|
||||
PRUDP,
|
||||
PRUDPS
|
||||
}
|
||||
|
||||
pub mod nat_types{
|
||||
pub const BEHIND_NAT: u8 = 1;
|
||||
pub const PUBLIC: u8 = 2;
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub enum UrlOptions {
|
||||
Address(Ipv4Addr),
|
||||
Port(u16),
|
||||
StreamType(u8),
|
||||
StreamID(u8),
|
||||
ConnectionID(u8),
|
||||
PrincipalID(u32),
|
||||
NatType(u8),
|
||||
NatMapping(u8),
|
||||
NatFiltering(u8),
|
||||
UPNP(u8),
|
||||
RVConnectionID(u32),
|
||||
Platform(u8),
|
||||
PMP(u8),
|
||||
PID(u32),
|
||||
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct StationUrl{
|
||||
pub url_type: Type,
|
||||
pub options: Vec<UrlOptions>
|
||||
}
|
||||
|
||||
impl StationUrl{
|
||||
pub fn read_options(options: &str) -> Option<Vec<UrlOptions>>{
|
||||
let mut options_out = Vec::new();
|
||||
|
||||
for option in options.split(';'){
|
||||
if option == "" { continue; }
|
||||
let mut option_parts = option.split('=');
|
||||
let option_name= option_parts.next()?.to_ascii_lowercase();
|
||||
let option_value = option_parts.next()?;
|
||||
|
||||
match option_name.as_ref(){
|
||||
"address" => {
|
||||
options_out.push(Address(option_value.parse().ok()?))
|
||||
},
|
||||
"port" => {
|
||||
options_out.push(Port(option_value.parse().ok()?))
|
||||
}
|
||||
"natf" => {
|
||||
options_out.push(NatFiltering(option_value.parse().ok()?))
|
||||
}
|
||||
"natm" => {
|
||||
options_out.push(NatMapping(option_value.parse().ok()?))
|
||||
}
|
||||
"sid" => {
|
||||
options_out.push(StreamID(option_value.parse().ok()?))
|
||||
}
|
||||
"upnp" => {
|
||||
options_out.push(UPNP(option_value.parse().ok()?))
|
||||
}
|
||||
"type" => {
|
||||
options_out.push(NatType(option_value.parse().ok()?))
|
||||
}
|
||||
"stream" => {
|
||||
options_out.push(StreamType(option_value.parse().ok()?))
|
||||
}
|
||||
"RVCID" => {
|
||||
options_out.push(RVConnectionID(option_value.parse().ok()?))
|
||||
}
|
||||
"rvcid" => {
|
||||
options_out.push(RVConnectionID(option_value.parse().ok()?))
|
||||
}
|
||||
"pl" => {
|
||||
options_out.push(Platform(option_value.parse().ok()?))
|
||||
}
|
||||
"pmp" => {
|
||||
options_out.push(PMP(option_value.parse().ok()?))
|
||||
},
|
||||
"pid" => {
|
||||
options_out.push(PID(option_value.parse().ok()?))
|
||||
},
|
||||
"PID" => {
|
||||
options_out.push(PID(option_value.parse().ok()?))
|
||||
},
|
||||
_ => {
|
||||
error!("unimplemented option type, skipping: {}", option_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Some(options_out)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for StationUrl{
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, ()> {
|
||||
let (url_type, options) = value.split_at(value.find(":/").ok_or(())?);
|
||||
|
||||
let options = &options[2..];
|
||||
|
||||
let url_type = match url_type{
|
||||
"udp" => UDP,
|
||||
"prudp" => PRUDP,
|
||||
"prudps" => PRUDPS,
|
||||
_ => return Err(())
|
||||
};
|
||||
|
||||
let options = Self::read_options(options).ok_or(())?;
|
||||
|
||||
Ok(
|
||||
Self{
|
||||
url_type,
|
||||
options
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<String> for &'a StationUrl{
|
||||
fn into(self) -> String {
|
||||
let mut url = match self.url_type{
|
||||
UDP => "udp:/",
|
||||
PRUDP => "prudp:/",
|
||||
PRUDPS => "prudps:/"
|
||||
}.to_owned();
|
||||
|
||||
for option in &self.options{
|
||||
match option{
|
||||
Address(v) => write!(url, "address={}", v).expect("failed to write"),
|
||||
Port(v) => write!(url, "port={}", v).expect("failed to write"),
|
||||
StreamType(v) => write!(url, "stream={}", v).expect("failed to write"),
|
||||
StreamID(v) => write!(url, "sid={}", v).expect("failed to write"),
|
||||
ConnectionID(v) => write!(url, "CID={}", v).expect("failed to write"),
|
||||
PrincipalID(v) => write!(url, "PID={}", v).expect("failed to write"),
|
||||
NatType(v) => write!(url, "type={}", v).expect("failed to write"),
|
||||
NatMapping(v) => write!(url, "natm={}", v).expect("failed to write"),
|
||||
NatFiltering(v) => write!(url, "natf={}", v).expect("failed to write"),
|
||||
UPNP(v) => write!(url, "upnp={}", v).expect("failed to write"),
|
||||
RVConnectionID(v) => write!(url, "RVCID={}", v).expect("failed to write"),
|
||||
Platform(v) => write!(url, "pl={}", v).expect("failed to write"),
|
||||
PMP(v) => write!(url, "pmp={}", v).expect("failed to write"),
|
||||
PID(v) => write!(url, "PID={}", v).expect("failed to write"),
|
||||
}
|
||||
write!(url, ";").expect("failed to write");
|
||||
}
|
||||
|
||||
url[0..url.len()-1].into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StationUrl{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let str: String = self.into();
|
||||
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl RmcSerialize for StationUrl{
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let str = String::deserialize(reader)?;
|
||||
|
||||
Self::try_from(str.as_str()).map_err(|_| StationUrlInvalid)
|
||||
}
|
||||
fn serialize(&self, writer: &mut dyn std::io::Write) -> crate::rmc::structures::Result<()> {
|
||||
let str: String = self.into();
|
||||
|
||||
str.serialize(writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for StationUrl{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let str: String = self.into();
|
||||
f.write_str(&str)
|
||||
}
|
||||
}
|
||||
49
rnex-core/src/prudp/virtual_port.rs
Normal file
49
rnex-core/src/prudp/virtual_port.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use std::fmt::{Debug, Formatter};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use v_byte_helpers::SwapEndian;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Pod, Zeroable, SwapEndian, Hash, Default)]
|
||||
pub struct VirtualPort(pub u8);
|
||||
|
||||
impl VirtualPort {
|
||||
|
||||
#[inline]
|
||||
pub const fn get_stream_type(self) -> u8 {
|
||||
(self.0 & 0xF0) >> 4
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn get_port_number(self) -> u8 {
|
||||
self.0 & 0x0F
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn stream_type(self, val: u8) -> Self {
|
||||
let masked_val = val & 0x0F;
|
||||
assert_eq!(masked_val, val);
|
||||
|
||||
Self((self.0 & 0x0F) | (masked_val << 4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn port_number(self, val: u8) -> Self {
|
||||
let masked_val = val & 0x0F;
|
||||
assert_eq!(masked_val, val);
|
||||
|
||||
Self((self.0 & 0xF0) | masked_val)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new(port: u8, stream_type: u8) -> Self {
|
||||
Self(0).stream_type(stream_type).port_number(port)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for VirtualPort {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let stream_type = self.get_stream_type();
|
||||
let port_number = self.get_port_number();
|
||||
write!(f, "VirtualPort{{ stream_type: {}, port_number: {} }}", stream_type, port_number)
|
||||
}
|
||||
}
|
||||
82
rnex-core/src/reggie.rs
Normal file
82
rnex-core/src/reggie.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use std::{env, fs, io};
|
||||
use std::hash::Hash;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::net::{SocketAddrV4, ToSocketAddrs};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use macros::{method_id, rmc_proto, rmc_struct, RmcSerialize};
|
||||
use once_cell::sync::Lazy;
|
||||
use rustls::{ClientConfig, RootCertStore, ServerConfig};
|
||||
use rustls::client::WebPkiServerVerifier;
|
||||
use rustls::server::WebPkiClientVerifier;
|
||||
use rustls_pki_types::{CertificateDer, PrivateKeyDer, ServerName, TrustAnchor};
|
||||
use thiserror::Error;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_rustls::{TlsAcceptor, TlsConnector};
|
||||
use tokio_rustls::client::TlsStream;
|
||||
use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream};
|
||||
use tokio_tungstenite::tungstenite::Message;
|
||||
use webpki::anchor_from_trusted_cert;
|
||||
use crate::common::setup;
|
||||
use crate::define_rmc_proto;
|
||||
use crate::nex::account::Account;
|
||||
use crate::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote, RmcCallable, RmcConnection};
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
pub trait UnitPacketRead: AsyncRead + Unpin{
|
||||
async fn read_buffer(&mut self) -> Result<Vec<u8>, io::Error>{
|
||||
let mut len_raw: [u8; 4] = [0; 4];
|
||||
|
||||
self.read_exact(&mut len_raw).await?;
|
||||
|
||||
let len = u32::from_le_bytes(len_raw);
|
||||
|
||||
let mut vec = vec![0u8; len as _];
|
||||
|
||||
self.read_exact(&mut vec).await?;
|
||||
|
||||
Ok(vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + Unpin> UnitPacketRead for T{}
|
||||
pub trait UnitPacketWrite: AsyncWrite + Unpin{
|
||||
async fn send_buffer(&mut self, data: &[u8]) -> Result<(), io::Error> {
|
||||
let mut dest_data = Vec::new();
|
||||
|
||||
data.serialize(&mut dest_data).expect("ran out of memory or something");
|
||||
|
||||
self.write_all(&dest_data[..]).await?;
|
||||
|
||||
self.flush().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite + Unpin> UnitPacketWrite for T{}
|
||||
|
||||
|
||||
|
||||
#[rmc_proto(1)]
|
||||
pub trait EdgeNodeManagement {
|
||||
#[method_id(1)]
|
||||
async fn get_url(&self, seed: u64) -> Result<SocketAddrV4, ErrorCode>;
|
||||
}
|
||||
|
||||
define_rmc_proto!(
|
||||
proto EdgeNodeHolder{
|
||||
EdgeNodeManagement
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(RmcSerialize, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum EdgeNodeHolderConnectOption{
|
||||
DontRegister = 0,
|
||||
Register(SocketAddrV4) = 1
|
||||
}
|
||||
23
rnex-core/src/result.rs
Normal file
23
rnex-core/src/result.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use std::error::Error;
|
||||
use log::error;
|
||||
|
||||
pub trait ResultExtension{
|
||||
type Output;
|
||||
|
||||
fn display_err_or_some(self) -> Option<Self::Output>;
|
||||
}
|
||||
|
||||
impl<T, U: Error> ResultExtension for Result<T, U>{
|
||||
type Output = T;
|
||||
|
||||
fn display_err_or_some(self) -> Option<Self::Output> {
|
||||
match self{
|
||||
Ok(v) => Some(v),
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
rnex-core/src/rmc/message.rs
Normal file
89
rnex-core/src/rmc/message.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
use std::io;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use bytemuck::bytes_of;
|
||||
use log::error;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::response::{ErrorCode, RMCResponseResult};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RMCMessage{
|
||||
pub protocol_id: u16,
|
||||
pub call_id: u32,
|
||||
pub method_id: u32,
|
||||
|
||||
pub rest_of_data: Vec<u8>
|
||||
}
|
||||
|
||||
impl RMCMessage{
|
||||
pub fn new(stream: &mut (impl Seek + Read)) -> io::Result<Self>{
|
||||
let size: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut header_size = 1 + 4 + 4;
|
||||
|
||||
let protocol_id: u8 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let protocol_id= protocol_id & (!0x80);
|
||||
|
||||
let protocol_id: u16 = match protocol_id{
|
||||
0x7F => {
|
||||
header_size += 2;
|
||||
stream.read_struct(IS_BIG_ENDIAN)?
|
||||
},
|
||||
_ => protocol_id as u16
|
||||
};
|
||||
|
||||
let call_id = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let method_id = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut rest_of_data = Vec::new();
|
||||
|
||||
stream.read_to_end(&mut rest_of_data)?;
|
||||
|
||||
if header_size + rest_of_data.len() != size as usize {
|
||||
error!("received incorrect rmc packet: expected size {} but found {}", size, header_size + rest_of_data.len());
|
||||
}
|
||||
|
||||
|
||||
|
||||
//stream.
|
||||
Ok(Self{
|
||||
protocol_id,
|
||||
method_id,
|
||||
call_id,
|
||||
rest_of_data
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_data(&self) -> Vec<u8>{
|
||||
let size = (1 + 4 + 4 + self.rest_of_data.len()) as u32;
|
||||
|
||||
let mut output = Vec::new();
|
||||
|
||||
output.write_all(bytes_of(&size)).expect("unable to write size");
|
||||
|
||||
let proto_id = self.protocol_id as u8 | 0x80;
|
||||
|
||||
output.write_all(bytes_of(&proto_id)).expect("unable to write size");
|
||||
|
||||
output.write_all(bytes_of(&self.call_id)).expect("unable to write size");
|
||||
output.write_all(bytes_of(&self.method_id)).expect("unable to write size");
|
||||
|
||||
output.write_all(&self.rest_of_data).expect("unable to write data");
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
pub fn error_result_with_code(&self, error_code: ErrorCode) -> RMCResponseResult{
|
||||
RMCResponseResult::Error {
|
||||
call_id: self.call_id,
|
||||
error_code
|
||||
}
|
||||
}
|
||||
|
||||
pub fn success_with_data(&self, data: Vec<u8>) -> RMCResponseResult{
|
||||
RMCResponseResult::Success {
|
||||
call_id: self.call_id,
|
||||
method_id: self.method_id,
|
||||
data
|
||||
}
|
||||
}
|
||||
}
|
||||
7
rnex-core/src/rmc/mod.rs
Normal file
7
rnex-core/src/rmc/mod.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pub mod message;
|
||||
pub mod structures;
|
||||
pub mod response;
|
||||
pub mod protocols;
|
||||
|
||||
|
||||
|
||||
47
rnex-core/src/rmc/protocols/auth.rs
Normal file
47
rnex-core/src/rmc/protocols/auth.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use rnex_core::rmc::response::ErrorCode;
|
||||
use rnex_core::rmc::structures::any::Any;
|
||||
use crate::rmc::structures::connection_data::ConnectionData;
|
||||
use rnex_core::rmc::structures::qresult::QResult;
|
||||
use macros::{method_id, rmc_proto};
|
||||
|
||||
|
||||
/// This is the representation for `Ticket Granting`(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[rmc_proto(10)]
|
||||
pub trait Auth {
|
||||
/// representation of the `Login` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(1)]
|
||||
async fn login(&self, name: String) -> Result<(), ErrorCode>;
|
||||
|
||||
/// representation of the `LoginEx` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(2)]
|
||||
async fn login_ex(
|
||||
&self,
|
||||
name: String,
|
||||
extra_data: Any,
|
||||
) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode>;
|
||||
|
||||
/// representation of the `RequestTicket` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(3)]
|
||||
async fn request_ticket(
|
||||
&self,
|
||||
source_pid: u32,
|
||||
destination_pid: u32,
|
||||
) -> Result<(QResult, Vec<u8>), ErrorCode>;
|
||||
|
||||
/// representation of the `GetPID` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(4)]
|
||||
async fn get_pid(&self, username: String) -> Result<u32, ErrorCode>;
|
||||
|
||||
/// representation of the `LoginWithContext` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(5)]
|
||||
async fn get_name(&self, pid: u32) -> Result<String, ErrorCode>;
|
||||
|
||||
// `LoginWithContext` is left out here because we don't need it right now and versioning still
|
||||
// needs to be figured out
|
||||
}
|
||||
17
rnex-core/src/rmc/protocols/matchmake.rs
Normal file
17
rnex-core/src/rmc/protocols/matchmake.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use rnex_core::prudp::station_url::StationUrl;
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
|
||||
#[rmc_proto(21)]
|
||||
pub trait Matchmake{
|
||||
#[method_id(2)]
|
||||
async fn unregister_gathering(&self, gid: u32) -> Result<bool, ErrorCode>;
|
||||
#[method_id(41)]
|
||||
async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode>;
|
||||
|
||||
#[method_id(42)]
|
||||
async fn update_session_host(&self, gid: u32, change_owner: bool) -> Result<(), ErrorCode>;
|
||||
|
||||
#[method_id(44)]
|
||||
async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec<u32>, participants_only: bool) -> Result<(), ErrorCode>;
|
||||
}
|
||||
8
rnex-core/src/rmc/protocols/matchmake_ext.rs
Normal file
8
rnex-core/src/rmc/protocols/matchmake_ext.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
|
||||
#[rmc_proto(50)]
|
||||
pub trait MatchmakeExt{
|
||||
#[method_id(1)]
|
||||
async fn end_participation(&self, gid: u32, message: String) -> Result<bool, ErrorCode>;
|
||||
}
|
||||
32
rnex-core/src/rmc/protocols/matchmake_extension.rs
Normal file
32
rnex-core/src/rmc/protocols/matchmake_extension.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession};
|
||||
|
||||
#[rmc_proto(109)]
|
||||
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(8)]
|
||||
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>;
|
||||
|
||||
#[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>;
|
||||
|
||||
#[method_id(39)]
|
||||
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>;
|
||||
|
||||
#[method_id(41)]
|
||||
async fn find_matchmake_session_by_gathering_id_detail(&self, gid: u32) -> Result<MatchmakeSession, ErrorCode>;
|
||||
}
|
||||
342
rnex-core/src/rmc/protocols/mod.rs
Normal file
342
rnex-core/src/rmc/protocols/mod.rs
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
#![allow(async_fn_in_trait)]
|
||||
|
||||
pub mod auth;
|
||||
pub mod secure;
|
||||
pub mod notifications;
|
||||
pub mod matchmake;
|
||||
pub mod matchmake_extension;
|
||||
pub mod nat_traversal;
|
||||
pub mod matchmake_ext;
|
||||
pub mod ranking;
|
||||
|
||||
use crate::util::{SendingBufferConnection, SplittableBufferConnection};
|
||||
use crate::rmc::message::RMCMessage;
|
||||
use crate::rmc::protocols::RemoteCallError::ConnectionBroke;
|
||||
use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult};
|
||||
use crate::rmc::structures;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
use log::{error, info};
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::io::Cursor;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::{Mutex, Notify};
|
||||
use tokio::time::{sleep, sleep_until, Instant};
|
||||
use crate::result::ResultExtension;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RemoteCallError {
|
||||
#[error("Call to remote timed out whilest waiting on response.")]
|
||||
Timeout,
|
||||
#[error("A server side rmc error occurred: {0:?}")]
|
||||
ServerError(ErrorCode),
|
||||
#[error("Connection broke")]
|
||||
ConnectionBroke,
|
||||
#[error("Error reading response data: {0}")]
|
||||
InvalidResponse(#[from] structures::Error),
|
||||
}
|
||||
|
||||
pub struct RmcConnection(pub SendingBufferConnection, pub RmcResponseReceiver);
|
||||
|
||||
pub struct RmcResponseReceiver(Arc<Notify>, Arc<Mutex<HashMap<u32, RMCResponse>>>);
|
||||
|
||||
impl RmcConnection {
|
||||
pub async fn make_raw_call<T: RmcSerialize>(
|
||||
&self,
|
||||
message: &RMCMessage,
|
||||
) -> Result<T, RemoteCallError> {
|
||||
self.make_raw_call_no_response(message).await?;
|
||||
|
||||
let data = self.1.get_response_data(message.call_id).await?;
|
||||
|
||||
let out = <T as RmcSerialize>::deserialize(&mut Cursor::new(data))?;
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub async fn make_raw_call_no_response(
|
||||
&self,
|
||||
message: &RMCMessage,
|
||||
) -> Result<(), RemoteCallError> {
|
||||
let message_data = message.to_data();
|
||||
|
||||
self.0.send(message_data).await.ok_or(ConnectionBroke)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn disconnect(&self){
|
||||
self.0.disconnect().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcResponseReceiver {
|
||||
// returns none if timed out
|
||||
pub async fn get_response_data(&self, call_id: u32) -> Result<Vec<u8>, RemoteCallError> {
|
||||
let mut end_wait_time = Instant::now();
|
||||
end_wait_time += Duration::from_secs(5);
|
||||
|
||||
let sleep_fut = sleep_until(end_wait_time);
|
||||
tokio::pin!(sleep_fut);
|
||||
|
||||
let mut sleep_manual_unlock_fut = Instant::now();
|
||||
sleep_manual_unlock_fut += Duration::from_secs(4);
|
||||
|
||||
let sleep_manual_unlock_fut = sleep_until(sleep_manual_unlock_fut);
|
||||
tokio::pin!(sleep_manual_unlock_fut);
|
||||
|
||||
loop {
|
||||
let mut locked = self.1.lock().await;
|
||||
|
||||
if let Some(v) = locked.remove(&call_id) {
|
||||
match v.response_result{
|
||||
RMCResponseResult::Success {
|
||||
data,
|
||||
..
|
||||
} => return Ok(data),
|
||||
RMCResponseResult::Error {
|
||||
error_code,
|
||||
..
|
||||
} => return Err(RemoteCallError::ServerError(error_code))
|
||||
}
|
||||
}
|
||||
|
||||
drop(locked);
|
||||
|
||||
let notif_fut = self.0.notified();
|
||||
|
||||
tokio::select! {
|
||||
_ = &mut sleep_manual_unlock_fut => {
|
||||
continue;
|
||||
}
|
||||
_ = &mut sleep_fut => {
|
||||
return Err(RemoteCallError::Timeout);
|
||||
}
|
||||
_ = notif_fut => {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasRmcConnection {
|
||||
fn get_connection(&self) -> &RmcConnection;
|
||||
}
|
||||
|
||||
pub trait RemoteObject {
|
||||
fn new(conn: RmcConnection) -> Self;
|
||||
}
|
||||
|
||||
impl RemoteObject for () {
|
||||
fn new(_: RmcConnection) -> Self {}
|
||||
}
|
||||
|
||||
pub trait RmcCallable {
|
||||
//type Remote: RemoteObject;
|
||||
fn rmc_call(
|
||||
&self,
|
||||
responder: &SendingBufferConnection,
|
||||
protocol_id: u16,
|
||||
method_id: u32,
|
||||
call_id: u32,
|
||||
rest: Vec<u8>,
|
||||
) -> impl std::future::Future<Output = ()> + Send;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! define_rmc_proto {
|
||||
(proto $name:ident{
|
||||
$($protocol:path),*
|
||||
}) => {
|
||||
paste::paste!{
|
||||
pub trait [<Local $name>]: std::any::Any $( + [<Raw $protocol>] + $protocol)* {
|
||||
async fn rmc_call(&self, remote_response_connection: &rnex_core::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>){
|
||||
match protocol_id{
|
||||
$(
|
||||
[<Raw $protocol Info>]::PROTOCOL_ID => <Self as [<Raw $protocol>]>::rmc_call_proto(self, remote_response_connection, method_id, call_id, rest).await,
|
||||
)*
|
||||
v => log::error!("invalid protocol called on rmc object {}", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<Remote $name>](rnex_core::rmc::protocols::RmcConnection);
|
||||
|
||||
impl rnex_core::rmc::protocols::RemoteInstantiatable for [<Remote $name>]{
|
||||
fn new(conn: rnex_core::rmc::protocols::RmcConnection) -> Self{
|
||||
Self(conn)
|
||||
}
|
||||
async fn disconnect(&self){
|
||||
self.0.disconnect().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl rnex_core::rmc::protocols::HasRmcConnection for [<Remote $name>]{
|
||||
fn get_connection(&self) -> &rnex_core::rmc::protocols::RmcConnection{
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl [<Remote $protocol>] for [<Remote $name>]{}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This is a special case to allow unit to represent the fact that no object is represented.
|
||||
impl RmcCallable for () {
|
||||
async fn rmc_call(
|
||||
&self,
|
||||
_remote_response_connection: &SendingBufferConnection,
|
||||
_protocol_id: u16,
|
||||
_method_id: u32,
|
||||
_call_id: u32,
|
||||
_rest: Vec<u8>,
|
||||
) {
|
||||
//todo: maybe reply with not implemented(?)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RemoteInstantiatable{
|
||||
fn new(conn: RmcConnection) -> Self;
|
||||
async fn disconnect(&self);
|
||||
}
|
||||
|
||||
pub struct OnlyRemote<T: RemoteInstantiatable>(T);
|
||||
|
||||
impl<T: RemoteInstantiatable> Deref for OnlyRemote<T>{
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RemoteInstantiatable> OnlyRemote<T>{
|
||||
pub fn new(conn: RmcConnection) -> Self{
|
||||
Self(T::new(conn))
|
||||
}
|
||||
|
||||
pub async fn disconnect(&self) {
|
||||
self.0.disconnect().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RemoteInstantiatable> RmcCallable for OnlyRemote<T>{
|
||||
fn rmc_call(&self, _responder: &SendingBufferConnection, _protocol_id: u16, _method_id: u32, _call_id: u32, _rest: Vec<u8>) -> impl Future<Output = ()> + Send {
|
||||
// maybe respond with not implemented or something
|
||||
async{}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_incoming<T: RmcCallable + Send + Sync + 'static>(
|
||||
mut connection: SplittableBufferConnection,
|
||||
remote: Arc<T>,
|
||||
notify: Arc<Notify>,
|
||||
incoming: Arc<Mutex<HashMap<u32, RMCResponse>>>,
|
||||
) {
|
||||
let sending_conn = connection.duplicate_sender();
|
||||
|
||||
while let Some(v) = connection.recv().await{
|
||||
let Some(proto_id) = v.get(4) else {
|
||||
error!("received too small rmc message.");
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
};
|
||||
|
||||
// protocol 0 is hardcoded to be the no protocol protocol aka keepalive protocol
|
||||
if *proto_id == 0{
|
||||
println!("got keepalive");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (proto_id & 0x80) == 0{
|
||||
let Some(response) = RMCResponse::new(&mut Cursor::new(v)).display_err_or_some() else {
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
};
|
||||
|
||||
info!("got rmc response");
|
||||
|
||||
let mut locked = incoming.lock().await;
|
||||
|
||||
locked.insert(response.get_call_id(), response);
|
||||
notify.notify_waiters();
|
||||
} else {
|
||||
let Some(message) = RMCMessage::new(&mut Cursor::new(v)).display_err_or_some() else {
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
};
|
||||
|
||||
let RMCMessage{
|
||||
protocol_id,
|
||||
method_id,
|
||||
call_id,
|
||||
rest_of_data
|
||||
} = message;
|
||||
|
||||
info!("RMC REQUEST: Proto: {}; Method: {};", protocol_id, method_id);
|
||||
|
||||
remote.rmc_call(&sending_conn, protocol_id, method_id, call_id, rest_of_data).await;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
info!("rmc disconnected")
|
||||
}
|
||||
|
||||
pub fn new_rmc_gateway_connection<T: RmcCallable + Sync + Send + 'static,F>(conn: SplittableBufferConnection, create_internal: F) -> Arc<T>
|
||||
where
|
||||
F: FnOnce(RmcConnection) -> Arc<T>,
|
||||
{
|
||||
let notify = Arc::new(Notify::new());
|
||||
let incoming: Arc<Mutex<HashMap<u32, RMCResponse>>> = Default::default();
|
||||
|
||||
let response_recv = RmcResponseReceiver(notify.clone(), incoming.clone());
|
||||
|
||||
let sending_conn = conn.duplicate_sender();
|
||||
|
||||
let rmc_conn = RmcConnection(sending_conn, response_recv);
|
||||
|
||||
let sending_conn = conn.duplicate_sender();
|
||||
|
||||
let exposed_object = (create_internal)(rmc_conn);
|
||||
|
||||
{
|
||||
let exposed_object = exposed_object.clone();
|
||||
tokio::spawn(async move {
|
||||
handle_incoming(
|
||||
conn,
|
||||
exposed_object,
|
||||
notify,
|
||||
incoming
|
||||
).await;
|
||||
});
|
||||
|
||||
|
||||
tokio::spawn(async move {
|
||||
while sending_conn.is_alive(){
|
||||
sending_conn.send([0,0,0,0,0].to_vec()).await;
|
||||
sleep(Duration::from_secs(10)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exposed_object
|
||||
}
|
||||
|
||||
impl<T: RmcCallable> RmcCallable for Arc<T>{
|
||||
fn rmc_call(&self, responder: &SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>) -> impl Future<Output=()> + Send {
|
||||
self.as_ref().rmc_call(responder, protocol_id, method_id, call_id, rest)
|
||||
}
|
||||
}
|
||||
|
||||
define_rmc_proto! {
|
||||
proto NoProto{}
|
||||
}
|
||||
23
rnex-core/src/rmc/protocols/nat_traversal.rs
Normal file
23
rnex-core/src/rmc/protocols/nat_traversal.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
|
||||
#[rmc_proto(3)]
|
||||
pub trait NatTraversal{
|
||||
#[method_id(2)]
|
||||
async fn request_probe_initiation(&self, station_to_probe: String) -> Result<(),ErrorCode>;
|
||||
|
||||
#[method_id(3)]
|
||||
async fn request_probe_initialization_ext(&self, target_list: Vec<String>, station_to_probe: String) -> Result<(),ErrorCode>;
|
||||
|
||||
#[method_id(4)]
|
||||
async fn report_nat_traversal_result(&self, cid: u32, result: bool, rtt: u32) -> Result<(),ErrorCode>;
|
||||
|
||||
#[method_id(5)]
|
||||
async fn report_nat_properties(&self, nat_mapping: u32, nat_filtering: u32, rtt: u32) -> Result<(),ErrorCode>;
|
||||
}
|
||||
|
||||
#[rmc_proto(3, NoReturn)]
|
||||
pub trait NatTraversalConsole{
|
||||
#[method_id(2)]
|
||||
async fn request_probe_initiation(&self, station_to_probe: String);
|
||||
}
|
||||
24
rnex-core/src/rmc/protocols/notifications.rs
Normal file
24
rnex-core/src/rmc/protocols/notifications.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
use macros::{method_id, rmc_proto, rmc_struct, RmcSerialize};
|
||||
|
||||
pub mod notification_types{
|
||||
pub const OWNERSHIP_CHANGED: u32 = 4000;
|
||||
pub const HOST_CHANGED: u32 = 110000;
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Default, Clone)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct NotificationEvent{
|
||||
pub pid_source: u32,
|
||||
pub notif_type: u32,
|
||||
pub param_1: u32,
|
||||
pub param_2: u32,
|
||||
pub str_param: String,
|
||||
pub param_3: u32,
|
||||
}
|
||||
|
||||
#[rmc_proto(14, NoReturn)]
|
||||
pub trait Notification {
|
||||
#[method_id(1)]
|
||||
async fn process_notification_event(&self, event: NotificationEvent);
|
||||
}
|
||||
|
||||
5
rnex-core/src/rmc/protocols/ranking.rs
Normal file
5
rnex-core/src/rmc/protocols/ranking.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
use macros::rmc_proto;
|
||||
|
||||
#[rmc_proto(112)]
|
||||
pub trait Ranking{
|
||||
}
|
||||
12
rnex-core/src/rmc/protocols/secure.rs
Normal file
12
rnex-core/src/rmc/protocols/secure.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use rnex_core::prudp::station_url::StationUrl;
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
use rnex_core::rmc::structures::qresult::QResult;
|
||||
|
||||
#[rmc_proto(11)]
|
||||
pub trait Secure {
|
||||
#[method_id(1)]
|
||||
async fn register(&self, station_urls: Vec<StationUrl>) -> Result<(QResult, u32, StationUrl), ErrorCode>;
|
||||
#[method_id(7)]
|
||||
async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode>;
|
||||
}
|
||||
497
rnex-core/src/rmc/response.rs
Normal file
497
rnex-core/src/rmc/response.rs
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
// i seriously dont know why the compiler is complaining about unused parentheses in the repr
|
||||
// attributes but this gets it to not complain anymore
|
||||
#![allow(unused_parens)]
|
||||
|
||||
use std::io;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::mem::transmute;
|
||||
use bytemuck::bytes_of;
|
||||
use log::error;
|
||||
use v_byte_helpers::EnumTryInto;
|
||||
use v_byte_helpers::{ReadExtensions, IS_BIG_ENDIAN};
|
||||
use crate::rmc::response::ErrorCode::Core_Exception;
|
||||
use crate::rmc::structures::qresult::ERROR_MASK;
|
||||
use crate::util::SendingBufferConnection;
|
||||
|
||||
pub enum RMCResponseResult {
|
||||
Success {
|
||||
call_id: u32,
|
||||
method_id: u32,
|
||||
data: Vec<u8>,
|
||||
},
|
||||
Error {
|
||||
error_code: ErrorCode,
|
||||
call_id: u32,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct RMCResponse {
|
||||
pub protocol_id: u8,
|
||||
pub response_result: RMCResponseResult,
|
||||
}
|
||||
|
||||
impl RMCResponse {
|
||||
pub fn new(stream: &mut (impl Seek + Read)) -> io::Result<Self>{
|
||||
// ignore the size for now this will only be used for checking
|
||||
let size: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let protocol_id: u8 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
/*let protocol_id: u16 = match protocol_id{
|
||||
0x7F => {
|
||||
stream.read_struct(IS_BIG_ENDIAN)?
|
||||
},
|
||||
_ => protocol_id as u16
|
||||
};*/
|
||||
|
||||
let is_success: u8 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let response_result = if is_success == 0x01{
|
||||
let call_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let method_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let method_id = method_id & (!0x8000);
|
||||
|
||||
let mut data: Vec<u8> = vec![0u8; (size - 2 - 4 - 4) as _];
|
||||
|
||||
stream.read(&mut data)?;
|
||||
|
||||
|
||||
RMCResponseResult::Success {
|
||||
call_id,
|
||||
method_id,
|
||||
data
|
||||
}
|
||||
} else {
|
||||
let error_code: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let error_code = error_code & (!0x80000000);
|
||||
let call_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
RMCResponseResult::Error {
|
||||
error_code: {
|
||||
match ErrorCode::try_from(error_code){
|
||||
Ok(v) => v,
|
||||
Err(()) => {
|
||||
error!("invalid error code {:#010x}", error_code);
|
||||
Core_Exception
|
||||
}
|
||||
}
|
||||
},
|
||||
call_id,
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self{
|
||||
protocol_id,
|
||||
response_result
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_call_id(&self) -> u32{
|
||||
match &self.response_result{
|
||||
RMCResponseResult::Success { call_id, ..} => *call_id,
|
||||
RMCResponseResult::Error { call_id, .. } => *call_id
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_data(self) -> Vec<u8> {
|
||||
generate_response(self.protocol_id, self.response_result).expect("failed to generate response")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Result<Vec<u8>> {
|
||||
let size = 1 + 1 + match &response {
|
||||
RMCResponseResult::Success {
|
||||
data,
|
||||
..
|
||||
} => 4 + 4 + data.len(),
|
||||
RMCResponseResult::Error { .. } => 4 + 4,
|
||||
};
|
||||
|
||||
let mut data_out = Vec::with_capacity(size + 4);
|
||||
|
||||
let u32_size: u32 = size as _;
|
||||
|
||||
data_out.write_all(bytes_of(&u32_size))?;
|
||||
data_out.push(protocol_id);
|
||||
|
||||
match response {
|
||||
RMCResponseResult::Success {
|
||||
call_id,
|
||||
method_id,
|
||||
data
|
||||
} => {
|
||||
data_out.push(1);
|
||||
data_out.write_all(bytes_of(&call_id))?;
|
||||
let ored_method_id = method_id | 0x8000;
|
||||
data_out.write_all(bytes_of(&ored_method_id))?;
|
||||
data_out.write_all(&data)?;
|
||||
}
|
||||
RMCResponseResult::Error {
|
||||
call_id,
|
||||
error_code
|
||||
} => {
|
||||
data_out.push(0);
|
||||
let error_code_val: u32 = error_code.into();
|
||||
let error_code_val = error_code_val | ERROR_MASK;
|
||||
data_out.write_all(bytes_of(&error_code_val))?;
|
||||
data_out.write_all(bytes_of(&call_id))?;
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(data_out.len(), size + 4);
|
||||
|
||||
Ok(data_out)
|
||||
}
|
||||
|
||||
pub async fn send_result(
|
||||
connection: &SendingBufferConnection,
|
||||
result: Result<Vec<u8>, ErrorCode>,
|
||||
protocol_id: u8,
|
||||
method_id: u32,
|
||||
call_id: u32,
|
||||
) {
|
||||
|
||||
let response_result = match result {
|
||||
Ok(v) => RMCResponseResult::Success {
|
||||
call_id,
|
||||
method_id,
|
||||
data: v
|
||||
},
|
||||
Err(e) =>
|
||||
RMCResponseResult::Error {
|
||||
call_id,
|
||||
error_code: e.into()
|
||||
}
|
||||
};
|
||||
|
||||
let response = RMCResponse{
|
||||
response_result,
|
||||
protocol_id
|
||||
};
|
||||
|
||||
send_response(connection, response).await
|
||||
}
|
||||
|
||||
pub async fn send_response(connection: &SendingBufferConnection, rmcresponse: RMCResponse) {
|
||||
connection.send(rmcresponse.to_data()).await;
|
||||
}
|
||||
|
||||
|
||||
//taken from kinnays error list directly
|
||||
#[allow(nonstandard_style)]
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, EnumTryInto, Clone, Copy)]
|
||||
pub enum ErrorCode {
|
||||
Core_Unknown = 0x00010001,
|
||||
Core_NotImplemented = 0x00010002,
|
||||
Core_InvalidPointer = 0x00010003,
|
||||
Core_OperationAborted = 0x00010004,
|
||||
Core_Exception = 0x00010005,
|
||||
Core_AccessDenied = 0x00010006,
|
||||
Core_InvalidHandle = 0x00010007,
|
||||
Core_InvalidIndex = 0x00010008,
|
||||
Core_OutOfMemory = 0x00010009,
|
||||
Core_InvalidArgument = 0x0001000A,
|
||||
Core_Timeout = 0x0001000B,
|
||||
Core_InitializationFailure = 0x0001000C,
|
||||
Core_CallInitiationFailure = 0x0001000D,
|
||||
Core_RegistrationError = 0x0001000E,
|
||||
Core_BufferOverflow = 0x0001000F,
|
||||
Core_InvalidLockState = 0x00010010,
|
||||
Core_InvalidSequence = 0x00010011,
|
||||
Core_SystemError = 0x00010012,
|
||||
Core_Cancelled = 0x00010013,
|
||||
DDL_InvalidSignature = 0x00020001,
|
||||
DDL_IncorrectVersion = 0x00020002,
|
||||
RendezVous_ConnectionFailure = 0x00030001,
|
||||
RendezVous_NotAuthenticated = 0x00030002,
|
||||
RendezVous_InvalidUsername = 0x00030064,
|
||||
RendezVous_InvalidPassword = 0x00030065,
|
||||
RendezVous_UsernameAlreadyExists = 0x00030066,
|
||||
RendezVous_AccountDisabled = 0x00030067,
|
||||
RendezVous_AccountExpired = 0x00030068,
|
||||
RendezVous_ConcurrentLoginDenied = 0x00030069,
|
||||
RendezVous_EncryptionFailure = 0x0003006A,
|
||||
RendezVous_InvalidPID = 0x0003006B,
|
||||
RendezVous_MaxConnectionsReached = 0x0003006C,
|
||||
RendezVous_InvalidGID = 0x0003006D,
|
||||
RendezVous_InvalidControlScriptID = 0x0003006E,
|
||||
RendezVous_InvalidOperationInLiveEnvironment = 0x0003006F,
|
||||
RendezVous_DuplicateEntry = 0x00030070,
|
||||
RendezVous_ControlScriptFailure = 0x00030071,
|
||||
RendezVous_ClassNotFound = 0x00030072,
|
||||
RendezVous_SessionVoid = 0x00030073,
|
||||
RendezVous_DDLMismatch = 0x00030075,
|
||||
RendezVous_InvalidConfiguration = 0x00030076,
|
||||
RendezVous_SessionFull = 0x000300C8,
|
||||
RendezVous_InvalidGatheringPassword = 0x000300C9,
|
||||
RendezVous_WithoutParticipationPeriod = 0x000300CA,
|
||||
RendezVous_PersistentGatheringCreationMax = 0x000300CB,
|
||||
RendezVous_PersistentGatheringParticipationMax = 0x000300CC,
|
||||
RendezVous_DeniedByParticipants = 0x000300CD,
|
||||
RendezVous_ParticipantInBlackList = 0x000300CE,
|
||||
RendezVous_GameServerMaintenance = 0x000300CF,
|
||||
RendezVous_OperationPostpone = 0x000300D0,
|
||||
RendezVous_OutOfRatingRange = 0x000300D1,
|
||||
RendezVous_ConnectionDisconnected = 0x000300D2,
|
||||
RendezVous_InvalidOperation = 0x000300D3,
|
||||
RendezVous_NotParticipatedGathering = 0x000300D4,
|
||||
RendezVous_MatchmakeSessionUserPasswordUnmatch = 0x000300D5,
|
||||
RendezVous_MatchmakeSessionSystemPasswordUnmatch = 0x000300D6,
|
||||
RendezVous_UserIsOffline = 0x000300D7,
|
||||
RendezVous_AlreadyParticipatedGathering = 0x000300D8,
|
||||
RendezVous_PermissionDenied = 0x000300D9,
|
||||
RendezVous_NotFriend = 0x000300DA,
|
||||
RendezVous_SessionClosed = 0x000300DB,
|
||||
RendezVous_DatabaseTemporarilyUnavailable = 0x000300DC,
|
||||
RendezVous_InvalidUniqueId = 0x000300DD,
|
||||
RendezVous_MatchmakingWithdrawn = 0x000300DE,
|
||||
RendezVous_LimitExceeded = 0x000300DF,
|
||||
RendezVous_AccountTemporarilyDisabled = 0x000300E0,
|
||||
RendezVous_PartiallyServiceClosed = 0x000300E1,
|
||||
RendezVous_ConnectionDisconnectedForConcurrentLogin = 0x000300E2,
|
||||
PythonCore_Exception = 0x00040001,
|
||||
PythonCore_TypeError = 0x00040002,
|
||||
PythonCore_IndexError = 0x00040003,
|
||||
PythonCore_InvalidReference = 0x00040004,
|
||||
PythonCore_CallFailure = 0x00040005,
|
||||
PythonCore_MemoryError = 0x00040006,
|
||||
PythonCore_KeyError = 0x00040007,
|
||||
PythonCore_OperationError = 0x00040008,
|
||||
PythonCore_ConversionError = 0x00040009,
|
||||
PythonCore_ValidationError = 0x0004000A,
|
||||
Transport_Unknown = 0x00050001,
|
||||
Transport_ConnectionFailure = 0x00050002,
|
||||
Transport_InvalidUrl = 0x00050003,
|
||||
Transport_InvalidKey = 0x00050004,
|
||||
Transport_InvalidURLType = 0x00050005,
|
||||
Transport_DuplicateEndpoint = 0x00050006,
|
||||
Transport_IOError = 0x00050007,
|
||||
Transport_Timeout = 0x00050008,
|
||||
Transport_ConnectionReset = 0x00050009,
|
||||
Transport_IncorrectRemoteAuthentication = 0x0005000A,
|
||||
Transport_ServerRequestError = 0x0005000B,
|
||||
Transport_DecompressionFailure = 0x0005000C,
|
||||
Transport_ReliableSendBufferFullFatal = 0x0005000D,
|
||||
Transport_UPnPCannotInit = 0x0005000E,
|
||||
Transport_UPnPCannotAddMapping = 0x0005000F,
|
||||
Transport_NatPMPCannotInit = 0x00050010,
|
||||
Transport_NatPMPCannotAddMapping = 0x00050011,
|
||||
Transport_UnsupportedNAT = 0x00050013,
|
||||
Transport_DnsError = 0x00050014,
|
||||
Transport_ProxyError = 0x00050015,
|
||||
Transport_DataRemaining = 0x00050016,
|
||||
Transport_NoBuffer = 0x00050017,
|
||||
Transport_NotFound = 0x00050018,
|
||||
Transport_TemporaryServerError = 0x00050019,
|
||||
Transport_PermanentServerError = 0x0005001A,
|
||||
Transport_ServiceUnavailable = 0x0005001B,
|
||||
Transport_ReliableSendBufferFull = 0x0005001C,
|
||||
Transport_InvalidStation = 0x0005001D,
|
||||
Transport_InvalidSubStreamID = 0x0005001E,
|
||||
Transport_PacketBufferFull = 0x0005001F,
|
||||
Transport_NatTraversalError = 0x00050020,
|
||||
Transport_NatCheckError = 0x00050021,
|
||||
DOCore_StationNotReached = 0x00060001,
|
||||
DOCore_TargetStationDisconnect = 0x00060002,
|
||||
DOCore_LocalStationLeaving = 0x00060003,
|
||||
DOCore_ObjectNotFound = 0x00060004,
|
||||
DOCore_InvalidRole = 0x00060005,
|
||||
DOCore_CallTimeout = 0x00060006,
|
||||
DOCore_RMCDispatchFailed = 0x00060007,
|
||||
DOCore_MigrationInProgress = 0x00060008,
|
||||
DOCore_NoAuthority = 0x00060009,
|
||||
DOCore_NoTargetStationSpecified = 0x0006000A,
|
||||
DOCore_JoinFailed = 0x0006000B,
|
||||
DOCore_JoinDenied = 0x0006000C,
|
||||
DOCore_ConnectivityTestFailed = 0x0006000D,
|
||||
DOCore_Unknown = 0x0006000E,
|
||||
DOCore_UnfreedReferences = 0x0006000F,
|
||||
DOCore_JobTerminationFailed = 0x00060010,
|
||||
DOCore_InvalidState = 0x00060011,
|
||||
DOCore_FaultRecoveryFatal = 0x00060012,
|
||||
DOCore_FaultRecoveryJobProcessFailed = 0x00060013,
|
||||
DOCore_StationInconsitency = 0x00060014,
|
||||
DOCore_AbnormalMasterState = 0x00060015,
|
||||
DOCore_VersionMismatch = 0x00060016,
|
||||
FPD_NotInitialized = 0x00650000,
|
||||
FPD_AlreadyInitialized = 0x00650001,
|
||||
FPD_NotConnected = 0x00650002,
|
||||
FPD_Connected = 0x00650003,
|
||||
FPD_InitializationFailure = 0x00650004,
|
||||
FPD_OutOfMemory = 0x00650005,
|
||||
FPD_RmcFailed = 0x00650006,
|
||||
FPD_InvalidArgument = 0x00650007,
|
||||
FPD_InvalidLocalAccountID = 0x00650008,
|
||||
FPD_InvalidPrincipalID = 0x00650009,
|
||||
FPD_InvalidLocalFriendCode = 0x0065000A,
|
||||
FPD_LocalAccountNotExists = 0x0065000B,
|
||||
FPD_LocalAccountNotLoaded = 0x0065000C,
|
||||
FPD_LocalAccountAlreadyLoaded = 0x0065000D,
|
||||
FPD_FriendAlreadyExists = 0x0065000E,
|
||||
FPD_FriendNotExists = 0x0065000F,
|
||||
FPD_FriendNumMax = 0x00650010,
|
||||
FPD_NotFriend = 0x00650011,
|
||||
FPD_FileIO = 0x00650012,
|
||||
FPD_P2PInternetProhibited = 0x00650013,
|
||||
FPD_Unknown = 0x00650014,
|
||||
FPD_InvalidState = 0x00650015,
|
||||
FPD_AddFriendProhibited = 0x00650017,
|
||||
FPD_InvalidAccount = 0x00650019,
|
||||
FPD_BlacklistedByMe = 0x0065001A,
|
||||
FPD_FriendAlreadyAdded = 0x0065001C,
|
||||
FPD_MyFriendListLimitExceed = 0x0065001D,
|
||||
FPD_RequestLimitExceed = 0x0065001E,
|
||||
FPD_InvalidMessageID = 0x0065001F,
|
||||
FPD_MessageIsNotMine = 0x00650020,
|
||||
FPD_MessageIsNotForMe = 0x00650021,
|
||||
FPD_FriendRequestBlocked = 0x00650022,
|
||||
FPD_NotInMyFriendList = 0x00650023,
|
||||
FPD_FriendListedByMe = 0x00650024,
|
||||
FPD_NotInMyBlacklist = 0x00650025,
|
||||
FPD_IncompatibleAccount = 0x00650026,
|
||||
FPD_BlockSettingChangeNotAllowed = 0x00650027,
|
||||
FPD_SizeLimitExceeded = 0x00650028,
|
||||
FPD_OperationNotAllowed = 0x00650029,
|
||||
FPD_NotNetworkAccount = 0x0065002A,
|
||||
FPD_NotificationNotFound = 0x0065002B,
|
||||
FPD_PreferenceNotInitialized = 0x0065002C,
|
||||
FPD_FriendRequestNotAllowed = 0x0065002D,
|
||||
Ranking_NotInitialized = 0x00670001,
|
||||
Ranking_InvalidArgument = 0x00670002,
|
||||
Ranking_RegistrationError = 0x00670003,
|
||||
Ranking_NotFound = 0x00670005,
|
||||
Ranking_InvalidScore = 0x00670006,
|
||||
Ranking_InvalidDataSize = 0x00670007,
|
||||
Ranking_PermissionDenied = 0x00670009,
|
||||
Ranking_Unknown = 0x0067000A,
|
||||
Ranking_NotImplemented = 0x0067000B,
|
||||
Authentication_NASAuthenticateError = 0x00680001,
|
||||
Authentication_TokenParseError = 0x00680002,
|
||||
Authentication_HttpConnectionError = 0x00680003,
|
||||
Authentication_HttpDNSError = 0x00680004,
|
||||
Authentication_HttpGetProxySetting = 0x00680005,
|
||||
Authentication_TokenExpired = 0x00680006,
|
||||
Authentication_ValidationFailed = 0x00680007,
|
||||
Authentication_InvalidParam = 0x00680008,
|
||||
Authentication_PrincipalIdUnmatched = 0x00680009,
|
||||
Authentication_MoveCountUnmatch = 0x0068000A,
|
||||
Authentication_UnderMaintenance = 0x0068000B,
|
||||
Authentication_UnsupportedVersion = 0x0068000C,
|
||||
Authentication_ServerVersionIsOld = 0x0068000D,
|
||||
Authentication_Unknown = 0x0068000E,
|
||||
Authentication_ClientVersionIsOld = 0x0068000F,
|
||||
Authentication_AccountLibraryError = 0x00680010,
|
||||
Authentication_ServiceNoLongerAvailable = 0x00680011,
|
||||
Authentication_UnknownApplication = 0x00680012,
|
||||
Authentication_ApplicationVersionIsOld = 0x00680013,
|
||||
Authentication_OutOfService = 0x00680014,
|
||||
Authentication_NetworkServiceLicenseRequired = 0x00680015,
|
||||
Authentication_NetworkServiceLicenseSystemError = 0x00680016,
|
||||
Authentication_NetworkServiceLicenseError3 = 0x00680017,
|
||||
Authentication_NetworkServiceLicenseError4 = 0x00680018,
|
||||
DataStore_Unknown = 0x00690001,
|
||||
DataStore_InvalidArgument = 0x00690002,
|
||||
DataStore_PermissionDenied = 0x00690003,
|
||||
DataStore_NotFound = 0x00690004,
|
||||
DataStore_AlreadyLocked = 0x00690005,
|
||||
DataStore_UnderReviewing = 0x00690006,
|
||||
DataStore_Expired = 0x00690007,
|
||||
DataStore_InvalidCheckToken = 0x00690008,
|
||||
DataStore_SystemFileError = 0x00690009,
|
||||
DataStore_OverCapacity = 0x0069000A,
|
||||
DataStore_OperationNotAllowed = 0x0069000B,
|
||||
DataStore_InvalidPassword = 0x0069000C,
|
||||
DataStore_ValueNotEqual = 0x0069000D,
|
||||
ServiceItem_Unknown = 0x006C0001,
|
||||
ServiceItem_InvalidArgument = 0x006C0002,
|
||||
ServiceItem_EShopUnknownHttpError = 0x006C0003,
|
||||
ServiceItem_EShopResponseParseError = 0x006C0004,
|
||||
ServiceItem_NotOwned = 0x006C0005,
|
||||
ServiceItem_InvalidLimitationType = 0x006C0006,
|
||||
ServiceItem_ConsumptionRightShortage = 0x006C0007,
|
||||
MatchmakeReferee_Unknown = 0x006F0001,
|
||||
MatchmakeReferee_InvalidArgument = 0x006F0002,
|
||||
MatchmakeReferee_AlreadyExists = 0x006F0003,
|
||||
MatchmakeReferee_NotParticipatedGathering = 0x006F0004,
|
||||
MatchmakeReferee_NotParticipatedRound = 0x006F0005,
|
||||
MatchmakeReferee_StatsNotFound = 0x006F0006,
|
||||
MatchmakeReferee_RoundNotFound = 0x006F0007,
|
||||
MatchmakeReferee_RoundArbitrated = 0x006F0008,
|
||||
MatchmakeReferee_RoundNotArbitrated = 0x006F0009,
|
||||
Subscriber_Unknown = 0x00700001,
|
||||
Subscriber_InvalidArgument = 0x00700002,
|
||||
Subscriber_OverLimit = 0x00700003,
|
||||
Subscriber_PermissionDenied = 0x00700004,
|
||||
Ranking2_Unknown = 0x00710001,
|
||||
Ranking2_InvalidArgument = 0x00710002,
|
||||
Ranking2_InvalidScore = 0x00710003,
|
||||
SmartDeviceVoiceChat_Unknown = 0x00720001,
|
||||
SmartDeviceVoiceChat_InvalidArgument = 0x00720002,
|
||||
SmartDeviceVoiceChat_InvalidResponse = 0x00720003,
|
||||
SmartDeviceVoiceChat_InvalidAccessToken = 0x00720004,
|
||||
SmartDeviceVoiceChat_Unauthorized = 0x00720005,
|
||||
SmartDeviceVoiceChat_AccessError = 0x00720006,
|
||||
SmartDeviceVoiceChat_UserNotFound = 0x00720007,
|
||||
SmartDeviceVoiceChat_RoomNotFound = 0x00720008,
|
||||
SmartDeviceVoiceChat_RoomNotActivated = 0x00720009,
|
||||
SmartDeviceVoiceChat_ApplicationNotSupported = 0x0072000A,
|
||||
SmartDeviceVoiceChat_InternalServerError = 0x0072000B,
|
||||
SmartDeviceVoiceChat_ServiceUnavailable = 0x0072000C,
|
||||
SmartDeviceVoiceChat_UnexpectedError = 0x0072000D,
|
||||
SmartDeviceVoiceChat_UnderMaintenance = 0x0072000E,
|
||||
SmartDeviceVoiceChat_ServiceNoLongerAvailable = 0x0072000F,
|
||||
SmartDeviceVoiceChat_AccountTemporarilyDisabled = 0x00720010,
|
||||
SmartDeviceVoiceChat_PermissionDenied = 0x00720011,
|
||||
SmartDeviceVoiceChat_NetworkServiceLicenseRequired = 0x00720012,
|
||||
SmartDeviceVoiceChat_AccountLibraryError = 0x00720013,
|
||||
SmartDeviceVoiceChat_GameModeNotFound = 0x00720014,
|
||||
Screening_Unknown = 0x00730001,
|
||||
Screening_InvalidArgument = 0x00730002,
|
||||
Screening_NotFound = 0x00730003,
|
||||
Custom_Unknown = 0x00740001,
|
||||
Ess_Unknown = 0x00750001,
|
||||
Ess_GameSessionError = 0x00750002,
|
||||
Ess_GameSessionMaintenance = 0x00750003,
|
||||
}
|
||||
|
||||
impl Into<u32> for ErrorCode {
|
||||
fn into(self) -> u32 {
|
||||
unsafe { transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use hmac::digest::consts::U5;
|
||||
use hmac::digest::KeyInit;
|
||||
use rc4::{Rc4, StreamCipher};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20];
|
||||
let mut data = data_orig;
|
||||
|
||||
let mut rc4: Rc4<U5> =
|
||||
Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key");
|
||||
|
||||
rc4.apply_keystream(&mut data);
|
||||
|
||||
assert_ne!(data_orig, data);
|
||||
|
||||
let mut rc4: Rc4<U5> =
|
||||
Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key");
|
||||
|
||||
rc4.apply_keystream(&mut data);
|
||||
|
||||
assert_eq!(data_orig, data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enum_equivilance() {
|
||||
let val: u32 = ErrorCode::Core_Unknown.into();
|
||||
assert_eq!(val, 0x00010001)
|
||||
}
|
||||
}
|
||||
42
rnex-core/src/rmc/structures/any.rs
Normal file
42
rnex-core/src/rmc/structures/any.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
use std::io::{Read, Write};
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use super::{Result, RmcSerialize};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Any{
|
||||
pub name: String,
|
||||
pub data: Vec<u8>
|
||||
}
|
||||
|
||||
impl RmcSerialize for Any{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
self.name.serialize(writer)?;
|
||||
|
||||
let u32_len = self.data.len() as u32;
|
||||
|
||||
u32_len.serialize(writer)?;
|
||||
u32_len.serialize(writer)?;
|
||||
|
||||
self.data.serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||
let name = String::deserialize(reader)?;
|
||||
|
||||
// also length ?
|
||||
let _len2: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
let length: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut data = vec![0; length as usize];
|
||||
|
||||
reader.read_exact(&mut data)?;
|
||||
|
||||
Ok(
|
||||
Any{
|
||||
name,
|
||||
data
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
29
rnex-core/src/rmc/structures/buffer.rs
Normal file
29
rnex-core/src/rmc/structures/buffer.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use std::io::{Read, Write};
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
|
||||
|
||||
impl<'a> RmcSerialize for &'a [u8]{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
let u32_size = self.len() as u32;
|
||||
writer.write(bytemuck::bytes_of(&u32_size))?;
|
||||
writer.write(self)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// DO NOT USE (also maybe split off the serialize and deserialize functions at some point)
|
||||
fn deserialize(_reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
panic!("cannot deserialize to a u8 slice reference (use this ONLY for writing)")
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for Box<[u8]>{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
(&self[..]).serialize(writer)
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Vec::deserialize(reader).map(|v| v.into_boxed_slice())
|
||||
}
|
||||
}
|
||||
13
rnex-core/src/rmc/structures/connection_data.rs
Normal file
13
rnex-core/src/rmc/structures/connection_data.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
use macros::RmcSerialize;
|
||||
use rnex_core::kerberos::KerberosDateTime;
|
||||
|
||||
#[derive(Debug, RmcSerialize)]
|
||||
#[rmc_struct(1)]
|
||||
pub struct ConnectionData{
|
||||
pub station_url: String,
|
||||
pub special_protocols: Vec<u8>,
|
||||
pub special_station_url: String,
|
||||
pub date_time: KerberosDateTime
|
||||
}
|
||||
|
||||
60
rnex-core/src/rmc/structures/list.rs
Normal file
60
rnex-core/src/rmc/structures/list.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
use std::array::from_fn;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem::MaybeUninit;
|
||||
use bytemuck::bytes_of;
|
||||
use serde::Serialize;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
|
||||
|
||||
// this is also for implementing `Buffer` this is tecnically not the same as its handled internaly
|
||||
// probably but as it has the same mapping it doesn't matter and simplifies things
|
||||
impl<T: RmcSerialize> RmcSerialize for Vec<T>{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
let u32_len = self.len() as u32;
|
||||
|
||||
writer.write_all(bytes_of(&u32_len))?;
|
||||
for e in self{
|
||||
e.serialize(writer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let len: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut vec = Vec::with_capacity(len as usize);
|
||||
|
||||
for _ in 0..len{
|
||||
vec.push(T::deserialize(reader)?);
|
||||
}
|
||||
|
||||
Ok(vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEN: usize, T: RmcSerialize> RmcSerialize for [T; LEN]{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
for i in 0..LEN{
|
||||
self[i].serialize(writer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let mut arr = [const { MaybeUninit::<T>::uninit() }; LEN];
|
||||
|
||||
for i in 0..LEN{
|
||||
arr[i] = MaybeUninit::new(T::deserialize(reader)?);
|
||||
}
|
||||
|
||||
// all of the elements are now initialized so it is safe to assume they are initialized
|
||||
|
||||
let arr = arr.map(|v| unsafe{ v.assume_init() });
|
||||
|
||||
Ok(arr)
|
||||
}
|
||||
}
|
||||
126
rnex-core/src/rmc/structures/matchmake.rs
Normal file
126
rnex-core/src/rmc/structures/matchmake.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
use rnex_core::kerberos::KerberosDateTime;
|
||||
use rnex_core::rmc::structures::variant::Variant;
|
||||
use macros::RmcSerialize;
|
||||
|
||||
// rmc structure
|
||||
#[derive(RmcSerialize, Debug, Clone, Default)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct Gathering {
|
||||
pub self_gid: u32,
|
||||
pub owner_pid: u32,
|
||||
pub host_pid: u32,
|
||||
pub minimum_participants: u16,
|
||||
pub maximum_participants: u16,
|
||||
pub participant_policy: u32,
|
||||
pub policy_argument: u32,
|
||||
pub flags: u32,
|
||||
pub state: u32,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
// rmc structure
|
||||
#[derive(RmcSerialize, Debug, Clone, Default)]
|
||||
#[rmc_struct(0)]
|
||||
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,
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Clone)]
|
||||
#[rmc_struct(3)]
|
||||
pub struct MatchmakeSessionSearchCriteria {
|
||||
pub attribs: Vec<String>,
|
||||
pub game_mode: String,
|
||||
pub minimum_participants: String,
|
||||
pub maximum_participants: String,
|
||||
pub matchmake_system_type: String,
|
||||
pub vacant_only: bool,
|
||||
pub exclude_locked: bool,
|
||||
pub exclude_non_host_pid: bool,
|
||||
pub selection_method: u32,
|
||||
pub vacant_participants: u16,
|
||||
pub matchmake_param: MatchmakeParam,
|
||||
pub exclude_user_password_set: bool,
|
||||
pub exclude_system_password_set: bool,
|
||||
pub refer_gid: u32,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Clone)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct AutoMatchmakeParam {
|
||||
pub matchmake_session: MatchmakeSession,
|
||||
pub additional_participants: Vec<u32>,
|
||||
pub gid_for_participation_check: u32,
|
||||
pub auto_matchmake_option: u32,
|
||||
pub join_message: String,
|
||||
pub participation_count: u16,
|
||||
pub search_criteria: Vec<MatchmakeSessionSearchCriteria>,
|
||||
pub target_gids: Vec<u32>,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Clone)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct CreateMatchmakeSessionParam {
|
||||
pub matchmake_session: MatchmakeSession,
|
||||
pub additional_participants: Vec<u32>,
|
||||
pub gid_for_participation_check: u32,
|
||||
pub create_matchmake_session_option: u32,
|
||||
pub join_message: String,
|
||||
pub participation_count: u16,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Clone)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct MatchmakeBlockListParam {
|
||||
option_flag: u32,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Clone)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct JoinMatchmakeSessionParam {
|
||||
pub gid: u32,
|
||||
pub additional_participants: Vec<u32>,
|
||||
pub gid_for_participation_check: u32,
|
||||
pub join_matchmake_session_open: u32,
|
||||
pub join_matchmake_session_behavior: u8,
|
||||
pub user_password: String,
|
||||
pub system_password: String,
|
||||
pub join_message: String,
|
||||
pub participation_count: u16,
|
||||
//pub extra_participant: u16,
|
||||
//pub block_list_param: MatchmakeBlockListParam
|
||||
}
|
||||
|
||||
pub mod gathering_flags {
|
||||
pub const PERSISTENT_GATHERING: u32 = 0x1;
|
||||
pub const DISCONNECT_CHANGE_OWNER: u32 = 0x10;
|
||||
pub const PERSISTENT_GATHERING_LEAVE_PARTICIPATION: u32 = 0x40;
|
||||
pub const PERSISTENT_GATHERING_ALLOW_ZERO_USERS: u32 = 0x80;
|
||||
pub const PARTICIPANTS_CHANGE_OWNER: u32 = 0x200;
|
||||
pub const VERBOSE_PARTICIPANTS: u32 = 0x400;
|
||||
pub const VERBOSE_PARTICIPANTS_EX: u32 = 0x800;
|
||||
}
|
||||
60
rnex-core/src/rmc/structures/mod.rs
Normal file
60
rnex-core/src/rmc/structures/mod.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::string::FromUtf8Error;
|
||||
use thiserror::Error;
|
||||
|
||||
//ideas for the future: make a proc macro library which allows generation of struct reads
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error{
|
||||
#[error("Io Error: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("UTF8 conversion Error: {0}")]
|
||||
Utf8(#[from] FromUtf8Error),
|
||||
#[error("unexpected value: {0}")]
|
||||
UnexpectedValue(u64),
|
||||
#[error("version mismatch: {0}")]
|
||||
VersionMismatch(u8),
|
||||
#[error("an error occurred reading the station url")]
|
||||
StationUrlInvalid
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub mod string;
|
||||
pub mod any;
|
||||
pub mod qresult;
|
||||
pub mod buffer;
|
||||
pub mod connection_data;
|
||||
pub mod rmc_struct;
|
||||
pub mod list;
|
||||
pub mod qbuffer;
|
||||
pub mod primitives;
|
||||
pub mod matchmake;
|
||||
pub mod variant;
|
||||
pub mod ranking;
|
||||
mod networking;
|
||||
|
||||
pub trait RmcSerialize{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()>;
|
||||
fn deserialize(reader: &mut dyn Read) -> Result<Self> where Self: Sized;
|
||||
|
||||
fn to_data(&self) -> Vec<u8>{
|
||||
let mut data = Vec::new();
|
||||
|
||||
self.serialize(&mut data).expect("out of memory or something");
|
||||
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for (){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn deserialize(reader: &mut dyn Read) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
33
rnex-core/src/rmc/structures/networking.rs
Normal file
33
rnex-core/src/rmc/structures/networking.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use std::io::{Read, Write};
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use rnex_core::prudp::virtual_port::VirtualPort;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
impl RmcSerialize for SocketAddrV4{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.ip().to_bits().serialize(writer)?;
|
||||
self.port().serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let ip = u32::deserialize(reader)?;
|
||||
let port = u16::deserialize(reader)?;
|
||||
|
||||
Ok(SocketAddrV4::new(Ipv4Addr::from_bits(ip), port))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl RmcSerialize for VirtualPort{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(Self(u8::deserialize(reader)?))
|
||||
}
|
||||
}
|
||||
241
rnex-core/src/rmc/structures/primitives.rs
Normal file
241
rnex-core/src/rmc/structures/primitives.rs
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
use std::io::{Read, Write};
|
||||
use bytemuck::bytes_of;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
impl RmcSerialize for u8{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for i8{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for u16{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for i16{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for u32{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for i32{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for u64{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for i64{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for f64{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for bool{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
match self{
|
||||
true => writer.write_all(&[1])?,
|
||||
false => writer.write_all(&[0])?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(u8::deserialize(reader)? != 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize> RmcSerialize for (T, U){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
|
||||
Ok((first, second))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize> RmcSerialize for (T, U, V){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
self.2.serialize(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
let third = V::deserialize(reader)?;
|
||||
|
||||
Ok((first, second, third))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize> RmcSerialize for (T, U, V, W){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
self.2.serialize(writer)?;
|
||||
self.3.serialize(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
let third = V::deserialize(reader)?;
|
||||
let fourth = W::deserialize(reader)?;
|
||||
|
||||
Ok((first, second, third, fourth))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize, X: RmcSerialize> RmcSerialize for (T, U, V, W, X){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
self.2.serialize(writer)?;
|
||||
self.3.serialize(writer)?;
|
||||
self.4.serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
let third = V::deserialize(reader)?;
|
||||
let fourth = W::deserialize(reader)?;
|
||||
let fifth = X::deserialize(reader)?;
|
||||
|
||||
Ok((first, second, third, fourth, fifth))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize, X: RmcSerialize, Y: RmcSerialize> RmcSerialize for (T, U, V, W, X, Y){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
self.2.serialize(writer)?;
|
||||
self.3.serialize(writer)?;
|
||||
self.4.serialize(writer)?;
|
||||
self.5.serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
let third = V::deserialize(reader)?;
|
||||
let fourth = W::deserialize(reader)?;
|
||||
let fifth = X::deserialize(reader)?;
|
||||
let sixth = Y::deserialize(reader)?;
|
||||
|
||||
Ok((first, second, third, fourth, fifth, sixth))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize, X: RmcSerialize, Y: RmcSerialize, Z: RmcSerialize> RmcSerialize for (T, U, V, W, X, Y, Z){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
self.2.serialize(writer)?;
|
||||
self.3.serialize(writer)?;
|
||||
self.4.serialize(writer)?;
|
||||
self.5.serialize(writer)?;
|
||||
self.6.serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
let third = V::deserialize(reader)?;
|
||||
let fourth = W::deserialize(reader)?;
|
||||
let fifth = X::deserialize(reader)?;
|
||||
let sixth = Y::deserialize(reader)?;
|
||||
let seventh = Z::deserialize(reader)?;
|
||||
|
||||
Ok((first, second, third, fourth, fifth, sixth, seventh))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize> RmcSerialize for Box<T>{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.as_ref().serialize(writer)
|
||||
}
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
T::deserialize(reader).map(Box::new)
|
||||
}
|
||||
}
|
||||
29
rnex-core/src/rmc/structures/qbuffer.rs
Normal file
29
rnex-core/src/rmc/structures/qbuffer.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use std::io::{Read, Write};
|
||||
use bytemuck::bytes_of;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::structures::{Result, RmcSerialize};
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QBuffer(pub Vec<u8>);
|
||||
|
||||
impl RmcSerialize for QBuffer{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
let len_u16 = self.0.len() as u16;
|
||||
|
||||
writer.write(bytes_of(&len_u16))?;
|
||||
writer.write(&self.0)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||
let size: u16 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut vec = vec![0; size as usize];
|
||||
|
||||
reader.read_exact(&mut vec)?;
|
||||
|
||||
Ok(Self(vec))
|
||||
}
|
||||
}
|
||||
37
rnex-core/src/rmc/structures/qresult.rs
Normal file
37
rnex-core/src/rmc/structures/qresult.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use std::io::{Read, Write};
|
||||
use bytemuck::{bytes_of, Pod, Zeroable};
|
||||
use v_byte_helpers::SwapEndian;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
use crate::rmc::structures::{RmcSerialize, Result};
|
||||
|
||||
pub const ERROR_MASK: u32 = 1 << 31;
|
||||
|
||||
#[derive(Pod, Zeroable, Copy, Clone, SwapEndian, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct QResult(u32);
|
||||
|
||||
impl QResult{
|
||||
pub fn success(error_code: ErrorCode) -> Self{
|
||||
let val: u32 = error_code.into();
|
||||
|
||||
Self(val & (!ERROR_MASK))
|
||||
}
|
||||
|
||||
pub fn error(error_code: ErrorCode) -> Self{
|
||||
let val: u32 = error_code.into();
|
||||
|
||||
Self(val | ERROR_MASK)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for QResult{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
writer.write(bytes_of(self))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
69
rnex-core/src/rmc/structures/ranking.rs
Normal file
69
rnex-core/src/rmc/structures/ranking.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
use macros::RmcSerialize;
|
||||
use rnex_core::rmc::structures::qbuffer::QBuffer;
|
||||
|
||||
#[derive(RmcSerialize, Debug)]
|
||||
#[rmc_struct(0)]
|
||||
struct UploadCompetitionData{
|
||||
winning_team/*?*/: u32,
|
||||
splatfest_id/*?*/: u32,
|
||||
unk_2/*?*/: u32,
|
||||
unk_3: u32,
|
||||
team_id_1: u8,
|
||||
team_id_2: u8,
|
||||
unk_5: u32,
|
||||
player_data/*?*/: QBuffer,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
struct UserData{
|
||||
name: [u16; 0x10],
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use std::io::Cursor;
|
||||
use bytemuck::from_bytes;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use crate::rmc::structures::ranking::{UploadCompetitionData, UserData};
|
||||
use rnex_core::rmc::structures::RmcSerialize;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let data: [u8; 0xBD] = [
|
||||
0x00, 0xB8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00,
|
||||
0x00, 0x1F, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x49, 0x00,
|
||||
0x7A, 0x00, 0x7A, 0x00, 0x79, 0x00, 0x53, 0x00, 0x50, 0x00, 0x46, 0x00, 0x4E, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0xF2, 0x00, 0x00, 0x00,
|
||||
0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1F, 0x5E, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x90, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0A, 0x00, 0x00, 0x14, 0x87, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
let mut cursor = Cursor::new(data);
|
||||
|
||||
let data = UploadCompetitionData::deserialize(&mut cursor).expect("unable to deserialize data");
|
||||
|
||||
let user_data: &UserData = from_bytes(&data.player_data.0[..size_of::<UserData>()]);
|
||||
|
||||
let pos = user_data.name.iter()
|
||||
.position(|v| *v == 0x0000)
|
||||
.unwrap_or(0x10);
|
||||
|
||||
let mut name = user_data.name[0..pos].to_vec();
|
||||
|
||||
name.iter_mut().for_each(|v| *v = v.swap_bytes());
|
||||
|
||||
let name = String::from_utf16(&name).expect("unable to get name");
|
||||
|
||||
println!("{:?}", name);
|
||||
|
||||
assert!(u8::deserialize(&mut cursor).is_err())
|
||||
}
|
||||
}
|
||||
44
rnex-core/src/rmc/structures/rmc_struct.rs
Normal file
44
rnex-core/src/rmc/structures/rmc_struct.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use std::io::{Cursor, Read, Write};
|
||||
use bytemuck::bytes_of;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::structures::Error::VersionMismatch;
|
||||
use crate::rmc::structures::Result;
|
||||
|
||||
#[repr(C, packed)]
|
||||
struct StructureHeader{
|
||||
version: u8,
|
||||
length: u32
|
||||
}
|
||||
|
||||
pub fn write_struct(writer: &mut dyn Write, version: u8, pred: impl FnOnce(&mut Vec<u8>) -> Result<()> ) -> Result<()> {
|
||||
writer.write_all(&[version])?;
|
||||
|
||||
let mut scratch_space: Vec<u8> = Vec::new();
|
||||
|
||||
(pred)(&mut scratch_space)?;
|
||||
|
||||
let u32_size = scratch_space.len() as u32;
|
||||
|
||||
writer.write_all(bytes_of(&u32_size))?;
|
||||
writer.write_all(&scratch_space)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_struct<T: Sized>(mut reader: &mut dyn Read, version: u8, pred: impl FnOnce(&mut Cursor<Vec<u8>>) -> Result<T>) -> Result<T> {
|
||||
let ver: u8 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
if ver != version{
|
||||
return Err(VersionMismatch(ver));
|
||||
}
|
||||
|
||||
let size: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut vec = vec![0u8; size as usize];
|
||||
|
||||
reader.read_exact(&mut vec)?;
|
||||
|
||||
let mut cursor = Cursor::new(vec);
|
||||
|
||||
Ok(pred(&mut cursor)?)
|
||||
}
|
||||
39
rnex-core/src/rmc/structures/string.rs
Normal file
39
rnex-core/src/rmc/structures/string.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use std::io::{Read, Write};
|
||||
use bytemuck::bytes_of;
|
||||
use log::error;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use super::{Result, RmcSerialize};
|
||||
|
||||
impl RmcSerialize for String{
|
||||
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||
let len: u16 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
let mut data = vec![0; len as usize - 1];
|
||||
reader.read_exact(&mut data)?;
|
||||
|
||||
let null: u8 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
if null != 0{
|
||||
error!("unable to find null terminator... continuing anyways");
|
||||
}
|
||||
|
||||
Ok(String::from_utf8(data)?)
|
||||
}
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
(&self[..]).serialize(writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for &str{
|
||||
fn deserialize(_reader: &mut dyn Read) -> Result<Self> {
|
||||
panic!("cannot serialize to &str")
|
||||
}
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
let u16_len: u16 = (self.len() + 1) as u16;
|
||||
writer.write(bytes_of(&u16_len))?;
|
||||
|
||||
writer.write(self.as_bytes())?;
|
||||
writer.write(&[0])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
65
rnex-core/src/rmc/structures/variant.rs
Normal file
65
rnex-core/src/rmc/structures/variant.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use std::io::{Read, Write};
|
||||
use crate::kerberos::KerberosDateTime;
|
||||
use crate::rmc::structures;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub enum Variant{
|
||||
#[default]
|
||||
None,
|
||||
SInt64(i64),
|
||||
Double(f64),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
DateTime(KerberosDateTime),
|
||||
UInt64(u64),
|
||||
}
|
||||
|
||||
impl RmcSerialize for Variant{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
match self{
|
||||
Variant::None => {
|
||||
writer.write_all(&[0])?;
|
||||
}
|
||||
Variant::SInt64(v) => {
|
||||
writer.write_all(&[1])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
Variant::Double(v) => {
|
||||
writer.write_all(&[2])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
Variant::Bool(v) => {
|
||||
writer.write_all(&[3])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
Variant::String(v) => {
|
||||
writer.write_all(&[4])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
Variant::DateTime(v) => {
|
||||
writer.write_all(&[5])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
Variant::UInt64(v) => {
|
||||
writer.write_all(&[6])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
match u8::deserialize(reader)?{
|
||||
0 => Ok(Variant::None),
|
||||
1 => Ok(Variant::SInt64(i64::deserialize(reader)?)),
|
||||
2 => Ok(Variant::Double(f64::deserialize(reader)?)),
|
||||
3 => Ok(Variant::Bool(bool::deserialize(reader)?)),
|
||||
4 => Ok(Variant::String(String::deserialize(reader)?)),
|
||||
5 => Ok(Variant::DateTime(KerberosDateTime::deserialize(reader)?)),
|
||||
6 => Ok(Variant::UInt64(u64::deserialize(reader)?)),
|
||||
v => Err(structures::Error::UnexpectedValue(v as u64))
|
||||
}
|
||||
}
|
||||
}
|
||||
11
rnex-core/src/rnex_proxy_common.rs
Normal file
11
rnex-core/src/rnex_proxy_common.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use macros::RmcSerialize;
|
||||
use crate::prudp::socket_addr::PRUDPSockAddr;
|
||||
|
||||
#[derive(Debug, RmcSerialize)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct ConnectionInitData{
|
||||
pub prudpsock_addr: PRUDPSockAddr,
|
||||
pub pid: u32,
|
||||
|
||||
}
|
||||
|
||||
115
rnex-core/src/util.rs
Normal file
115
rnex-core/src/util.rs
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use log::{error, info};
|
||||
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
|
||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||
use tokio::sync::Notify;
|
||||
use tokio::task;
|
||||
use crate::reggie::{UnitPacketRead, UnitPacketWrite};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SendingBufferConnection(Sender<Vec<u8>>, Arc<Notify>);
|
||||
|
||||
pub struct SplittableBufferConnection(SendingBufferConnection, Receiver<Vec<u8>>);
|
||||
|
||||
impl AsRef<SendingBufferConnection> for SplittableBufferConnection{
|
||||
fn as_ref(&self) -> &SendingBufferConnection {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SplittableBufferConnection{
|
||||
type Target = SendingBufferConnection;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl<T: Send + Unpin + AsyncWrite + AsyncRead + 'static> From<T> for SplittableBufferConnection{
|
||||
fn from(value: T) -> Self {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl SplittableBufferConnection {
|
||||
fn new<T: Send + Unpin + AsyncWrite + AsyncRead + 'static>(stream: T) -> Self {
|
||||
let (outside_send, inside_recv) = channel::<Vec<u8>>(10);
|
||||
let (inside_send, outside_recv) = channel::<Vec<u8>>(10);
|
||||
|
||||
let notify = Arc::new(Notify::new());
|
||||
|
||||
{
|
||||
let notify = notify.clone();
|
||||
task::spawn(async move {
|
||||
let sender = inside_send;
|
||||
let mut recver = inside_recv;
|
||||
let mut stream = stream;
|
||||
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
data = recver.recv() => {
|
||||
let Some(data) = data else {
|
||||
break;
|
||||
};
|
||||
|
||||
if let Err(e) = stream.send_buffer(&data[..]).await{
|
||||
error!("error sending data to backend: {}", e);
|
||||
break;
|
||||
}
|
||||
},
|
||||
data = stream.read_buffer() => {
|
||||
let data = match data{
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
error!("error reveiving data from backend: {}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = sender.send(data).await{
|
||||
error!("a send error occurred {}", e);
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ = notify.notified() => {
|
||||
info!("shutting down connection");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stream.shutdown().await;
|
||||
});
|
||||
}
|
||||
|
||||
Self(SendingBufferConnection(outside_send, notify), outside_recv)
|
||||
}
|
||||
}
|
||||
|
||||
impl SendingBufferConnection{
|
||||
pub async fn send(&self, buffer: Vec<u8>) -> Option<()>{
|
||||
self.0.send(buffer).await.ok()
|
||||
}
|
||||
pub fn is_alive(&self) -> bool{
|
||||
!self.0.is_closed()
|
||||
}
|
||||
pub async fn disconnect(&self) {
|
||||
while !self.0.is_closed() {
|
||||
self.1.notify_waiters();
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SplittableBufferConnection{
|
||||
pub async fn recv(&mut self) -> Option<Vec<u8>>{
|
||||
self.1.recv().await
|
||||
}
|
||||
|
||||
pub fn duplicate_sender(&self) -> SendingBufferConnection{
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
97
rnex-core/src/versions.rs
Normal file
97
rnex-core/src/versions.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::ops::{BitAnd, BitOr};
|
||||
use typenum::{Cmp, IsEqual, IsLess, IsLessOrEqual, Unsigned};
|
||||
|
||||
/// This trait represents a version at compile time
|
||||
trait Version{
|
||||
type Major: Unsigned;
|
||||
type Minor: Unsigned;
|
||||
}
|
||||
|
||||
/// This struct contains nothing and is used to represent specific versions as an instance of
|
||||
/// [`Version`]. It is instances as `Ver<Major, Minor>`
|
||||
struct Ver<MAJ: Unsigned, MIN: Unsigned>{
|
||||
_phantom: PhantomData<(MAJ, MIN)>
|
||||
}
|
||||
|
||||
impl<MAJ: Unsigned, MIN: Unsigned> Version for Ver<MAJ, MIN>{
|
||||
type Major = MAJ;
|
||||
type Minor = MIN;
|
||||
}
|
||||
|
||||
/// Represents two versions which can be compared
|
||||
trait ComparableVersion<T: Version>: Version{
|
||||
type IsAtLeast: SameOrUnit;
|
||||
}
|
||||
|
||||
impl<T: Version, U: Version> ComparableVersion<T> for U where
|
||||
<T as Version>::Major: Cmp<Self::Major>,
|
||||
<T as Version>::Minor: IsLessOrEqual<Self::Minor>,
|
||||
<T as Version>::Major: IsEqual<
|
||||
Self::Major,
|
||||
Output: BitAnd<
|
||||
typenum::LeEq<T::Minor, Self::Minor>
|
||||
>,
|
||||
>,
|
||||
<T as Version>::Major: IsLess<
|
||||
Self::Major,
|
||||
Output: BitOr<
|
||||
typenum::And<
|
||||
typenum::Eq<T::Major, Self::Major>,
|
||||
typenum::LeEq<T::Minor, Self::Minor>,
|
||||
>,
|
||||
Output: SameOrUnit
|
||||
>
|
||||
> {
|
||||
|
||||
type IsAtLeast = typenum::Or<
|
||||
typenum::Le<T::Major, Self::Major>,
|
||||
typenum::And<
|
||||
typenum::Eq<T::Major, Self::Major>,
|
||||
typenum::LeEq<T::Minor, Self::Minor>,
|
||||
>
|
||||
>;
|
||||
}
|
||||
|
||||
|
||||
/// Simple check for testing if the `TEST` version is at least `REQ` or higher.
|
||||
type VersionAbove<REQ, TEST> = <TEST as ComparableVersion<REQ>>::IsAtLeast;
|
||||
|
||||
trait VersionIsAtLeast<VER: Version>{}
|
||||
|
||||
impl<VER: Version, T: ComparableVersion<VER, IsAtLeast = typenum::True>> VersionIsAtLeast<VER> for T{}
|
||||
|
||||
|
||||
/// Trait for containing the result of elements which only conditionally exist
|
||||
trait CondElemResult{
|
||||
type Output;
|
||||
}
|
||||
|
||||
/// Empty helper struct which only servers to give a concrete type when creating fields in rmc
|
||||
/// structs which have a version requirement. This is not meant to be used directly, use
|
||||
/// [`MinVersion`] instead.
|
||||
struct MinVersionElementHelper<T, REQUIRED: Version, VER: Version + ComparableVersion<REQUIRED>>{
|
||||
_phantom: PhantomData<(T, REQUIRED, VER)>
|
||||
}
|
||||
|
||||
/// This should be used either with [`typenum::True`] or [`typenum::False`]. When `True` the [`Self::Output`]
|
||||
/// will be the same as the `T` you put into Output. When `False` it will always be `()`
|
||||
trait SameOrUnit{
|
||||
type Output<T>;
|
||||
}
|
||||
|
||||
impl SameOrUnit for typenum::True{
|
||||
type Output<T> = T;
|
||||
}
|
||||
|
||||
impl SameOrUnit for typenum::False{
|
||||
type Output<T> = ();
|
||||
}
|
||||
|
||||
impl<T, REQUIRED: Version, VER: Version + ComparableVersion<REQUIRED>> CondElemResult for MinVersionElementHelper<T, REQUIRED, VER> where {
|
||||
type Output = <<VER as ComparableVersion<REQUIRED>>::IsAtLeast as SameOrUnit>::Output<T>;
|
||||
}
|
||||
|
||||
/// When the version condition is met the field will exist and will simply be `T` if not it will be
|
||||
/// replaced by `()`. Use this when you need to add versioning to rmc structs.
|
||||
type MinVersion<T, REQUIRED, VER> = <MinVersionElementHelper<T, REQUIRED, VER> as CondElemResult>::Output;
|
||||
92
rnex-core/src/web/mod.rs
Normal file
92
rnex-core/src/web/mod.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use rocket::{get, routes, Request, State};
|
||||
use rocket::request::{FromRequest, Outcome};
|
||||
use rocket::serde::json::Json;
|
||||
use tokio::task::JoinHandle;
|
||||
use crate::nex::matchmake::MatchmakeManager;
|
||||
use crate::rmc::protocols::notifications::NotificationEvent;
|
||||
|
||||
struct RnexApiAuth;
|
||||
|
||||
#[async_trait]
|
||||
impl<'r> FromRequest<'r> for RnexApiAuth{
|
||||
|
||||
type Error = ();
|
||||
async fn from_request<'a>(request: &'r Request<'a>) -> Outcome<Self, Self::Error> {
|
||||
Outcome::Success(RnexApiAuth)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[get("/gatherings")]
|
||||
async fn gatherings(mmm: &State<Arc<MatchmakeManager>>) -> Json<Vec<u32>>{
|
||||
let matches = mmm.sessions.read().await;
|
||||
|
||||
Json(matches.keys().map(|v| *v).collect())
|
||||
}
|
||||
|
||||
#[get("/gathering/<gid>/players")]
|
||||
async fn players_in_match(mmm: &State<Arc<MatchmakeManager>>, gid: u32) -> Option<Json<Vec<u32>>>{
|
||||
let mmm = mmm.sessions.read().await;
|
||||
|
||||
let gathering = mmm.get(&gid)?;
|
||||
|
||||
let gathering = gathering.clone();
|
||||
|
||||
drop(mmm);
|
||||
|
||||
let gathering = gathering.lock().await;
|
||||
|
||||
Some(Json(gathering.connected_players.iter().filter_map(|p| p.upgrade()).map(|p| p.pid).collect()))
|
||||
}
|
||||
/*
|
||||
#[get("/player/<pid>/disconnect")]
|
||||
async fn disconnect_player(_auth: RnexApiAuth, mmm: &State<Arc<MatchmakeManager>>, pid: u32) -> Option<()>{
|
||||
// this doesnt work and is broken, there might be some other way to remotely close gatherings...
|
||||
// also if anyone gets this working change it to POST cause the only reason its get is because
|
||||
// that makes testing it easier
|
||||
let mmm = mmm.users.read().await;
|
||||
|
||||
for player in mmm.values().filter_map(|p| p.upgrade()).filter(|p| p.pid == pid) {
|
||||
player.remote.get_connection().0.close_connection().await;
|
||||
}
|
||||
|
||||
|
||||
Some(())
|
||||
}*/
|
||||
|
||||
#[get("/gathering/<gid>/close")]
|
||||
async fn close_gathering(_auth: RnexApiAuth, mmm: &State<Arc<MatchmakeManager>>, gid: u32) -> Option<()>{
|
||||
// this doesnt work and is broken, there might be some other way to remotely close gatherings...
|
||||
// also if anyone gets this working change it to POST cause the only reason its get is because
|
||||
// that makes testing it easier
|
||||
let mmm = mmm.sessions.read().await;
|
||||
|
||||
let gathering = mmm.get(&gid)?;
|
||||
|
||||
let gathering = gathering.clone();
|
||||
|
||||
drop(mmm);
|
||||
|
||||
let gathering = gathering.lock().await;
|
||||
|
||||
gathering.broadcast_notification(&NotificationEvent{
|
||||
pid_source: gathering.session.gathering.owner_pid,
|
||||
notif_type: 109000,
|
||||
param_1: gathering.session.gathering.self_gid,
|
||||
..Default::default()
|
||||
}).await;
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub async fn start_web(mgr: Arc<MatchmakeManager>) -> JoinHandle<()> {
|
||||
tokio::spawn(async move {
|
||||
rocket::build()
|
||||
.mount("/", routes![gatherings, players_in_match, close_gathering])
|
||||
.manage(mgr)
|
||||
.launch().await
|
||||
.expect("unable to start webserver");
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue