feat: stuff happend
This commit is contained in:
parent
b8d2cd7b09
commit
fa37331780
11 changed files with 511 additions and 343 deletions
|
|
@ -1,3 +1,7 @@
|
|||
//! 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)>;
|
||||
|
|
|
|||
28
src/main.rs
28
src/main.rs
|
|
@ -1,24 +1,24 @@
|
|||
#![allow(dead_code)]
|
||||
#![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.
|
||||
|
||||
use std::{env, fs};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use std::sync::Arc;
|
||||
use chrono::{Local, SecondsFormat};
|
||||
use log::info;
|
||||
use once_cell::sync::Lazy;
|
||||
use rc4::{KeyInit, Rc4, StreamCipher};
|
||||
use rc4::consts::U5;
|
||||
use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, TermLogger, WriteLogger};
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::task::JoinHandle;
|
||||
use crate::nex::account::Account;
|
||||
use crate::prudp::socket::{EncryptionPair, Unsecure};
|
||||
use crate::prudp::socket::Unsecure;
|
||||
use crate::prudp::packet::{VirtualPort};
|
||||
use crate::prudp::router::Router;
|
||||
use crate::prudp::sockaddr::PRUDPSockAddr;
|
||||
use crate::prudp::station_url::Type::PRUDP;
|
||||
|
||||
mod endianness;
|
||||
mod prudp;
|
||||
|
|
@ -234,6 +234,18 @@ async fn start_secure_server() -> SecureServer{
|
|||
socket,
|
||||
}
|
||||
}*/
|
||||
/*
|
||||
define_rmc_proto!(
|
||||
proto AuthClientProtocol{
|
||||
Auth
|
||||
}
|
||||
);*/
|
||||
|
||||
|
||||
//#[rmc_struct(AuthClientProtocol)]
|
||||
struct AuthClient{
|
||||
|
||||
}
|
||||
|
||||
async fn start_servers(){
|
||||
|
||||
|
|
|
|||
34
src/rmc/protocols/auth.rs
Normal file
34
src/rmc/protocols/auth.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
use crate::rmc::response::ErrorCode;
|
||||
use crate::rmc::structures::any::Any;
|
||||
use crate::rmc::structures::connection_data::ConnectionData;
|
||||
use crate::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 {
|
||||
#[method_id(1)]
|
||||
async fn login(&self, name: String) -> Result<(), ErrorCode>;
|
||||
|
||||
#[method_id(2)]
|
||||
async fn login_ex(
|
||||
&self,
|
||||
name: String,
|
||||
extra_data: Any,
|
||||
) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode>;
|
||||
|
||||
#[method_id(3)]
|
||||
async fn request_ticket(
|
||||
&self,
|
||||
source_pid: u32,
|
||||
destination_pid: u32,
|
||||
) -> Result<(QResult, Vec<u8>), ErrorCode>;
|
||||
|
||||
#[method_id(4)]
|
||||
async fn get_pid(&self, username: String) -> Result<u32, ErrorCode>;
|
||||
|
||||
#[method_id(5)]
|
||||
async fn get_name(&self, pid: u32) -> Result<String, ErrorCode>;
|
||||
}
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
#![allow(async_fn_in_trait)]
|
||||
|
||||
pub mod auth;
|
||||
|
||||
use macros::method_id;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Add;
|
||||
|
|
@ -10,16 +14,18 @@ use paste::paste;
|
|||
use tokio::sync::{Mutex, Notify};
|
||||
use tokio::time::{sleep_until, Instant};
|
||||
use crate::prudp::socket::{ExternalConnection, SendingConnection};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
use crate::rmc::structures::connection_data::ConnectionData;
|
||||
use crate::rmc::structures::Error;
|
||||
use crate::rmc::structures::matchmake::AutoMatchmakeParam;
|
||||
|
||||
pub struct RmcConnection(pub SendingConnection, pub RmcResponseReceiver);
|
||||
|
||||
pub struct RmcResponseReceiver(Notify, Mutex<HashMap<(u16, u32), Vec<u8>>>);
|
||||
pub struct RmcResponseReceiver(Notify, Mutex<HashMap<(u32), Vec<u8>>>);
|
||||
|
||||
impl RmcResponseReceiver{
|
||||
// returns none if timed out
|
||||
pub async fn get_response_data(&self, proto: u16, method: u32) -> Option<Vec<u8>>{
|
||||
pub async fn get_response_data(&self, call_id: u32) -> Option<Vec<u8>>{
|
||||
let mut end_wait_time = Instant::now();
|
||||
end_wait_time += Duration::from_secs(5);
|
||||
|
||||
|
|
@ -29,13 +35,15 @@ impl RmcResponseReceiver{
|
|||
loop {
|
||||
let mut locked = self.1.lock().await;
|
||||
|
||||
if let Some(v) = locked.remove(&(proto, method)){
|
||||
if let Some(v) = locked.remove(&call_id){
|
||||
return Some(v);
|
||||
}
|
||||
|
||||
drop(locked);
|
||||
|
||||
|
||||
let notif_fut = self.0.notified();
|
||||
|
||||
drop(locked);
|
||||
|
||||
tokio::select! {
|
||||
_ = &mut sleep_fut => {
|
||||
|
|
@ -49,6 +57,10 @@ impl RmcResponseReceiver{
|
|||
}
|
||||
}
|
||||
|
||||
pub trait HasRmcConnection{
|
||||
fn get_response_receiver(&self) -> &RmcConnection;
|
||||
}
|
||||
|
||||
pub trait RemoteObject{
|
||||
fn new(conn: RmcConnection) -> Self;
|
||||
}
|
||||
|
|
@ -57,84 +69,36 @@ impl RemoteObject for (){
|
|||
fn new(_: RmcConnection) -> Self {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub trait RmcCallable{
|
||||
//type Remote: RemoteObject;
|
||||
//fn new_callable(remote: Self::Remote);
|
||||
async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec<u8>);
|
||||
async fn rmc_call(&self, responder: &SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! define_rmc_proto {
|
||||
(proto $name:ident{
|
||||
$($protocol:path),*
|
||||
}) => {
|
||||
paste!{
|
||||
paste::paste!{
|
||||
trait [<Local $name>]: std::any::Any $( + [<Raw $protocol>] + $protocol)* {
|
||||
async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec<u8>){
|
||||
async fn rmc_call(&self, remote_response_connection: &crate::prudp::socket::SendingConnection, 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, method_id, rest).await,
|
||||
[<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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct [<Remote $name>](crate::rmc::protocols::RmcConnection);
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
trait RawNotif{
|
||||
async fn rmc_call_proto(&self, method_id: u32, rest: Vec<u8>){
|
||||
|
||||
}
|
||||
}
|
||||
trait Notif{
|
||||
|
||||
}
|
||||
|
||||
struct RawNotifInfo;
|
||||
impl RawNotifInfo{
|
||||
const PROTOCOL_ID: u16 = 10;
|
||||
}
|
||||
|
||||
pub trait ImplementRemoteCalls{}
|
||||
|
||||
#[rmc_proto(1, NoReturn)]
|
||||
pub trait Another{
|
||||
#[method_id(1)]
|
||||
async fn test(&self, thing: AutoMatchmakeParam);
|
||||
}
|
||||
|
||||
define_rmc_proto!{
|
||||
proto TestProto{
|
||||
Notif,
|
||||
Another
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[rmc_struct(TestProto)]
|
||||
struct TestProtoImplementor{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl Notif for TestProtoImplementor{
|
||||
|
||||
}
|
||||
|
||||
impl RawNotif for TestProtoImplementor{
|
||||
|
||||
}
|
||||
|
||||
impl Another for TestProtoImplementor{
|
||||
async fn test(&self, thing: AutoMatchmakeParam) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplementRemoteCalls for TestProtoImplementor{}
|
||||
}
|
||||
|
|
@ -6,41 +6,42 @@ use crate::prudp::packet::{PRUDPPacket};
|
|||
use crate::prudp::packet::flags::{NEED_ACK, RELIABLE};
|
||||
use crate::prudp::packet::PacketOption::FragmentId;
|
||||
use crate::prudp::packet::types::DATA;
|
||||
use crate::prudp::socket::ExternalConnection;
|
||||
use crate::prudp::socket::{ExternalConnection, SendingConnection};
|
||||
use crate::rmc::structures::qresult::ERROR_MASK;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
use crate::web::DirectionalData::{Incoming, Outgoing};
|
||||
use crate::web::WEB_DATA;
|
||||
|
||||
pub enum RMCResponseResult {
|
||||
Success{
|
||||
Success {
|
||||
call_id: u32,
|
||||
method_id: u32,
|
||||
data: Vec<u8>,
|
||||
},
|
||||
Error{
|
||||
Error {
|
||||
error_code: ErrorCode,
|
||||
call_id: u32,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
pub struct RMCResponse {
|
||||
pub protocol_id: u8,
|
||||
pub response_result: RMCResponseResult
|
||||
pub response_result: RMCResponseResult,
|
||||
}
|
||||
|
||||
impl RMCResponse {
|
||||
pub fn to_data(self) -> Vec<u8>{
|
||||
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{
|
||||
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,
|
||||
RMCResponseResult::Error { .. } => 4 + 4,
|
||||
};
|
||||
|
||||
let mut data_out = Vec::with_capacity(size + 4);
|
||||
|
|
@ -50,7 +51,7 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re
|
|||
data_out.write_all(bytes_of(&u32_size))?;
|
||||
data_out.push(protocol_id);
|
||||
|
||||
match response{
|
||||
match response {
|
||||
RMCResponseResult::Success {
|
||||
call_id,
|
||||
method_id,
|
||||
|
|
@ -61,7 +62,7 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re
|
|||
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
|
||||
|
|
@ -78,10 +79,44 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re
|
|||
|
||||
Ok(data_out)
|
||||
}
|
||||
pub async fn send_response(original_packet: &PRUDPPacket, connection: &mut ExternalConnection, rmcresponse: RMCResponse){
|
||||
|
||||
pub async fn send_result(
|
||||
connection: &SendingConnection,
|
||||
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: {
|
||||
let mut vec = Vec::new();
|
||||
v.serialize(&mut vec).expect("serialization error");
|
||||
vec
|
||||
}
|
||||
},
|
||||
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: &SendingConnection, rmcresponse: RMCResponse) {
|
||||
connection.send(rmcresponse.to_data()).await;
|
||||
}
|
||||
|
||||
|
||||
//taken from kinnays error list directly
|
||||
#[allow(nonstandard_style)]
|
||||
#[repr(u32)]
|
||||
|
|
@ -356,25 +391,25 @@ pub enum ErrorCode {
|
|||
Custom_Unknown = 0x00740001,
|
||||
Ess_Unknown = 0x00750001,
|
||||
Ess_GameSessionError = 0x00750002,
|
||||
Ess_GameSessionMaintenance = 0x00750003
|
||||
Ess_GameSessionMaintenance = 0x00750003,
|
||||
}
|
||||
|
||||
impl Into<u32> for ErrorCode {
|
||||
impl Into<u32> for ErrorCode {
|
||||
fn into(self) -> u32 {
|
||||
unsafe{ transmute(self) }
|
||||
unsafe { transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod 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];
|
||||
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> =
|
||||
|
|
@ -390,11 +425,10 @@ mod test{
|
|||
rc4.apply_keystream(&mut data);
|
||||
|
||||
assert_eq!(data_orig, data);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enum_equivilance(){
|
||||
fn test_enum_equivilance() {
|
||||
let val: u32 = ErrorCode::Core_Unknown.into();
|
||||
assert_eq!(val, 0x00010001)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ use crate::endianness::{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;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ pub enum Error{
|
|||
VersionMismatch(u8),
|
||||
}
|
||||
|
||||
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub mod string;
|
||||
pub mod any;
|
||||
|
|
@ -30,9 +30,18 @@ pub mod qbuffer;
|
|||
pub mod primitives;
|
||||
pub mod matchmake;
|
||||
pub mod variant;
|
||||
mod ranking;
|
||||
pub mod ranking;
|
||||
|
||||
pub trait RmcSerialize: Sized{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()>;
|
||||
fn deserialize(reader: &mut dyn Read) -> Result<Self>;
|
||||
}
|
||||
|
||||
impl RmcSerialize for (){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn deserialize(reader: &mut dyn Read) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -91,4 +91,112 @@ impl<T: RmcSerialize, U: RmcSerialize> RmcSerialize for (T, U){
|
|||
|
||||
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))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue