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
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:
parent
7dd502aa7a
commit
4ff0a5efcc
187 changed files with 4526 additions and 5132 deletions
31
rnex-rmc/Cargo.toml
Normal file
31
rnex-rmc/Cargo.toml
Normal 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
160
rnex-rmc/macros/Cargo.lock
generated
Normal 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",
|
||||
]
|
||||
19
rnex-rmc/macros/Cargo.toml
Normal file
19
rnex-rmc/macros/Cargo.toml
Normal 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
159
rnex-rmc/macros/src/lib.rs
Normal 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
|
||||
}
|
||||
437
rnex-rmc/macros/src/protos.rs
Normal file
437
rnex-rmc/macros/src/protos.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
484
rnex-rmc/macros/src/rmc_struct.rs
Normal file
484
rnex-rmc/macros/src/rmc_struct.rs
Normal 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)
|
||||
}
|
||||
10
rnex-rmc/macros/src/util.rs
Normal file
10
rnex-rmc/macros/src/util.rs
Normal 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
103
rnex-rmc/src/any.rs
Normal 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
36
rnex-rmc/src/buffer.rs
Normal 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
41
rnex-rmc/src/data.rs
Normal 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
15
rnex-rmc/src/date_time.rs
Normal 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
53
rnex-rmc/src/helpers.rs
Normal 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
473
rnex-rmc/src/lib.rs
Normal 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
78
rnex-rmc/src/list.rs
Normal 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
105
rnex-rmc/src/message.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
94
rnex-rmc/src/networking.rs
Normal file
94
rnex-rmc/src/networking.rs
Normal 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
497
rnex-rmc/src/primitives.rs
Normal 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
29
rnex-rmc/src/qbuffer.rs
Normal 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
40
rnex-rmc/src/qresult.rs
Normal 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
470
rnex-rmc/src/response.rs
Normal 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
126
rnex-rmc/src/rmc_struct.rs
Normal 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)?)
|
||||
}
|
||||
99
rnex-rmc/src/serialization.rs
Normal file
99
rnex-rmc/src/serialization.rs
Normal 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;
|
||||
}
|
||||
30
rnex-rmc/src/station_url.rs
Normal file
30
rnex-rmc/src/station_url.rs
Normal 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
48
rnex-rmc/src/string.rs
Normal 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
100
rnex-rmc/src/string_set.rs
Normal 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
66
rnex-rmc/src/variant.rs
Normal 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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue