progress
Some checks failed
Build and Test / super-mario-maker (push) Failing after 10m30s
Build and Test / fast-racing-neo (push) Failing after 11m40s
Build and Test / friends (push) Failing after 11m56s
Build and Test / wii-sports-club (push) Failing after 12m9s
Build and Test / splatoon (push) Failing after 12m14s
Build and Test / puyopuyo (push) Failing after 12m23s
Build and Test / wii-u-chat (push) Failing after 12m23s
Build and Test / mario-tennis (push) Failing after 12m23s
Build and Test / sonic-transformed (push) Failing after 12m23s
Build and Test / splatoon-testfire (push) Failing after 12m30s
Build and Test / minecraft-wiiu (push) Failing after 12m37s

This commit is contained in:
Maple Nebel 2026-07-12 18:21:18 +02:00
commit 4ff0a5efcc
187 changed files with 4526 additions and 5132 deletions

31
rnex-rmc/Cargo.toml Normal file
View file

@ -0,0 +1,31 @@
[package]
name = "rnex-rmc"
version = "0.1.0"
edition = "2024"
[dependencies]
bytemuck = { version = "1.25.0", features = ["derive"] }
thiserror = "2.0.18"
tracing = "0.1.44"
v-byte-helpers = { git = "https://github.com/RusticMaple/VByteMacros", version = "0.1.1" }
rnex-util = { path = "../rnex-util" }
tokio = { version = "1.52.3", features = ["sync", "io-util", "rt", "macros", "time"] }
ctor = "1.0.8"
rnex-rmc-macros = { path = "./macros" }
paste = "1.0.15"
rand = "0.10.2"
hex = "0.4.3"
[features]
rmc_struct_header = []
big_pid = []
v3-3-2 = []
v3-4-0 = ["v3-3-2", "rmc_struct_header"]
v3-5-0 = ["v3-4-0"]
v3-8-15 = ["v3-5-0"]
v3-10-22 = ["v3-8-15"]
v4-3-11 = ["v3-8-15"]
[lints]
workspace = true

160
rnex-rmc/macros/Cargo.lock generated Normal file
View file

@ -0,0 +1,160 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "macros"
version = "0.0.0"
dependencies = [
"proc-macro2",
"quote",
"rand",
"syn",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
[[package]]
name = "syn"
version = "2.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
[[package]]
name = "zerocopy"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View file

@ -0,0 +1,19 @@
[package]
name = "rnex-rmc-macros"
version = "0.0.0"
authors = ["RusticMaple <tvnebel@gmail.com>"]
description = "A `cargo generate` template for quick-starting a procedural macro crate"
keywords = ["template", "proc_macro", "procmacro"]
edition = "2024"
[lints]
workspace = true
[lib]
proc-macro = true
doctest = false
[dependencies]
quote = "1.0.38"
proc-macro2 = "1.0.93"
syn = { version = "2.0.98", features = ["full"] }

159
rnex-rmc/macros/src/lib.rs Normal file
View file

@ -0,0 +1,159 @@
#![allow(dead_code)]
#![allow(clippy::pedantic)]
mod protos;
mod rmc_struct;
mod util;
extern crate proc_macro;
use crate::protos::{ProtoInputParams, RmcProtocolData};
use crate::rmc_struct::{rmc_serialize_enum, rmc_serialize_struct};
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use syn::spanned::Spanned;
use syn::{Data, DeriveInput, parse_macro_input};
#[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))]
pub fn rmc_serialize(input: TokenStream) -> TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput);
let (serialize, deserialize, write_size, version, rmc_struct_impl) = match &derive_input.data {
Data::Struct(s) => rmc_serialize_struct(s, &derive_input.ident, &derive_input),
Data::Enum(e) => rmc_serialize_enum(e, &derive_input),
Data::Union(_) => {
unimplemented!("serializing a union is not allowed");
}
};
// generate base data
let ident = derive_input.ident;
let write_size = if let Some(v) = write_size {
quote! {
fn serialize_write_size(&self) -> ::rnex_rmc::serialization::Result<u32>{
#v
}
}
} else {
quote! {}
};
let version = if let Some(v) = version {
quote! {
fn version() -> Option<u8>{
#v
}
}
} else {
quote! {}
};
let rmc_struct_impl = rmc_struct_impl.unwrap_or_default();
let tokens = quote! {
#[automatically_derived]
impl ::rnex_rmc::serialization::RmcSerialize for #ident{
#[inline(always)]
fn serialize(&self, writer: &mut (impl ::std::io::Write + ?::std::marker::Sized)) -> ::rnex_rmc::serialization::Result<()>{
#serialize
}
#[inline(always)]
fn deserialize(reader: &mut (impl ::std::io::Read + ?::std::marker::Sized)) -> ::rnex_rmc::serialization::Result<Self>{
#deserialize
}
#write_size
#version
}
#rmc_struct_impl
};
tokens.into()
}
/// Macro to automatically generate code to use a specific trait as an rmc protocol for calling to
/// remote objects or accepting incoming remote requests.
/// This is needed in order to be able to use this as part of an rmc server interface.
///
/// The protocol id which is needed to be specified is specified as a parameter to this attribute.
///
/// You will also need to assign each function inside the trait a method id by using the
/// [`macro@method_id`] attribute.
///
/// You can also specify to have the protocol to be non-returning by adding a second parameter to
/// the attribute which is just `NoReturn` e.g. `#[rmc_proto(1, NoReturn)]`
///
/// Example
/// ```
/// // this rmc protocol has protocol id 1
/// use macros::rmc_proto;
///
/// #[rmc_proto(1)]
/// trait ExampleProtocol{
/// // this defines an rmc method with id 1
/// #[rmc_method(1)]
/// async fn hello_world_method(&self, name: String) -> Result<String, ErrorCode>;
/// }
/// ```
#[proc_macro_attribute]
pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream {
let params = parse_macro_input!(attr as ProtoInputParams);
let input = parse_macro_input!(input as syn::ItemTrait);
let raw_data = RmcProtocolData::new(params, &input);
quote! {
#input
#raw_data
}
.into()
}
/// Used to specify the method id of methods when making rmc protocols.
/// See [`macro@rmc_proto`] for further details.
///
/// Note: This attribute doesn't do anything by itself and just returns the thing it was attached to
/// unchanged.
#[proc_macro_attribute]
pub fn method_id(_attr: TokenStream, input: TokenStream) -> TokenStream {
// this attribute doesnt do anything by itself, see `rmc_proto`
input
}
#[proc_macro_attribute]
pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream {
let type_data = parse_macro_input!(input as DeriveInput);
let mut ident = parse_macro_input!(attr as syn::Path);
let last_token = ident.segments.last_mut().expect("empty path?");
last_token.ident = Ident::new(
&("Local".to_owned() + &last_token.ident.to_string()),
last_token.span(),
);
let struct_name = &type_data.ident;
let out = quote! {
#type_data
impl #ident for #struct_name{
}
impl ::rnex_rmc::RmcCallable for #struct_name{
async fn rmc_call(&self, remote_response_connection: &::rnex_rmc::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: &[u8]) -> bool{
<Self as #ident>::rmc_call(self, remote_response_connection, protocol_id, method_id, call_id, rest).await
}
}
};
out.into()
}
#[proc_macro_attribute]
pub fn connection(_attr: TokenStream, input: TokenStream) -> TokenStream {
// this attribute doesnt do anything by itself, see `rmc_struct`
input
}

View file

@ -0,0 +1,437 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::{ToTokens, quote};
use syn::{
Attribute, FnArg, ItemTrait, LitInt, LitStr, Meta, Pat, ReturnType, Token, TraitItem, Type,
parse::{Parse, ParseStream},
punctuated::Punctuated,
};
use crate::util::fold_tokenable;
pub struct ProtoInputParams {
proto_num: LitInt,
properties: Option<(Token![,], Punctuated<Ident, Token![,]>)>,
}
impl Parse for ProtoInputParams {
fn parse(input: ParseStream) -> syn::Result<Self> {
let proto_num = input.parse()?;
if let Some(seperator) = input.parse()? {
let mut punctuated = Punctuated::new();
loop {
punctuated.push_value(input.parse()?);
if let Some(punct) = input.parse()? {
punctuated.push_punct(punct);
} else {
return Ok(Self {
proto_num,
properties: Some((seperator, punctuated)),
});
}
}
} else {
Ok(Self {
proto_num,
properties: None,
})
}
}
}
pub struct ProtoMethodData {
pub id: LitInt,
pub name: Ident,
pub attributes: Vec<Attribute>,
pub parameters: Vec<(Ident, Type, Vec<Attribute>)>,
pub ret_val: ReturnType,
}
/// This is a representation of the code generated by `rmc_proto` it serves to split the logic of
/// acquiring data from the actual generation to tidy up the process into first getting then
/// generating.
///
/// Use the [`ToTokens`] trait to generate the actual code.
pub struct RmcProtocolData {
pub has_returns: bool,
pub id: LitInt,
pub name: Ident,
pub methods: Vec<ProtoMethodData>,
}
impl RmcProtocolData {
pub fn new(params: ProtoInputParams, input: &ItemTrait) -> Self {
let ProtoInputParams {
proto_num,
properties,
} = params;
let no_return_data = properties.is_some_and(|p| p.1.iter().any(|i| i == "NoReturn"));
// gigantic ass struct initializer (to summarize this gets all of the data)
RmcProtocolData {
has_returns: !no_return_data,
name: input.ident.clone(),
id: proto_num,
methods: input
.items
.iter()
.filter_map(|v| match v {
TraitItem::Fn(v) => Some(v),
_ => None,
})
.map(|func| {
let Some(attr) = func.attrs.iter().find(|a| {
a.path()
.segments
.last()
.is_some_and(|s| s.ident == "method_id")
}) else {
panic!("every function inside of an rmc protocol must have a method id");
};
let Ok(id): Result<LitInt, _> = attr.parse_args() else {
panic!("todo: put a propper error message here");
};
let funcs = func
.sig
.inputs
.iter()
.skip(1)
.map(|f| {
let FnArg::Typed(t) = f else {
panic!("what");
};
let Pat::Ident(i) = &*t.pat else {
panic!(
"unable to handle non identifier patterns as parameter bindings"
);
};
(i.ident.clone(), t.ty.as_ref().clone(), t.attrs.clone())
})
.collect();
ProtoMethodData {
id,
name: func.sig.ident.clone(),
parameters: funcs,
ret_val: func.sig.output.clone(),
attributes: func
.attrs
.iter()
.filter(|a| match &a.meta {
Meta::NameValue(v) => {
if let Some(i) = v.path.get_ident() {
i != "doc"
} else {
true
}
}
Meta::List(l) => {
if let Some(seg) = l.path.segments.last() {
seg.ident != "method_id"
} else {
true
}
}
_ => true,
})
.cloned()
.collect(),
}
})
.collect(),
}
}
fn generate_raw_trait(&self) -> TokenStream {
let Self {
has_returns,
name,
id,
methods,
} = self;
let generate_raw_method = |method: &ProtoMethodData| -> TokenStream {
let ProtoMethodData {
name,
parameters,
attributes,
..
} = method;
let attribs = fold_tokenable(attributes.iter());
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
let optional_return = if self.has_returns {
quote! {
-> ::core::result::Result<Vec<u8>, ::rnex_rmc::response::ErrorCode>
}
} else {
quote! {}
}
.into_token_stream();
let deser_params =
fold_tokenable(parameters.iter().map(|(param_name, param_type, attribs)| {
let error_msg = LitStr::new(
&format!("an error occurred whilst deserializing {}", param_name),
Span::call_site(),
);
let return_from_deser_error = if self.has_returns {
quote! {
return Err(::rnex_rmc::response::ErrorCode::Core_InvalidArgument);
}
} else {
quote! {
return;
}
};
let attribs = fold_tokenable(attribs.iter());
quote! {
#attribs
let Ok(#param_name) =
<#param_type as ::rnex_rmc::serialization::RmcSerialize>::deserialize(
&mut cursor
) else{
::rnex_rmc::tracing::error!(#error_msg);
#return_from_deser_error
};
}
}));
let call_params = fold_tokenable(parameters.iter().map(|(param_name, _, attribs)| {
let attribs = fold_tokenable(attribs.iter());
quote! {
#attribs
#param_name,
}
}));
let optional_method_return = if *has_returns {
quote! {
let retval = retval?;
let mut vec = Vec::new();
::rnex_rmc::serialization::RmcSerialize::serialize(&retval, &mut vec).ok();
Ok(vec)
}
} else {
quote! {}
};
quote! {
#[inline(always)]
#attribs
async fn #raw_name (&self, data: &[u8]) #optional_return{
let mut cursor = ::std::io::Cursor::new(data);
#deser_params
let retval = self.#name(#call_params).await;
#optional_method_return
}
}
};
let generate_rmc_call_proto = || {
let method_entries = fold_tokenable(methods.iter().map(|m| {
let ProtoMethodData {
id,
name,
attributes,
..
} = m;
let attribs = fold_tokenable(attributes.iter());
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
quote! {
#attribs
#id => self.#raw_name(data).await,
}
}));
let optional_notimpl_return = if self.has_returns {
quote! {
Err(::rnex_rmc::response::ErrorCode::Core_NotImplemented)
}
} else {
quote! {}
};
let optional_result_sendback = if *has_returns {
quote! {
::rnex_rmc::response::send_result(
remote_response_connection,
ret,
#id,
method_id,
call_id,
).await
}
} else {
quote! {}
};
quote! {
#[inline(always)]
async fn rmc_call_proto(
&self,
remote_response_connection: &::rnex_rmc::util::SendingBufferConnection,
method_id: u32,
call_id: u32,
data: &[u8],
){
let ret = match method_id{
#method_entries
v => {
::rnex_rmc::tracing::error!("(protocol {})unimplemented method id called on protocol: {}", #id, v);
#optional_notimpl_return
}
};
#optional_result_sendback
}
}
};
// this gives us the name which the identifier of the corresponding Raw trait
let raw_name = Ident::new(&format!("Raw{}", name), name.span());
let proto_raw_methods = fold_tokenable(self.methods.iter().map(generate_raw_method));
let rmc_call_proto = generate_rmc_call_proto();
// boilerplate tokens which all raw traits need
quote! {
#[allow(unused_must_use)]
#[automatically_derived]
pub trait #raw_name: #name{
#proto_raw_methods
#rmc_call_proto
}
#[automatically_derived]
impl<T: #name> #raw_name for T{}
}
.to_token_stream()
}
fn generate_raw_remote_trait(&self) -> TokenStream {
let Self {
has_returns,
name,
id: proto_id,
methods,
..
} = self;
// this gives us the name which the identifier of the corresponding Raw trait
let remote_name = Ident::new(&format!("Remote{}", name), name.span());
let generate_remote_method = |m: &ProtoMethodData| -> TokenStream {
let ProtoMethodData {
name,
parameters,
ret_val,
attributes,
id: method_id,
} = m;
let params = fold_tokenable(parameters.iter().map(|(ident, ty, attr)| {
let attrs = fold_tokenable(attr.iter());
quote! { #attrs #ident: #ty, }
}));
let optional_questionmark_operator = if self.has_returns {
quote! {
?
}
} else {
quote! {}
};
let param_serialize = fold_tokenable(parameters.iter().map(|(name, ty, attrs)|{
let attrs = fold_tokenable(attrs.iter());
quote!{
#attrs
::rnex_rmc::util::result::ResultExtension::display_err_or_some(
<#ty as ::rnex_rmc::serialization::RmcSerialize>::serialize(
&#name,
&mut cursor
)
).ok_or(::rnex_rmc::response::ErrorCode::Core_InvalidArgument)#optional_questionmark_operator ;
}
}));
let make_call = if *has_returns {
quote! {
::rnex_rmc::util::result::ResultExtension::display_err_or_some(
rmc_conn.make_raw_call(&message).await
).ok_or(::rnex_rmc::response::ErrorCode::Core_Exception)
}
} else {
quote! {
::rnex_rmc::util::result::ResultExtension::display_err_or_some(
rmc_conn.make_raw_call_no_response(&message).await
);
}
};
let attribs = fold_tokenable(attributes.iter());
quote! {
#attribs
async fn #name(&self, #params) #ret_val{
let mut send_data = ::std::vec::Vec::new();
let mut cursor = ::std::io::Cursor::new(&mut send_data);
#param_serialize
let call_id = ::rnex_rmc::rand::random();
let message = ::rnex_rmc::message::RMCMessage{
call_id,
method_id: #method_id,
protocol_id: #proto_id,
rest_of_data: send_data
};
let rmc_conn = <Self as ::rnex_rmc::HasRmcConnection>::get_connection(self);
#make_call
}
}
};
let remote_methods = fold_tokenable(methods.iter().map(generate_remote_method));
quote! {
#[doc(hidden)]
#[allow(unused_must_use)]
#[automatically_derived]
pub trait #remote_name: ::rnex_rmc::HasRmcConnection{
#remote_methods
}
}
}
fn generate_raw_info(&self) -> TokenStream {
let Self { name, id, .. } = self;
let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site());
quote! {
#[doc(hidden)]
#[automatically_derived]
pub struct #raw_info_name;
#[automatically_derived]
impl #raw_info_name {
pub const PROTOCOL_ID: u16 = #id;
}
}
}
}
impl ToTokens for RmcProtocolData {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.generate_raw_trait().to_tokens(tokens);
self.generate_raw_info().to_tokens(tokens);
self.generate_raw_remote_trait().to_tokens(tokens);
}
}

View file

@ -0,0 +1,484 @@
use proc_macro2::{Literal, Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
bracketed, parse::Parse, punctuated::Punctuated, token::Bracket, DataEnum, DataStruct,
DeriveInput, Field, Fields, Ident, LitStr, Meta, Token, Variant,
};
use crate::util::fold_tokenable;
struct RmcStructAttrVersion {
bracket: Bracket,
delim: Token![,],
feature_name: Literal,
struct_version: Literal,
}
struct RmcStructAttr {
base_ver: Literal,
versions: Option<(Token![,], Punctuated<RmcStructAttrVersion, Token![,]>)>,
}
impl Parse for RmcStructAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let base_ver = input.parse()?;
if let Some(seperator) = input.parse()? {
let mut punctuated = Punctuated::new();
loop {
punctuated.push_value(input.parse()?);
if let Some(punct) = input.parse()? {
punctuated.push_punct(punct);
} else {
return Ok(Self {
base_ver,
versions: Some((seperator, punctuated)),
});
}
}
} else {
Ok(Self {
base_ver,
versions: None,
})
}
}
}
impl RmcStructAttr {
fn versions(&self) -> impl Iterator<Item = &RmcStructAttrVersion> {
self.versions.iter().flat_map(|v| v.1.iter())
}
}
impl Parse for RmcStructAttrVersion {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let content;
let bracket = bracketed!(content in input);
let (feature_name, delim, struct_version) =
content.call(|s| Ok((s.parse()?, s.parse()?, s.parse()?)))?;
Ok(Self {
bracket,
delim,
feature_name,
struct_version,
})
}
}
pub fn generate_write_size_struct(
s: &DataStruct,
with_potential_header: bool,
) -> proc_macro2::TokenStream {
// this is fine and works because of a quirk where the sizes of the structs dont change
// if we ignore wether or not a struct extends the other struct or has it as a field
let base_size = fold_tokenable(s.fields.iter().map(|f| {
let ident = f.ident.as_ref().unwrap();
let attrs = fold_tokenable(f.attrs.iter().filter(|a| {
if let Some(i) = a.meta.path().get_ident() {
i != "extends"
} else {
true
}
}));
quote! {
#attrs
sum += ::rnex_rmc::serialization::RmcSerialize::serialize_write_size(&self.#ident)?;
}
}));
let optional_struct_header_calc = if with_potential_header {
quote! { sum += (if ::rnex_rmc::config::FEATURE_HAS_STRUCT_HEADER{ 5 } else { 0 }); }
} else {
quote! {}
};
quote! {
let mut sum = 0;
#base_size
#optional_struct_header_calc
Ok(sum)
}
}
pub fn generate_serialize_struct(
extended_struct: Option<&Field>,
elems: &[&Field],
with_header: bool,
) -> proc_macro2::TokenStream {
fn gen_elem_serialize(f: &Field) -> TokenStream {
let ident = f.ident.as_ref().unwrap();
let attrs = fold_tokenable(f.attrs.iter().filter(|a| {
if let Some(i) = a.meta.path().get_ident() {
i != "extends"
} else {
true
}
}));
quote! {
#attrs
::rnex_rmc::serialization::RmcSerialize::serialize(&self.#ident, writer)?;
}
}
let optional_extended_struct = if let Some(f) = extended_struct {
gen_elem_serialize(f)
} else {
quote! {}
};
let elems = fold_tokenable(elems.iter().map(|e| gen_elem_serialize(e)));
let ser_body = if with_header {
quote! {
::rnex_rmc::rmc_struct::write_struct(
writer,
Self::version().unwrap(),
::rnex_rmc::helpers::len_of_write(
|writer|{
#elems
Ok(())
}
),
|writer|{
#elems
Ok(())
}
)?;
}
} else {
elems
};
quote! {
#optional_extended_struct
#ser_body
Ok(())
}
}
pub fn generate_deserialize_struct(
s: &DataStruct,
extended_struct: Option<&Field>,
elems: &[&Field],
with_header: bool,
) -> proc_macro2::TokenStream {
fn gen_elem_serialize(f: &Field) -> TokenStream {
let ident = f.ident.as_ref().unwrap();
let ty = &f.ty;
let attrs = fold_tokenable(f.attrs.iter().filter(|a| {
if let Some(i) = a.meta.path().get_ident() {
i != "extends"
} else {
true
}
}));
quote! {
#attrs
let #ident: #ty = ::rnex_rmc::serialization::RmcSerialize::deserialize(reader)?;
}
}
let optional_extended_struct = if let Some(f) = extended_struct {
gen_elem_serialize(f)
} else {
quote! {}
};
let elems = fold_tokenable(elems.iter().map(|e| gen_elem_serialize(e)));
let struct_ctor_content = fold_tokenable(s.fields.iter().map(|f| {
let ident = f.ident.as_ref().unwrap();
let attrs = fold_tokenable(f.attrs.iter().filter(|a| {
if let Some(i) = a.meta.path().get_ident() {
i != "extends"
} else {
true
}
}));
quote! { #attrs #ident, }
}));
let de_body_inner = quote! {
#elems
Ok(Self{
#struct_ctor_content
})
};
let de_body = if with_header {
quote! {
::rnex_rmc::rmc_struct::read_struct(reader, Self::version().unwrap(), move |mut reader|{
#de_body_inner
})
}
} else {
de_body_inner
};
quote! {
#optional_extended_struct
#de_body
}
}
fn generate_struct_version(attr: Option<&RmcStructAttr>) -> proc_macro2::TokenStream {
if let Some(attr) = attr {
let base_ver = &attr.base_ver;
let if_else_chain = fold_tokenable(attr.versions().map(|v| {
let version_val = &v.struct_version;
let feature = &v.feature_name;
quote! {
if cfg!(feature = #feature){
#version_val
} else
}
}));
quote! {
Some(#if_else_chain {
#base_ver
})
}
} else {
quote! { None }
}
}
fn gen_rmc_struct_impl(
struct_ident: &Ident,
extended_struct: Option<&Field>,
) -> proc_macro2::TokenStream {
let self_name_str_lit = LitStr::new(&struct_ident.to_string(), struct_ident.span());
let register = if let Some(extended_struct) = extended_struct {
let extended_struct_ty = &extended_struct.ty;
let ext_ty_name = LitStr::new(
&extended_struct_ty.to_token_stream().to_string(),
Span::call_site(),
);
quote! {
#[::ctor::ctor(unsafe)]
#[allow(nonstandard_style)]
fn register_fun() {
println!("registering {} as parent of {}", #self_name_str_lit, #ext_ty_name);
let mut wr = <#extended_struct_ty as ::rnex_rmc::serialization::RmcStruct>::get_struct_info()
.inheritors
.write()
.expect("poisoned");
wr.push(<#struct_ident as ::rnex_rmc::serialization::RmcStruct>::get_struct_info());
}
}
} else {
quote! {}
};
quote! {
impl ::rnex_rmc::serialization::RmcStruct for #struct_ident {
fn get_struct_info() -> &'static ::rnex_rmc::serialization::RmcStructInfo {
#register
static STRUCT_DATA: ::rnex_rmc::serialization::RmcStructInfo =
::rnex_rmc::serialization::RmcStructInfo {
inheritors: ::std::sync::RwLock::new(::std::vec::Vec::new()),
name: #self_name_str_lit,
};
&STRUCT_DATA
}
}
}
}
pub fn rmc_serialize_struct(
s: &DataStruct,
name: &Ident,
derive_input: &DeriveInput,
) -> (
proc_macro2::TokenStream,
proc_macro2::TokenStream,
Option<proc_macro2::TokenStream>,
Option<proc_macro2::TokenStream>,
Option<proc_macro2::TokenStream>,
) {
let struct_attr = derive_input.attrs.iter().find(|a| {
a.path().segments.len() == 1
&& a.path()
.segments
.first()
.is_some_and(|p| p.ident == "rmc_struct")
&& matches!(a.meta, Meta::List(_))
});
let struct_attr: Option<RmcStructAttr> = struct_attr.map(|a| a.parse_args().unwrap());
let struct_attr = struct_attr.as_ref();
let extended_struct = s.fields.iter().find(|f| {
f.attrs.iter().any(|a| {
a.path().segments.len() == 1
&& a.path()
.segments
.first()
.is_some_and(|p| p.ident == "extends")
})
});
let elements: Vec<_> = s
.fields
.iter()
.filter(|f| {
!f.attrs.iter().any(|a| {
a.path().segments.len() == 1
&& a.path()
.segments
.first()
.is_some_and(|p| p.ident == "extends")
})
})
.collect();
let elements = &elements[..];
let serialize = generate_serialize_struct(extended_struct, elements, struct_attr.is_some());
let deserialize =
generate_deserialize_struct(s, extended_struct, elements, struct_attr.is_some());
let write_size = generate_write_size_struct(s, struct_attr.is_some());
let version = generate_struct_version(struct_attr);
let rmc_struct_impl = if struct_attr.is_some() {
Some(gen_rmc_struct_impl(name, extended_struct))
} else {
None
};
(
serialize,
deserialize,
Some(write_size),
Some(version),
rmc_struct_impl,
)
}
fn field_to_ident(field: &Field, idx: usize) -> Ident {
if let Some(i) = &field.ident {
i.clone()
} else {
Ident::new(&format!("field_{}", idx), Span::call_site())
}
}
fn variant_to_pattern_and_fields(variant: &Variant) -> (proc_macro2::TokenStream, Vec<Field>) {
match &variant.fields {
Fields::Named(n) => {
let inner = n
.named
.iter()
.map(|f| {
let attrs = fold_tokenable(f.attrs.iter());
let ident = f.ident.as_ref().unwrap();
quote! { #attrs #ident }
})
.reduce(|a, b| quote! {#a, #b});
(quote! {{#inner}}, n.named.iter().cloned().collect())
}
Fields::Unnamed(n) => {
let inner = n
.unnamed
.iter()
.enumerate()
.map(|(i, f)| {
let attrs = fold_tokenable(f.attrs.iter());
let name = field_to_ident(f, i);
quote! { #attrs #name }
})
.reduce(|a, b| quote! {#a, #b});
(quote! {(#inner)}, n.unnamed.iter().cloned().collect())
}
Fields::Unit => (quote! {}, vec![]),
}
}
pub fn rmc_generate_serialize_enum(
enum_data: &DataEnum,
repr_ty: &Ident,
) -> proc_macro2::TokenStream {
let match_content = fold_tokenable(enum_data.variants.iter().map(|v| {
let ident = &v.ident;
let descriminant = &v
.discriminant
.as_ref()
.expect("every variant must have a descriminant to be a valid rmc struct")
.1;
let (pattern, fields) = variant_to_pattern_and_fields(v);
let inner = fold_tokenable(fields.iter().enumerate().map(|(i, f)| {
let ty = &f.ty;
let name = field_to_ident(f, i);
quote! {<#ty as ::rnex_rmc::serialization::RmcSerialize>::serialize(#name, writer)?;}
}));
quote! {
Self::#ident #pattern => {
<#repr_ty as ::rnex_rmc::serialization::RmcSerialize>::serialize(&#descriminant, writer)?;
#inner
}
}
}));
quote! {
match self{
#match_content
}
Ok(())
}
}
pub fn rmc_generate_deserialize_enum(
enum_data: &DataEnum,
repr_ty: &Ident,
) -> proc_macro2::TokenStream {
let match_content = fold_tokenable(enum_data.variants.iter().map(|v| {
let ident = &v.ident;
let descriminant = &v
.discriminant
.as_ref()
.expect("every variant must have a descriminant to be a valid rmc struct")
.1;
let (pattern, fields) = variant_to_pattern_and_fields(v);
let inner = fold_tokenable(fields.iter().enumerate().map(|(i, f)| {
let ty = &f.ty;
let name = field_to_ident(f, i);
quote! {let #name = <#ty as ::rnex_rmc::serialization::RmcSerialize>::deserialize(reader)?;}
}));
quote! {
#descriminant => {
#inner
Self::#ident #pattern
}
}
}));
quote! {
let discriminant = <#repr_ty as ::rnex_rmc::serialization::RmcSerialize>::deserialize(reader)?;
Ok(match discriminant{
#match_content
v => {
return Err(::rnex_rmc::serialization::Error::UnexpectedValue(v as u64))
}
})
}
}
pub fn rmc_serialize_enum(
enum_data: &DataEnum,
derive_input: &DeriveInput,
) -> (
proc_macro2::TokenStream,
proc_macro2::TokenStream,
Option<proc_macro2::TokenStream>,
Option<proc_macro2::TokenStream>,
Option<proc_macro2::TokenStream>,
) {
let repr_attr = derive_input.attrs.iter().find(|a| {
a.path().segments.len() == 1 && a.path().segments.first().is_some_and(|p| p.ident == "repr")
});
let Some(repr_attr) = repr_attr else {
panic!("missing repr attribute");
};
let ty: Ident = repr_attr.parse_args().unwrap();
let serialize = rmc_generate_serialize_enum(enum_data, &ty);
let deserialize = rmc_generate_deserialize_enum(enum_data, &ty);
(serialize, deserialize, None, None, None)
}

View file

@ -0,0 +1,10 @@
use proc_macro2::TokenStream;
use quote::ToTokens;
// todo: return a wrapper struct implementing ToTokens over the iterator instead as to avoid unnescesary allocations with the token stream
pub fn fold_tokenable<T: ToTokens>(list: impl Iterator<Item = T>) -> TokenStream {
list.fold(TokenStream::new(), |mut s, i| {
i.to_tokens(&mut s);
s
})
}

103
rnex-rmc/src/any.rs Normal file
View file

@ -0,0 +1,103 @@
use crate::{
RmcSerialize,
serialization::{Error, Result, RmcStruct},
};
use std::{
io::{Cursor, Read, Write},
marker::PhantomData,
};
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use crate::data::Data;
#[derive(Clone, Debug)]
pub struct Any<T: RmcStruct = Data> {
pub name: String,
pub data: Vec<u8>,
pub phantom_data: PhantomData<T>,
}
impl<T: RmcStruct> RmcSerialize for Any<T> {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.name.serialize(writer)?;
let u32_len = self.data.len() as u32;
(u32_len + 4).serialize(writer)?;
self.data.serialize(writer)?;
Ok(())
}
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
let name = String::deserialize(reader)?;
if !T::get_struct_info().is_inheritor(&name) {
return Err(Error::InheritanceError);
}
// also length ?
let _len2: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
let data = Vec::deserialize(reader)?;
Ok(Any {
name,
data,
phantom_data: PhantomData,
})
}
}
impl<T: RmcStruct> Any<T> {
pub fn try_into<U: RmcStruct>(self) -> Result<Any<U>> {
if !U::get_struct_info().is_inheritor(&self.name) {
return Err(Error::InheritanceError);
}
Ok(Any {
data: self.data,
name: self.name,
phantom_data: PhantomData,
})
}
pub fn try_get_as<U: RmcStruct>(&self) -> Result<U> {
if !U::get_struct_info().is_inheritor(&self.name) {
return Err(Error::InheritanceError);
}
return U::deserialize(&mut Cursor::new(&self.data[..]));
}
pub fn new<U: RmcStruct>(val: &U) -> Result<Self> {
if !T::get_struct_info().is_inheritor(U::get_struct_info().name) {
return Err(Error::InheritanceError);
}
return Ok(Self {
name: U::get_struct_info().name.to_owned(),
data: val.to_data()?,
phantom_data: PhantomData,
});
}
pub fn get(&self) -> Result<T> {
return T::deserialize(&mut Cursor::new(&self.data[..]));
}
pub fn emplace_parent<E: RmcStruct>(&mut self, parent: &E) -> Result<()> {
// validate if E is actually a parent of the contained struct
if !E::get_struct_info().is_inheritor(&self.name) {
return Err(Error::InheritanceError);
}
let mut cur = Cursor::new(&self.data[..]);
// skip the parent part of the struct
let _ = T::deserialize(&mut cur)?;
let end_of_parent_pos = cur.position();
let rest_of_struct = &self
.data
.get(end_of_parent_pos as usize..)
.ok_or(Error::OOB)?;
let mut new_data = parent.to_data()?;
new_data.extend_from_slice(&rest_of_struct);
self.data = new_data;
Ok(())
}
}

36
rnex-rmc/src/buffer.rs Normal file
View file

@ -0,0 +1,36 @@
use std::io::{Read, Write};
use crate::serialization::{Result, RmcSerialize};
impl<'a> RmcSerialize for &'a [u8] {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> 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 (impl Read + ?Sized)) -> Result<Self> {
panic!("cannot deserialize to a u8 slice reference (use this ONLY for writing)")
}
fn serialize_write_size(&self) -> Result<u32> {
Ok(4 + self.len() as u32)
}
}
impl RmcSerialize for Box<[u8]> {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
(&self[..]).serialize(writer)
}
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Vec::deserialize(reader).map(|v| v.into_boxed_slice())
}
fn serialize_write_size(&self) -> Result<u32> {
(&self[..]).serialize_write_size()
}
}

41
rnex-rmc/src/data.rs Normal file
View file

@ -0,0 +1,41 @@
use std::io::{Read, Write};
use crate::{
config::FEATURE_HAS_STRUCT_HEADER,
helpers, rmc_struct,
serialization::{Result, RmcSerialize, RmcStruct, RmcStructInfo},
};
#[derive(Debug, Clone, Copy, Default)]
pub struct Data {}
impl RmcSerialize for Data {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
rmc_struct::write_struct(
writer,
Self::version().unwrap(),
helpers::len_of_write(|_| Ok(())),
|_| Ok(()),
)
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
rmc_struct::read_struct(reader, Self::version().unwrap(), move |_| Ok(Self {}))
}
fn serialize_write_size(&self) -> Result<u32> {
Ok(if FEATURE_HAS_STRUCT_HEADER { 5 } else { 0 })
}
fn version() -> Option<u8> {
Some(0)
}
}
impl RmcStruct for Data {
fn get_struct_info() -> &'static RmcStructInfo {
static STRUCT_DATA: RmcStructInfo = RmcStructInfo {
inheritors: ::std::sync::RwLock::new(::std::vec::Vec::new()),
name: "Data",
};
&STRUCT_DATA
}
}

15
rnex-rmc/src/date_time.rs Normal file
View file

@ -0,0 +1,15 @@
use std::io::{Read, Write};
use rnex_util::date_time::DateTime;
use crate::serialization::{Result, RmcSerialize};
impl RmcSerialize for DateTime {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.0.serialize(writer)
}
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(Self(u64::deserialize(reader)?))
}
}

53
rnex-rmc/src/helpers.rs Normal file
View file

@ -0,0 +1,53 @@
use std::{fmt, io};
use crate::serialization::Result;
pub struct DummyFormatWriter(u32);
impl fmt::Write for DummyFormatWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.0 += s.as_bytes().len() as u32;
Ok(())
}
}
impl DummyFormatWriter {
pub const fn new() -> Self {
Self(0)
}
pub const fn serialize_str_len(&self) -> u32 {
2 + self.0 + 1
}
}
pub struct DummyWriter(u32);
impl io::Write for DummyWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0 += buf.len() as u32;
Ok(buf.len())
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.0 += buf.len() as u32;
Ok(())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl DummyWriter {
pub const fn new() -> Self {
Self(0)
}
pub const fn get_total_len(&self) -> u32 {
self.0
}
}
pub fn len_of_write(f: impl FnOnce(&mut DummyWriter) -> Result<()>) -> u32 {
let mut dummy = DummyWriter::new();
f(&mut dummy).ok();
dummy.get_total_len()
}

473
rnex-rmc/src/lib.rs Normal file
View file

@ -0,0 +1,473 @@
#![allow(async_fn_in_trait)]
pub mod helpers;
pub mod message;
pub mod primitives;
pub mod qresult;
pub mod response;
pub mod rmc_struct;
pub mod station_url;
use std::{collections::HashMap, fmt::Debug, io::Cursor, ops::Deref, sync::Arc, time::Duration};
pub use rand;
pub use rnex_rmc_macros::*;
use rnex_util::{SendingBufferConnection, SplittableBufferConnection, result::ResultExtension};
use tokio::{
sync::{Mutex, Notify},
task,
time::{Instant, sleep, sleep_until},
};
pub use tracing;
pub mod any;
pub mod buffer;
pub mod data;
pub mod date_time;
pub mod list;
pub mod networking;
pub mod qbuffer;
pub mod serialization;
pub mod string;
pub mod string_set;
pub mod variant;
use thiserror::Error;
pub mod config {
pub const FEATURE_HAS_STRUCT_HEADER: bool = cfg!(feature = "rmc_struct_header");
}
pub use paste;
pub use rnex_util as util;
use tracing::{Instrument, error, info, info_span, instrument};
use crate::{
RemoteCallError::ConnectionBroke,
message::RMCMessage,
response::{ErrorCode, RMCResponse, RMCResponseResult},
serialization::RmcSerialize,
};
#[derive(Error, Debug)]
pub enum RemoteCallError {
#[error("Call to remote timed out whilst 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] serialization::Error),
}
#[derive(Clone, Debug)]
pub struct RmcConnection(pub SendingBufferConnection, pub RmcResponseReceiver);
#[derive(Clone, Debug)]
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;
}
}
pub trait HasProtoId<const ID: u16> {}
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;
// returns false on fail to match protocol to an implementation
fn rmc_call(
&self,
responder: &SendingBufferConnection,
protocol_id: u16,
method_id: u32,
call_id: u32,
rest: &[u8],
) -> impl std::future::Future<Output = bool> + Send;
}
impl<T: RmcCallable + Sync + Send> RmcCallable for Arc<T> {
async fn rmc_call(
&self,
responder: &SendingBufferConnection,
protocol_id: u16,
method_id: u32,
call_id: u32,
rest: &[u8],
) -> bool {
self.as_ref()
.rmc_call(responder, protocol_id, method_id, call_id, rest)
.await
}
}
impl<T: RmcCallable + Sync + Send> RmcCallable for Option<T> {
async fn rmc_call(
&self,
responder: &SendingBufferConnection,
protocol_id: u16,
method_id: u32,
call_id: u32,
rest: &[u8],
) -> bool {
if let Some(callable) = self.as_ref() {
return callable
.rmc_call(responder, protocol_id, method_id, call_id, rest)
.await;
}
false
}
}
impl<T: RmcCallable + Sync + Send> RmcCallable for Box<T> {
async fn rmc_call(
&self,
responder: &SendingBufferConnection,
protocol_id: u16,
method_id: u32,
call_id: u32,
rest: &[u8],
) -> bool {
self.as_ref()
.rmc_call(responder, protocol_id, method_id, call_id, rest)
.await
}
}
#[macro_export]
macro_rules! define_rmc_proto {
(proto $name:ident{
$($protocol:path),* $(,)?
}) => {
$crate::paste::paste!{
#[allow(unused_variables)]
pub trait [<Local $name>]: std::any::Any $( + [<Raw $protocol>] + $protocol)* {
async fn rmc_call(&self, remote_response_connection: &$crate::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: &[u8]) -> bool{
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; true},
)*
v => false
}
}
}
#[derive(Debug)]
pub struct [<Remote $name>]($crate::RmcConnection);
impl $crate::RmcPureRemoteObject for [<Remote $name>]{
fn new(conn: $crate::RmcConnection) -> Self{
Self(conn)
}
}
impl $crate::RemoteDisconnectable for [<Remote $name>]{
async fn disconnect(&self){
self.0.disconnect().await;
}
}
impl $crate::HasRmcConnection for [<Remote $name>]{
fn get_connection(&self) -> &$crate::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: &[u8],
) -> bool {
false
}
}
pub trait RmcPureRemoteObject {
fn new(conn: RmcConnection) -> Self;
}
pub trait RemoteDisconnectable {
async fn disconnect(&self);
}
#[derive(Debug)]
pub struct OnlyRemote<T: RemoteDisconnectable>(T);
impl<T: RemoteDisconnectable + RmcPureRemoteObject> OnlyRemote<T> {
pub fn new(conn: RmcConnection) -> Self {
Self(T::new(conn))
}
}
impl<T: RemoteDisconnectable> Deref for OnlyRemote<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: RemoteDisconnectable> OnlyRemote<T> {
pub async fn disconnect(&self) {
self.0.disconnect().await;
}
}
impl<T: RemoteDisconnectable> RmcCallable for OnlyRemote<T> {
fn rmc_call(
&self,
_responder: &SendingBufferConnection,
_protocol_id: u16,
_method_id: u32,
_call_id: u32,
_rest: &[u8],
) -> impl Future<Output = bool> + Send {
// maybe respond with not implemented or something
async { false }
}
}
async fn handle_incoming<T: RmcCallable + Send + Sync + Debug + 'static>(
sending_conn: SendingBufferConnection,
remote: Arc<T>,
notify: Arc<Notify>,
incoming: Arc<Mutex<HashMap<u32, RMCResponse>>>,
data: Vec<u8>,
) {
let Some(proto_id) = data.get(4) else {
error!("received too small rmc message.");
error!("ending rmc gateway.");
sending_conn.disconnect().await;
return;
};
// protocol 0 is hardcoded to be the no protocol protocol aka keepalive protocol
if *proto_id == 0 {
println!("got keepalive");
return;
}
if (proto_id & 0x80) == 0 {
let Some(response) = RMCResponse::new(&mut Cursor::new(data)).display_err_or_some() else {
error!("invalid rmc response.");
error!("ending rmc gateway.");
sending_conn.disconnect().await;
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(data)).display_err_or_some() else {
error!("invalid rmc message.");
error!("ending rmc gateway.");
sending_conn.disconnect().await;
return;
};
let RMCMessage {
protocol_id,
method_id,
call_id,
rest_of_data,
} = message;
async {
if !remote
.rmc_call(
&sending_conn,
protocol_id,
method_id,
call_id,
&rest_of_data[..],
)
.await
{
error!(
protocol_id,
method_id,
arguments = hex::encode(&rest_of_data),
"rmc call on unimplemented protocol"
)
}
}
.instrument(info_span!(
"rmc call",
protocol_id,
method_id,
call_id,
raw_arguments = hex::encode(&rest_of_data),
))
.await;
}
}
#[instrument]
async fn handle_incoming_loop<T: RmcCallable + Send + Sync + Debug + 'static>(
mut connection: SplittableBufferConnection,
remote: Arc<T>,
notify: Arc<Notify>,
incoming: Arc<Mutex<HashMap<u32, RMCResponse>>>,
) {
while let Some(data) = connection.recv().await {
let sending_conn = connection.duplicate_sender();
let remote = remote.clone();
let notify = notify.clone();
let incoming = incoming.clone();
task::spawn(
handle_incoming(sending_conn, remote, notify, incoming, data).in_current_span(),
);
}
info!("rmc disconnected")
}
pub async fn new_rmc_gateway_connection<T: RmcCallable + Debug + Sync + Send + 'static, F>(
conn: SplittableBufferConnection,
create_internal: F,
) -> Arc<T>
where
F: AsyncFnOnce(RmcConnection) -> Arc<T>,
{
async move {
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)
.instrument(info_span!("initializing inner object"))
.await;
{
let exposed_object = exposed_object.clone();
tokio::spawn(async move {
handle_incoming_loop(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;
}
}
.instrument(info_span!("timeout sender")),
);
}
exposed_object
}
// todo: maybe add info on who we're creating the gateway to somehow
.instrument(info_span!("create new rmc gateway connection"))
.await
}
define_rmc_proto! {
proto NoProto{}
}

78
rnex-rmc/src/list.rs Normal file
View file

@ -0,0 +1,78 @@
use bytemuck::bytes_of;
use std::io::{Read, Write};
use std::mem::MaybeUninit;
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use crate::serialization::{Result, RmcSerialize};
pub type Buffer = Vec<u8>;
pub type List<T> = Vec<T>;
// 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 (impl Write + ?Sized)) -> 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 (impl Read + ?Sized)) -> Result<Self> {
println!("reading list");
let len: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
println!("readijg list: {:?}", len);
//let mut vec = Vec::with_capacity(len as usize);
let vec: Vec<T> = (0..len)
.map(|_| T::deserialize(reader))
.collect::<Result<Vec<_>, _>>()?;
Ok(vec)
}
fn serialize_write_size(&self) -> Result<u32> {
let mut val = 0u32;
for i in self {
val += i.serialize_write_size()?;
}
Ok(4 + val)
}
}
impl<const LEN: usize, T: RmcSerialize> RmcSerialize for [T; LEN] {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
for i in 0..LEN {
self[i].serialize(writer)?;
}
Ok(())
}
fn deserialize(reader: &mut (impl Read + ?Sized)) -> 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)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
let mut val = 0u32;
for i in self {
val += i.serialize_write_size()?;
}
Ok(val)
}
}

105
rnex-rmc/src/message.rs Normal file
View file

@ -0,0 +1,105 @@
use bytemuck::bytes_of;
use std::io;
use std::io::{Read, Seek, Write};
use tracing::error;
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use crate::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()
);
}
// println!("rmc packet: protoid: {}, method id: {}", protocol_id, method_id);
// println!("{}", hex::encode(&rest_of_data));
//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,
}
}
}

View file

@ -0,0 +1,94 @@
use std::io::{Read, Write};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use crate::serialization::{Error, Result, RmcSerialize};
impl RmcSerialize for SocketAddr {
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self>
where
Self: Sized,
{
let val: u8 = reader.read_struct(IS_BIG_ENDIAN)?;
match val {
4 => Ok(SocketAddr::V4(SocketAddrV4::deserialize(reader)?)),
6 => Ok(SocketAddr::V6(SocketAddrV6::deserialize(reader)?)),
v => Err(Error::UnexpectedValue(v as u64)),
}
}
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
match self {
SocketAddr::V4(v) => {
writer.write_all(&[4])?;
v.serialize(writer)?;
}
SocketAddr::V6(v) => {
writer.write_all(&[6])?;
v.serialize(writer)?;
}
}
Ok(())
}
}
impl RmcSerialize for SocketAddrV4 {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.ip().to_bits().serialize(writer)?;
self.port().serialize(writer)?;
Ok(())
}
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
let ip = u32::deserialize(reader)?;
let port = u16::deserialize(reader)?;
Ok(SocketAddrV4::new(Ipv4Addr::from_bits(ip), port))
}
fn serialize_write_size(&self) -> Result<u32> {
Ok(6)
}
}
impl RmcSerialize for SocketAddrV6 {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.ip().to_bits().serialize(writer)?;
self.port().serialize(writer)?;
self.flowinfo().serialize(writer)?;
self.scope_id().serialize(writer)?;
Ok(())
}
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
let ip = u128::deserialize(reader)?;
let port = u16::deserialize(reader)?;
let flowinfo = u32::deserialize(reader)?;
let scope_id = u32::deserialize(reader)?;
Ok(SocketAddrV6::new(
Ipv6Addr::from_bits(ip),
port,
flowinfo,
scope_id,
))
}
fn serialize_write_size(&self) -> Result<u32> {
Ok(6)
}
}
/*
use rnex_core::prudp::virtual_port::VirtualPort;
impl RmcSerialize for VirtualPort {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.0.serialize(writer)?;
Ok(())
}
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(Self(u8::deserialize(reader)?))
}
fn serialize_write_size(&self) -> Result<u32> {
Ok(1)
}
}*/

497
rnex-rmc/src/primitives.rs Normal file
View file

@ -0,0 +1,497 @@
use bytemuck::{bytes_of, bytes_of_mut};
use std::io::{Read, Write};
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use crate::serialization::{Result, RmcSerialize};
impl RmcSerialize for u8 {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(writer.write_all(bytes_of(self))?)
}
#[inline(always)]
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(1)
}
}
impl RmcSerialize for i8 {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(writer.write_all(bytes_of(self))?)
}
#[inline(always)]
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(1)
}
}
impl RmcSerialize for u16 {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(writer.write_all(bytes_of(self))?)
}
#[inline(always)]
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(2)
}
}
impl RmcSerialize for i16 {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(writer.write_all(bytes_of(self))?)
}
#[inline(always)]
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(2)
}
}
impl RmcSerialize for u32 {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(writer.write_all(bytes_of(self))?)
}
#[inline(always)]
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(4)
}
}
impl RmcSerialize for i32 {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(writer.write_all(bytes_of(self))?)
}
#[inline(always)]
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(4)
}
}
impl RmcSerialize for u64 {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(writer.write_all(bytes_of(self))?)
}
#[inline(always)]
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(8)
}
}
impl RmcSerialize for u128 {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(writer.write_all(bytes_of(self))?)
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
let mut data = 0u128;
reader.read_exact(&mut bytes_of_mut(&mut data))?;
Ok(data)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(8)
}
}
impl RmcSerialize for i64 {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(writer.write_all(bytes_of(self))?)
}
#[inline(always)]
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(8)
}
}
impl RmcSerialize for f64 {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(writer.write_all(bytes_of(self))?)
}
#[inline(always)]
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(8)
}
}
impl RmcSerialize for bool {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
match self {
true => writer.write_all(&[1])?,
false => writer.write_all(&[0])?,
}
Ok(())
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(u8::deserialize(reader)? != 0)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(1)
}
}
impl<T: RmcSerialize, U: RmcSerialize> RmcSerialize for (T, U) {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.0.serialize(writer)?;
self.1.serialize(writer)?;
Ok(())
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
let first = T::deserialize(reader)?;
let second = U::deserialize(reader)?;
Ok((first, second))
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(self.0.serialize_write_size()? + self.1.serialize_write_size()?)
}
}
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize> RmcSerialize for (T, U, V) {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.0.serialize(writer)?;
self.1.serialize(writer)?;
self.2.serialize(writer)?;
Ok(())
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
let first = T::deserialize(reader)?;
let second = U::deserialize(reader)?;
let third = V::deserialize(reader)?;
Ok((first, second, third))
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(self.0.serialize_write_size()?
+ self.1.serialize_write_size()?
+ self.2.serialize_write_size()?)
}
}
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize> RmcSerialize
for (T, U, V, W)
{
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.0.serialize(writer)?;
self.1.serialize(writer)?;
self.2.serialize(writer)?;
self.3.serialize(writer)?;
Ok(())
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> 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))
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(self.0.serialize_write_size()?
+ self.1.serialize_write_size()?
+ self.2.serialize_write_size()?
+ self.3.serialize_write_size()?)
}
}
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize, X: RmcSerialize>
RmcSerialize for (T, U, V, W, X)
{
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.0.serialize(writer)?;
self.1.serialize(writer)?;
self.2.serialize(writer)?;
self.3.serialize(writer)?;
self.4.serialize(writer)?;
Ok(())
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> 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))
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(self.0.serialize_write_size()?
+ self.1.serialize_write_size()?
+ self.2.serialize_write_size()?
+ self.3.serialize_write_size()?
+ self.4.serialize_write_size()?)
}
}
impl<
T: RmcSerialize,
U: RmcSerialize,
V: RmcSerialize,
W: RmcSerialize,
X: RmcSerialize,
Y: RmcSerialize,
> RmcSerialize for (T, U, V, W, X, Y)
{
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> 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(())
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> 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))
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(self.0.serialize_write_size()?
+ self.1.serialize_write_size()?
+ self.2.serialize_write_size()?
+ self.3.serialize_write_size()?
+ self.4.serialize_write_size()?
+ self.5.serialize_write_size()?)
}
}
impl<
T: RmcSerialize,
U: RmcSerialize,
V: RmcSerialize,
W: RmcSerialize,
X: RmcSerialize,
Y: RmcSerialize,
Z: RmcSerialize,
> RmcSerialize for (T, U, V, W, X, Y, Z)
{
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> 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(())
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> 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))
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(self.0.serialize_write_size()?
+ self.1.serialize_write_size()?
+ self.2.serialize_write_size()?
+ self.3.serialize_write_size()?
+ self.4.serialize_write_size()?
+ self.5.serialize_write_size()?
+ self.6.serialize_write_size()?)
}
}
impl<
T: RmcSerialize,
U: RmcSerialize,
V: RmcSerialize,
W: RmcSerialize,
X: RmcSerialize,
Y: RmcSerialize,
Z: RmcSerialize,
A: RmcSerialize,
> RmcSerialize for (T, U, V, W, X, Y, Z, A)
{
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> 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)?;
self.7.serialize(writer)?;
Ok(())
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> 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)?;
let eighth = A::deserialize(reader)?;
Ok((first, second, third, fourth, fifth, sixth, seventh, eighth))
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(self.0.serialize_write_size()?
+ self.1.serialize_write_size()?
+ self.2.serialize_write_size()?
+ self.3.serialize_write_size()?
+ self.4.serialize_write_size()?
+ self.5.serialize_write_size()?
+ self.6.serialize_write_size()?
+ self.7.serialize_write_size()?)
}
}
impl<
T: RmcSerialize,
U: RmcSerialize,
V: RmcSerialize,
W: RmcSerialize,
X: RmcSerialize,
Y: RmcSerialize,
Z: RmcSerialize,
A: RmcSerialize,
B: RmcSerialize,
> RmcSerialize for (T, U, V, W, X, Y, Z, A, B)
{
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> 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)?;
self.7.serialize(writer)?;
self.8.serialize(writer)?;
Ok(())
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> 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)?;
let eighth = A::deserialize(reader)?;
let nineth = B::deserialize(reader)?;
Ok((
first, second, third, fourth, fifth, sixth, seventh, eighth, nineth,
))
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(self.0.serialize_write_size()?
+ self.1.serialize_write_size()?
+ self.2.serialize_write_size()?
+ self.3.serialize_write_size()?
+ self.4.serialize_write_size()?
+ self.5.serialize_write_size()?
+ self.6.serialize_write_size()?
+ self.7.serialize_write_size()?
+ self.8.serialize_write_size()?)
}
}
impl<T: RmcSerialize> RmcSerialize for Box<T> {
#[inline(always)]
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.as_ref().serialize(writer)
}
#[inline(always)]
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
T::deserialize(reader).map(Box::new)
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
T::serialize_write_size(self.as_ref())
}
}

29
rnex-rmc/src/qbuffer.rs Normal file
View file

@ -0,0 +1,29 @@
use bytemuck::bytes_of;
use std::io::{Read, Write};
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use crate::serialization::{Result, RmcSerialize};
#[derive(Clone, Debug, Default)]
pub struct QBuffer(pub Vec<u8>);
impl RmcSerialize for QBuffer {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> 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 (impl Read + ?Sized)) -> 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))
}
}

40
rnex-rmc/src/qresult.rs Normal file
View file

@ -0,0 +1,40 @@
use bytemuck::{Pod, Zeroable};
use std::io::{Read, Write};
use crate::{
response::ErrorCode,
serialization::{Result, RmcSerialize},
};
pub const ERROR_MASK: u32 = 1 << 31;
#[derive(Pod, Zeroable, Copy, Clone, 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 (impl Write + ?Sized)) -> Result<()> {
self.0.serialize(writer)
}
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
RmcSerialize::deserialize(reader).map(Self)
}
fn serialize_write_size(&self) -> Result<u32> {
Ok(4)
}
}

470
rnex-rmc/src/response.rs Normal file
View file

@ -0,0 +1,470 @@
// 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 crate::qresult::ERROR_MASK;
use crate::serialization::Error;
use bytemuck::bytes_of;
use rnex_util::SendingBufferConnection;
use std::io;
use std::io::{Read, Seek, Write};
use std::mem::transmute;
use tracing::{error, warn};
use v_byte_helpers::EnumTryInto;
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
#[derive(Debug, Clone)]
pub enum RMCResponseResult {
Success {
call_id: u32,
method_id: u32,
data: Vec<u8>,
},
Error {
error_code: ErrorCode,
call_id: u32,
},
}
#[derive(Debug, Clone)]
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 & (!0x8000_0000);
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);
ErrorCode::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) => {
warn!("error occurred during call: {:?}", 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 = 0x0001_0001,
Core_NotImplemented = 0x0001_0002,
Core_InvalidPointer = 0x0001_0003,
Core_OperationAborted = 0x0001_0004,
Core_Exception = 0x0001_0005,
Core_AccessDenied = 0x0001_0006,
Core_InvalidHandle = 0x0001_0007,
Core_InvalidIndex = 0x0001_0008,
Core_OutOfMemory = 0x0001_0009,
Core_InvalidArgument = 0x0001_000A,
Core_Timeout = 0x0001_000B,
Core_InitializationFailure = 0x0001_000C,
Core_CallInitiationFailure = 0x0001_000D,
Core_RegistrationError = 0x0001_000E,
Core_BufferOverflow = 0x0001_000F,
Core_InvalidLockState = 0x0001_0010,
Core_InvalidSequence = 0x0001_0011,
Core_SystemError = 0x0001_0012,
Core_Cancelled = 0x0001_0013,
DDL_InvalidSignature = 0x0002_0001,
DDL_IncorrectVersion = 0x0002_0002,
RendezVous_ConnectionFailure = 0x0003_0001,
RendezVous_NotAuthenticated = 0x0003_0002,
RendezVous_InvalidUsername = 0x0003_0064,
RendezVous_InvalidPassword = 0x0003_0065,
RendezVous_UsernameAlreadyExists = 0x0003_0066,
RendezVous_AccountDisabled = 0x0003_0067,
RendezVous_AccountExpired = 0x0003_0068,
RendezVous_ConcurrentLoginDenied = 0x0003_0069,
RendezVous_EncryptionFailure = 0x0003_006A,
RendezVous_InvalidPID = 0x0003_006B,
RendezVous_MaxConnectionsReached = 0x0003_006C,
RendezVous_InvalidGID = 0x0003_006D,
RendezVous_InvalidControlScriptID = 0x0003_006E,
RendezVous_InvalidOperationInLiveEnvironment = 0x0003_006F,
RendezVous_DuplicateEntry = 0x0003_0070,
RendezVous_ControlScriptFailure = 0x0003_0071,
RendezVous_ClassNotFound = 0x0003_0072,
RendezVous_SessionVoid = 0x0003_0073,
RendezVous_DDLMismatch = 0x0003_0075,
RendezVous_InvalidConfiguration = 0x0003_0076,
RendezVous_SessionFull = 0x0003_00C8,
RendezVous_InvalidGatheringPassword = 0x0003_00C9,
RendezVous_WithoutParticipationPeriod = 0x0003_00CA,
RendezVous_PersistentGatheringCreationMax = 0x0003_00CB,
RendezVous_PersistentGatheringParticipationMax = 0x0003_00CC,
RendezVous_DeniedByParticipants = 0x0003_00CD,
RendezVous_ParticipantInBlackList = 0x0003_00CE,
RendezVous_GameServerMaintenance = 0x0003_00CF,
RendezVous_OperationPostpone = 0x0003_00D0,
RendezVous_OutOfRatingRange = 0x0003_00D1,
RendezVous_ConnectionDisconnected = 0x0003_00D2,
RendezVous_InvalidOperation = 0x0003_00D3,
RendezVous_NotParticipatedGathering = 0x0003_00D4,
RendezVous_MatchmakeSessionUserPasswordUnmatch = 0x0003_00D5,
RendezVous_MatchmakeSessionSystemPasswordUnmatch = 0x0003_00D6,
RendezVous_UserIsOffline = 0x0003_00D7,
RendezVous_AlreadyParticipatedGathering = 0x0003_00D8,
RendezVous_PermissionDenied = 0x0003_00D9,
RendezVous_NotFriend = 0x0003_00DA,
RendezVous_SessionClosed = 0x0003_00DB,
RendezVous_DatabaseTemporarilyUnavailable = 0x0003_00DC,
RendezVous_InvalidUniqueId = 0x0003_00DD,
RendezVous_MatchmakingWithdrawn = 0x0003_00DE,
RendezVous_LimitExceeded = 0x0003_00DF,
RendezVous_AccountTemporarilyDisabled = 0x0003_00E0,
RendezVous_PartiallyServiceClosed = 0x0003_00E1,
RendezVous_ConnectionDisconnectedForConcurrentLogin = 0x0003_00E2,
PythonCore_Exception = 0x0004_0001,
PythonCore_TypeError = 0x0004_0002,
PythonCore_IndexError = 0x0004_0003,
PythonCore_InvalidReference = 0x0004_0004,
PythonCore_CallFailure = 0x0004_0005,
PythonCore_MemoryError = 0x0004_0006,
PythonCore_KeyError = 0x0004_0007,
PythonCore_OperationError = 0x0004_0008,
PythonCore_ConversionError = 0x0004_0009,
PythonCore_ValidationError = 0x0004_000A,
Transport_Unknown = 0x0005_0001,
Transport_ConnectionFailure = 0x0005_0002,
Transport_InvalidUrl = 0x0005_0003,
Transport_InvalidKey = 0x0005_0004,
Transport_InvalidURLType = 0x0005_0005,
Transport_DuplicateEndpoint = 0x0005_0006,
Transport_IOError = 0x0005_0007,
Transport_Timeout = 0x0005_0008,
Transport_ConnectionReset = 0x0005_0009,
Transport_IncorrectRemoteAuthentication = 0x0005_000A,
Transport_ServerRequestError = 0x0005_000B,
Transport_DecompressionFailure = 0x0005_000C,
Transport_ReliableSendBufferFullFatal = 0x0005_000D,
Transport_UPnPCannotInit = 0x0005_000E,
Transport_UPnPCannotAddMapping = 0x0005_000F,
Transport_NatPMPCannotInit = 0x0005_0010,
Transport_NatPMPCannotAddMapping = 0x0005_0011,
Transport_UnsupportedNAT = 0x0005_0013,
Transport_DnsError = 0x0005_0014,
Transport_ProxyError = 0x0005_0015,
Transport_DataRemaining = 0x0005_0016,
Transport_NoBuffer = 0x0005_0017,
Transport_NotFound = 0x0005_0018,
Transport_TemporaryServerError = 0x0005_0019,
Transport_PermanentServerError = 0x0005_001A,
Transport_ServiceUnavailable = 0x0005_001B,
Transport_ReliableSendBufferFull = 0x0005_001C,
Transport_InvalidStation = 0x0005_001D,
Transport_InvalidSubStreamID = 0x0005_001E,
Transport_PacketBufferFull = 0x0005_001F,
Transport_NatTraversalError = 0x0005_0020,
Transport_NatCheckError = 0x0005_0021,
DOCore_StationNotReached = 0x0006_0001,
DOCore_TargetStationDisconnect = 0x0006_0002,
DOCore_LocalStationLeaving = 0x0006_0003,
DOCore_ObjectNotFound = 0x0006_0004,
DOCore_InvalidRole = 0x0006_0005,
DOCore_CallTimeout = 0x0006_0006,
DOCore_RMCDispatchFailed = 0x0006_0007,
DOCore_MigrationInProgress = 0x0006_0008,
DOCore_NoAuthority = 0x0006_0009,
DOCore_NoTargetStationSpecified = 0x0006_000A,
DOCore_JoinFailed = 0x0006_000B,
DOCore_JoinDenied = 0x0006_000C,
DOCore_ConnectivityTestFailed = 0x0006_000D,
DOCore_Unknown = 0x0006_000E,
DOCore_UnfreedReferences = 0x0006_000F,
DOCore_JobTerminationFailed = 0x0006_0010,
DOCore_InvalidState = 0x0006_0011,
DOCore_FaultRecoveryFatal = 0x0006_0012,
DOCore_FaultRecoveryJobProcessFailed = 0x0006_0013,
DOCore_StationInconsitency = 0x0006_0014,
DOCore_AbnormalMasterState = 0x0006_0015,
DOCore_VersionMismatch = 0x0006_0016,
FPD_NotInitialized = 0x0065_0000,
FPD_AlreadyInitialized = 0x0065_0001,
FPD_NotConnected = 0x0065_0002,
FPD_Connected = 0x0065_0003,
FPD_InitializationFailure = 0x0065_0004,
FPD_OutOfMemory = 0x0065_0005,
FPD_RmcFailed = 0x0065_0006,
FPD_InvalidArgument = 0x0065_0007,
FPD_InvalidLocalAccountID = 0x0065_0008,
FPD_InvalidPrincipalID = 0x0065_0009,
FPD_InvalidLocalFriendCode = 0x0065_000A,
FPD_LocalAccountNotExists = 0x0065_000B,
FPD_LocalAccountNotLoaded = 0x0065_000C,
FPD_LocalAccountAlreadyLoaded = 0x0065_000D,
FPD_FriendAlreadyExists = 0x0065_000E,
FPD_FriendNotExists = 0x0065_000F,
FPD_FriendNumMax = 0x0065_0010,
FPD_NotFriend = 0x0065_0011,
FPD_FileIO = 0x0065_0012,
FPD_P2PInternetProhibited = 0x0065_0013,
FPD_Unknown = 0x0065_0014,
FPD_InvalidState = 0x0065_0015,
FPD_AddFriendProhibited = 0x0065_0017,
FPD_InvalidAccount = 0x0065_0019,
FPD_BlacklistedByMe = 0x0065_001A,
FPD_FriendAlreadyAdded = 0x0065_001C,
FPD_MyFriendListLimitExceed = 0x0065_001D,
FPD_RequestLimitExceed = 0x0065_001E,
FPD_InvalidMessageID = 0x0065_001F,
FPD_MessageIsNotMine = 0x0065_0020,
FPD_MessageIsNotForMe = 0x0065_0021,
FPD_FriendRequestBlocked = 0x0065_0022,
FPD_NotInMyFriendList = 0x0065_0023,
FPD_FriendListedByMe = 0x0065_0024,
FPD_NotInMyBlacklist = 0x0065_0025,
FPD_IncompatibleAccount = 0x0065_0026,
FPD_BlockSettingChangeNotAllowed = 0x0065_0027,
FPD_SizeLimitExceeded = 0x0065_0028,
FPD_OperationNotAllowed = 0x0065_0029,
FPD_NotNetworkAccount = 0x0065_002A,
FPD_NotificationNotFound = 0x0065_002B,
FPD_PreferenceNotInitialized = 0x0065_002C,
FPD_FriendRequestNotAllowed = 0x0065_002D,
Ranking_NotInitialized = 0x0067_0001,
Ranking_InvalidArgument = 0x0067_0002,
Ranking_RegistrationError = 0x0067_0003,
Ranking_NotFound = 0x0067_0005,
Ranking_InvalidScore = 0x0067_0006,
Ranking_InvalidDataSize = 0x0067_0007,
Ranking_PermissionDenied = 0x0067_0009,
Ranking_Unknown = 0x0067_000A,
Ranking_NotImplemented = 0x0067_000B,
Authentication_NASAuthenticateError = 0x0068_0001,
Authentication_TokenParseError = 0x0068_0002,
Authentication_HttpConnectionError = 0x0068_0003,
Authentication_HttpDNSError = 0x0068_0004,
Authentication_HttpGetProxySetting = 0x0068_0005,
Authentication_TokenExpired = 0x0068_0006,
Authentication_ValidationFailed = 0x0068_0007,
Authentication_InvalidParam = 0x0068_0008,
Authentication_PrincipalIdUnmatched = 0x0068_0009,
Authentication_MoveCountUnmatch = 0x0068_000A,
Authentication_UnderMaintenance = 0x0068_000B,
Authentication_UnsupportedVersion = 0x0068_000C,
Authentication_ServerVersionIsOld = 0x0068_000D,
Authentication_Unknown = 0x0068_000E,
Authentication_ClientVersionIsOld = 0x0068_000F,
Authentication_AccountLibraryError = 0x0068_0010,
Authentication_ServiceNoLongerAvailable = 0x0068_0011,
Authentication_UnknownApplication = 0x0068_0012,
Authentication_ApplicationVersionIsOld = 0x0068_0013,
Authentication_OutOfService = 0x0068_0014,
Authentication_NetworkServiceLicenseRequired = 0x0068_0015,
Authentication_NetworkServiceLicenseSystemError = 0x0068_0016,
Authentication_NetworkServiceLicenseError3 = 0x0068_0017,
Authentication_NetworkServiceLicenseError4 = 0x0068_0018,
DataStore_Unknown = 0x0069_0001,
DataStore_InvalidArgument = 0x0069_0002,
DataStore_PermissionDenied = 0x0069_0003,
DataStore_NotFound = 0x0069_0004,
DataStore_AlreadyLocked = 0x0069_0005,
DataStore_UnderReviewing = 0x0069_0006,
DataStore_Expired = 0x0069_0007,
DataStore_InvalidCheckToken = 0x0069_0008,
DataStore_SystemFileError = 0x0069_0009,
DataStore_OverCapacity = 0x0069_000A,
DataStore_OperationNotAllowed = 0x0069_000B,
DataStore_InvalidPassword = 0x0069_000C,
DataStore_ValueNotEqual = 0x0069_000D,
ServiceItem_Unknown = 0x006C_0001,
ServiceItem_InvalidArgument = 0x006C_0002,
ServiceItem_EShopUnknownHttpError = 0x006C_0003,
ServiceItem_EShopResponseParseError = 0x006C_0004,
ServiceItem_NotOwned = 0x006C_0005,
ServiceItem_InvalidLimitationType = 0x006C_0006,
ServiceItem_ConsumptionRightShortage = 0x006C_0007,
MatchmakeReferee_Unknown = 0x006F_0001,
MatchmakeReferee_InvalidArgument = 0x006F_0002,
MatchmakeReferee_AlreadyExists = 0x006F_0003,
MatchmakeReferee_NotParticipatedGathering = 0x006F_0004,
MatchmakeReferee_NotParticipatedRound = 0x006F_0005,
MatchmakeReferee_StatsNotFound = 0x006F_0006,
MatchmakeReferee_RoundNotFound = 0x006F_0007,
MatchmakeReferee_RoundArbitrated = 0x006F_0008,
MatchmakeReferee_RoundNotArbitrated = 0x006F_0009,
Subscriber_Unknown = 0x0070_0001,
Subscriber_InvalidArgument = 0x0070_0002,
Subscriber_OverLimit = 0x0070_0003,
Subscriber_PermissionDenied = 0x0070_0004,
Ranking2_Unknown = 0x0071_0001,
Ranking2_InvalidArgument = 0x0071_0002,
Ranking2_InvalidScore = 0x0071_0003,
SmartDeviceVoiceChat_Unknown = 0x0072_0001,
SmartDeviceVoiceChat_InvalidArgument = 0x0072_0002,
SmartDeviceVoiceChat_InvalidResponse = 0x0072_0003,
SmartDeviceVoiceChat_InvalidAccessToken = 0x0072_0004,
SmartDeviceVoiceChat_Unauthorized = 0x0072_0005,
SmartDeviceVoiceChat_AccessError = 0x0072_0006,
SmartDeviceVoiceChat_UserNotFound = 0x0072_0007,
SmartDeviceVoiceChat_RoomNotFound = 0x0072_0008,
SmartDeviceVoiceChat_RoomNotActivated = 0x0072_0009,
SmartDeviceVoiceChat_ApplicationNotSupported = 0x0072_000A,
SmartDeviceVoiceChat_InternalServerError = 0x0072_000B,
SmartDeviceVoiceChat_ServiceUnavailable = 0x0072_000C,
SmartDeviceVoiceChat_UnexpectedError = 0x0072_000D,
SmartDeviceVoiceChat_UnderMaintenance = 0x0072_000E,
SmartDeviceVoiceChat_ServiceNoLongerAvailable = 0x0072_000F,
SmartDeviceVoiceChat_AccountTemporarilyDisabled = 0x0072_0010,
SmartDeviceVoiceChat_PermissionDenied = 0x0072_0011,
SmartDeviceVoiceChat_NetworkServiceLicenseRequired = 0x0072_0012,
SmartDeviceVoiceChat_AccountLibraryError = 0x0072_0013,
SmartDeviceVoiceChat_GameModeNotFound = 0x0072_0014,
Screening_Unknown = 0x0073_0001,
Screening_InvalidArgument = 0x0073_0002,
Screening_NotFound = 0x0073_0003,
Custom_Unknown = 0x0074_0001,
Ess_Unknown = 0x0075_0001,
Ess_GameSessionError = 0x0075_0002,
Ess_GameSessionMaintenance = 0x0075_0003,
}
impl From<Error> for ErrorCode {
fn from(value: Error) -> Self {
error!("rmc error occurred during method runtime: {}", value);
Self::Core_InvalidArgument
}
}
impl Into<u32> for ErrorCode {
fn into(self) -> u32 {
unsafe { transmute(self) }
}
}

126
rnex-rmc/src/rmc_struct.rs Normal file
View file

@ -0,0 +1,126 @@
#[cfg(not(feature = "rmc_struct_header"))]
use std::io::Read;
use std::{
fmt::Arguments,
io::{self, ErrorKind, IoSlice, Write},
};
use crate::serialization::Result;
#[cfg(feature = "rmc_struct_header")]
pub const HEADER_SIZE: u32 = 0;
#[cfg(not(feature = "rmc_struct_header"))]
pub const HEADER_SIZE: u32 = 5;
pub struct OnlyWriteVec<'a>(&'a mut Vec<u8>);
impl Write for OnlyWriteVec<'_> {
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.0.write_all(buf)
}
fn write_fmt(&mut self, args: Arguments<'_>) -> io::Result<()> {
self.0.write_fmt(args)
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.0.write_vectored(bufs)
}
}
#[cfg(feature = "rmc_struct_header")]
pub fn write_struct<T: Write + ?Sized>(
writer: &mut T,
version: u8,
inner_size: u32,
pred: impl FnOnce(&mut T) -> Result<()>,
) -> Result<()> {
use bytemuck::bytes_of;
writer.write_all(&[version])?;
writer.write_all(bytes_of(&inner_size))?;
(pred)(writer)?;
Ok(())
}
#[cfg(not(feature = "rmc_struct_header"))]
pub fn write_struct<T: Write + ?Sized>(
writer: &mut T,
_version: u8,
_inner_size: u32,
pred: impl FnOnce(&mut T) -> Result<()>,
) -> Result<()> {
pred(writer)
}
pub struct SubRead<'a, T: Read + ?Sized> {
left_to_read: usize,
origin: &'a mut T,
}
impl<'a, T: Read + ?Sized> SubRead<'a, T> {
pub const fn new(origin: &'a mut T, left_to_read: usize) -> Self {
Self {
left_to_read,
origin,
}
}
}
impl<T: Read + ?Sized> Read for SubRead<'_, T> {
#[inline(always)]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let max_read = usize::max(self.left_to_read, buf.len());
let read = self.origin.read(&mut buf[..max_read])?;
self.left_to_read -= read;
Ok(read)
}
#[inline(always)]
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
if buf.len() > self.left_to_read {
return Err(io::Error::new(
ErrorKind::UnexpectedEof,
"Would run over end of SubRead",
));
}
self.left_to_read -= buf.len();
self.origin.read_exact(buf)
}
}
#[cfg(feature = "rmc_struct_header")]
pub fn read_struct<T: Sized, R: Read + ?Sized>(
mut reader: &mut R,
version: u8,
pred: impl FnOnce(&mut SubRead<R>) -> Result<T>,
) -> Result<T> {
use crate::rmc::structures::Error::VersionMismatch;
use v_byte_helpers::IS_BIG_ENDIAN;
use v_byte_helpers::ReadExtensions;
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)?;
Ok(pred(&mut SubRead::new(reader, size as usize))?)
}
#[cfg(not(feature = "rmc_struct_header"))]
pub fn read_struct<T: Sized, R: Read + ?Sized>(
mut reader: &mut R,
_version: u8,
pred: impl FnOnce(&mut R) -> Result<T>,
) -> Result<T> {
Ok(pred(&mut reader)?)
}

View file

@ -0,0 +1,99 @@
use std::{
fmt,
io::{self, Read, Write},
string::FromUtf8Error,
};
use crate::helpers::DummyWriter;
use thiserror::Error;
#[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),
#[cfg(feature = "rmc_struct_header")]
#[error("version mismatch: {0}")]
VersionMismatch(u8),
#[error("an error occurred reading the station url")]
StationUrlInvalid,
#[error("error formatting text: {0}")]
FormatError(#[from] fmt::Error),
#[error("tried to validate inheritance chain")]
InheritanceError,
#[error("uncategorized rmc error occurred: {0}")]
Other(Box<dyn std::error::Error + Send + Sync>),
#[error("unexpected out of bounds read/write")]
OOB,
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
pub trait RmcSerialize {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()>;
fn serialize_write_size(&self) -> Result<u32> {
let mut dummy = DummyWriter::new();
self.serialize(&mut dummy)?;
Ok(dummy.get_total_len())
}
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self>
where
Self: Sized;
fn to_data(&self) -> Result<Vec<u8>> {
let expected_size = self.serialize_write_size()?;
let mut data = Vec::with_capacity(expected_size as usize);
self.serialize(&mut data)?;
debug_assert_eq!(expected_size, data.len() as u32);
Ok(data)
}
fn version() -> Option<u8> {
None
}
}
impl RmcSerialize for () {
fn serialize(&self, _writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(())
}
fn deserialize(_reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(())
}
fn serialize_write_size(&self) -> Result<u32> {
Ok(0)
}
}
pub struct RmcStructInfo {
// this may never be locked after initialization
pub inheritors: std::sync::RwLock<Vec<&'static RmcStructInfo>>,
pub name: &'static str,
}
impl RmcStructInfo {
pub fn is_inheritor(&self, name: &str) -> bool {
if name == self.name {
return true;
}
let inheritors = self.inheritors.read().expect("poisoned");
for inheritor in inheritors.iter() {
if inheritor.is_inheritor(name) {
return true;
}
}
return false;
}
}
pub trait RmcStruct: RmcSerialize {
fn get_struct_info() -> &'static RmcStructInfo;
}

View file

@ -0,0 +1,30 @@
use std::fmt::Write;
use std::io::{self, Read};
use rnex_util::station_url::StationUrl;
use crate::{
helpers::DummyFormatWriter,
serialization::{Error::StationUrlInvalid, Result, RmcSerialize},
};
impl RmcSerialize for StationUrl {
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
let str = String::deserialize(reader)?;
Self::try_from(str.as_str()).map_err(|_| StationUrlInvalid)
}
fn serialize(&self, writer: &mut (impl io::Write + ?Sized)) -> Result<()> {
let str: String = self.into();
str.serialize(writer)
}
fn serialize_write_size(&self) -> Result<u32> {
let mut dummy = DummyFormatWriter::new();
write!(&mut dummy, "{}", self)?;
Ok(dummy.serialize_str_len())
}
}

48
rnex-rmc/src/string.rs Normal file
View file

@ -0,0 +1,48 @@
use bytemuck::bytes_of;
use std::io::{Read, Write};
use tracing::error;
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use crate::serialization::{Result, RmcSerialize};
impl RmcSerialize for String {
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
let len: u16 = reader.read_struct(IS_BIG_ENDIAN)?;
if len == 0 {
return Ok("".to_string());
}
let mut data = vec![0; len as usize];
reader.read_exact(&mut data)?;
if *data.last().unwrap() != 0 {
error!("unable to find null terminator... continuing anyways");
}
data.pop();
Ok(String::from_utf8(data)?)
}
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
(&self[..]).serialize(writer)
}
fn serialize_write_size(&self) -> Result<u32> {
(&self[..]).serialize_write_size()
}
}
impl RmcSerialize for &str {
fn deserialize(_reader: &mut (impl Read + ?Sized)) -> Result<Self> {
panic!("cannot serialize to &str")
}
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
let u16_len: u16 = (self.len() + 1) as u16;
writer.write_all(bytes_of(&u16_len))?;
writer.write_all(self.as_bytes())?;
writer.write_all(&[0])?;
Ok(())
}
#[inline(always)]
fn serialize_write_size(&self) -> Result<u32> {
Ok(2 + self.as_bytes().len() as u32 + 1)
}
}

100
rnex-rmc/src/string_set.rs Normal file
View file

@ -0,0 +1,100 @@
use std::{
collections::HashSet,
hash::Hash,
io::{Read, Write},
str::FromStr,
string::ToString,
};
use crate::serialization::{Error, Result, RmcSerialize};
#[derive(Debug, Clone)]
pub struct StringSet<T: FromStr + ToString + Eq>(pub HashSet<T>)
where
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static;
impl<T: FromStr + ToString + Eq + Hash> PartialEq for StringSet<T>
where
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
{
fn eq(&self, other: &Self) -> bool {
self.0.iter().eq(&other.0)
}
}
impl<T: FromStr + ToString + Eq + Hash> ToString for StringSet<T>
where
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
{
fn to_string(&self) -> String {
self.0
.iter()
.map(ToString::to_string)
.reduce(|a, b| format!("{}|{}", a, b))
.unwrap_or(String::new())
}
}
impl<T: FromStr + ToString + Eq + Hash> FromStr for StringSet<T>
where
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
{
type Err = Box<dyn std::error::Error + Send + Sync>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(
s.split("|")
.filter(|v| !v.is_empty())
.map(T::from_str)
.try_fold(
HashSet::new(),
|mut a, b| -> Result<HashSet<T>, Self::Err> {
a.insert(b.map_err(Box::new)?);
Ok(a)
},
)?,
))
}
}
impl<T: FromStr + ToString + Eq + Hash> RmcSerialize for StringSet<T>
where
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
{
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self>
where
Self: Sized,
{
Self::from_str(&String::deserialize(reader)?).map_err(Error::Other)
}
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.to_string().serialize(writer)
}
fn serialize_write_size(&self) -> Result<u32> {
self.to_string().serialize_write_size()
}
}
#[cfg(test)]
mod test {
use crate::string_set::StringSet;
use std::str::FromStr;
#[test]
fn test() {
let str_val = "0|100|200|10|110|210|20|120|220|30|130|230";
let set: StringSet<u32> = StringSet::from_str(str_val).unwrap();
let string_2 = set.to_string();
let reset: StringSet<u32> = StringSet::from_str(&string_2).unwrap();
for val in &set.0 {
if !reset.0.contains(&val) {
panic!("sets arent equivalent");
}
}
let _: StringSet<u32> = StringSet::from_str("").unwrap();
let _: StringSet<u32> = StringSet::from_str("10").unwrap();
}
}

66
rnex-rmc/src/variant.rs Normal file
View file

@ -0,0 +1,66 @@
use std::io::{Read, Write};
use rnex_util::date_time::DateTime;
use crate::serialization::{Error, Result, RmcSerialize};
#[derive(Debug, Clone, Default, PartialEq)]
pub enum Variant {
#[default]
None,
SInt64(i64),
Double(f64),
Bool(bool),
String(String),
DateTime(DateTime),
UInt64(u64),
}
impl RmcSerialize for Variant {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> 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 (impl Read + ?Sized)) -> 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(DateTime::deserialize(reader)?)),
6 => Ok(Variant::UInt64(u64::deserialize(reader)?)),
v => Err(Error::UnexpectedValue(v as u64)),
}
}
}