initial commit
This commit is contained in:
commit
0ed05f6116
15 changed files with 645 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
5
.idea/.gitignore
generated
vendored
Normal file
5
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
14
.idea/discord.xml
generated
Normal file
14
.idea/discord.xml
generated
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="ASK" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
<option name="applicationTheme" value="default" />
|
||||||
|
<option name="iconsTheme" value="default" />
|
||||||
|
<option name="button1Title" value="" />
|
||||||
|
<option name="button1Url" value="" />
|
||||||
|
<option name="button2Title" value="" />
|
||||||
|
<option name="button2Url" value="" />
|
||||||
|
<option name="customApplicationId" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/splatoon-server-rust.iml" filepath="$PROJECT_DIR$/.idea/splatoon-server-rust.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
11
.idea/splatoon-server-rust.iml
generated
Normal file
11
.idea/splatoon-server-rust.iml
generated
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
86
Cargo.lock
generated
Normal file
86
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dotenv"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||||
|
|
||||||
|
[[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 = "splatoon-server-rust"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"dotenv",
|
||||||
|
"once_cell",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.96"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "splatoon-server-rust"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bytemuck = "1.21.0"
|
||||||
|
dotenv = "0.15.0"
|
||||||
|
once_cell = "1.20.2"
|
||||||
|
thiserror = "2.0.11"
|
||||||
221
src/endianness.rs
Normal file
221
src/endianness.rs
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
use std::io;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use bytemuck::Pod;
|
||||||
|
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
pub const IS_LITTLE_ENDIAN: bool = true;
|
||||||
|
|
||||||
|
#[cfg(target_endian = "big")]
|
||||||
|
pub const IS_LITTLE_ENDIAN: bool = false;
|
||||||
|
|
||||||
|
pub const IS_BIG_ENDIAN: bool = !IS_LITTLE_ENDIAN;
|
||||||
|
|
||||||
|
pub mod little_endian{
|
||||||
|
use std::io;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read_u16(reader: &mut (impl Read + ?Sized)) -> io::Result<u16>{
|
||||||
|
let mut data = [0u8; 2];
|
||||||
|
|
||||||
|
reader.read_exact(&mut data)?;
|
||||||
|
|
||||||
|
Ok(((data[0] as u16) << 8) | (data[1] as u16))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read_u32(reader: &mut (impl Read + ?Sized)) -> io::Result<u32>{
|
||||||
|
let mut data = [0u8; 4];
|
||||||
|
|
||||||
|
reader.read_exact(&mut data)?;
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
((data[0] as u32) << 24) |
|
||||||
|
((data[1] as u32) << 16) |
|
||||||
|
((data[2] as u32) << 8) |
|
||||||
|
(data[3] as u32)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StructMultiReadIter<'a, T: Pod + SwapEndian>{
|
||||||
|
reader: &'a mut dyn Read,
|
||||||
|
left_to_read: usize,
|
||||||
|
swap_endian: bool,
|
||||||
|
_phantom_data: PhantomData<&'static T>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Pod + SwapEndian> Iterator for StructMultiReadIter<'a, T>{
|
||||||
|
type Item = io::Result<T>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.left_to_read == 0{
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.reader.read_struct(self.swap_endian))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Pod + SwapEndian> Drop for StructMultiReadIter<'a, T>{
|
||||||
|
#[inline]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
|
||||||
|
// read all the structs we would be reading and discard them to make the result after using
|
||||||
|
// this always be the same
|
||||||
|
while let Some(_) = self.next() { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub trait ReadExtensions: Read{
|
||||||
|
#[inline]
|
||||||
|
fn read_le_u16(&mut self) -> io::Result<u16>{
|
||||||
|
little_endian::read_u16(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_le_u32(&mut self) -> io::Result<u32>{
|
||||||
|
little_endian::read_u32(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_le_struct<T: Pod + SwapEndian>(&mut self) -> io::Result<T>{
|
||||||
|
let mut data = T::zeroed();
|
||||||
|
let bytes = bytemuck::bytes_of_mut(&mut data);
|
||||||
|
|
||||||
|
self.read_exact(bytes)?;
|
||||||
|
|
||||||
|
if cfg!(not(target_endian = "little")){
|
||||||
|
data = data.swap_endian();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_struct<T: Pod + SwapEndian>(&mut self, swap_endian: bool) -> io::Result<T>{
|
||||||
|
let mut data = T::zeroed();
|
||||||
|
let bytes = bytemuck::bytes_of_mut(&mut data);
|
||||||
|
|
||||||
|
self.read_exact(bytes)?;
|
||||||
|
|
||||||
|
if swap_endian{
|
||||||
|
data = data.swap_endian();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn read_struct_multi<T: Pod + SwapEndian>(&mut self, swap_endian: bool, count: usize) -> io::Result<StructMultiReadIter<T>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Read> ReadExtensions for T{
|
||||||
|
// i was forced to put this here because it requires info about self
|
||||||
|
#[inline]
|
||||||
|
fn read_struct_multi<U: Pod + SwapEndian>(&mut self, swap_endian: bool, count: usize) -> io::Result<StructMultiReadIter<'_, U>>{
|
||||||
|
Ok(StructMultiReadIter{
|
||||||
|
reader: self,
|
||||||
|
swap_endian,
|
||||||
|
left_to_read: count,
|
||||||
|
_phantom_data: Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub trait SwapEndian: Clone + Copy{
|
||||||
|
fn swap_endian(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwapEndian for u8{
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwapEndian for u16{
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(self) -> Self {
|
||||||
|
self.swap_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SwapEndian for u32{
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(self) -> Self {
|
||||||
|
self.swap_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwapEndian for u64{
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(self) -> Self {
|
||||||
|
self.swap_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwapEndian for i8{
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwapEndian for i16{
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(self) -> Self {
|
||||||
|
self.swap_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SwapEndian for i32{
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(self) -> Self {
|
||||||
|
self.swap_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwapEndian for i64{
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(self) -> Self {
|
||||||
|
self.swap_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SwapEndian, U: SwapEndian> SwapEndian for (T, U){
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(self) -> Self {
|
||||||
|
(self.0.swap_endian(), self.1.swap_endian())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SwapEndian, U: SwapEndian, V: SwapEndian> SwapEndian for (T, U, V){
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(self) -> Self {
|
||||||
|
(self.0.swap_endian(), self.1.swap_endian(), self.2.swap_endian())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SwapEndian, U: SwapEndian, V: SwapEndian, W: SwapEndian> SwapEndian for (T, U, V, W){
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(self) -> Self {
|
||||||
|
(self.0.swap_endian(), self.1.swap_endian(), self.2.swap_endian(), self.3.swap_endian())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SwapEndian, const size: usize> SwapEndian for [T; size]{
|
||||||
|
#[inline]
|
||||||
|
fn swap_endian(mut self) -> Self {
|
||||||
|
for elem in &mut self{
|
||||||
|
*elem = elem.swap_endian();
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/main.rs
Normal file
6
src/main.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
mod endianness;
|
||||||
|
mod prudp;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
0
src/prudp/auth_module.rs
Normal file
0
src/prudp/auth_module.rs
Normal file
3
src/prudp/endpoint.rs
Normal file
3
src/prudp/endpoint.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub struct Endpoint{
|
||||||
|
|
||||||
|
}
|
||||||
4
src/prudp/mod.rs
Normal file
4
src/prudp/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod packet;
|
||||||
|
mod server;
|
||||||
|
mod endpoint;
|
||||||
|
mod auth_module;
|
||||||
228
src/prudp/packet.rs
Normal file
228
src/prudp/packet.rs
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::hint::unreachable_unchecked;
|
||||||
|
use std::io;
|
||||||
|
use std::io::{Cursor, ErrorKind, Read, Seek};
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use thiserror::Error;
|
||||||
|
use v_byte_macros::{EnumTryInto, SwapEndian};
|
||||||
|
use crate::endianness::{IS_BIG_ENDIAN, IS_LITTLE_ENDIAN, ReadExtensions};
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error{
|
||||||
|
#[error("{0}")]
|
||||||
|
IO(#[from] io::Error),
|
||||||
|
#[error("invalid magic {0:#06x}")]
|
||||||
|
InvalidMagic(u16),
|
||||||
|
#[error("invalid version {0}")]
|
||||||
|
InvalidVersion(u8),
|
||||||
|
#[error("invalid option id {0}")]
|
||||||
|
InvalidOptionId(u8),
|
||||||
|
#[error("option size {size} doesnt match expected option for given option id {id}")]
|
||||||
|
InvalidOptionSize{
|
||||||
|
id: u8,
|
||||||
|
size: u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Copy, Clone, Pod, Zeroable, SwapEndian)]
|
||||||
|
pub struct TypesFlags(u16);
|
||||||
|
|
||||||
|
impl TypesFlags{
|
||||||
|
pub fn get_types(self) -> u8 {
|
||||||
|
(self.0 & 0x000F) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_flags(self) -> u16 {
|
||||||
|
(self.0 & 0xFFF0) >> 4
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn types(self, val: u8) -> Self {
|
||||||
|
Self((self.0 & 0xFFF0) | (val as u16 & 0x000F))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flags(self, val: u16) -> Self {
|
||||||
|
Self((self.0 & 0x000F) | ((val << 4) & 0xFFF0) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for TypesFlags{
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let stream_type = self.get_types();
|
||||||
|
let port_number = self.get_flags();
|
||||||
|
write!(f, "TypesFlags{{ types: {}, flags: {} }}", stream_type, port_number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Copy, Clone, Pod, Zeroable, SwapEndian)]
|
||||||
|
pub struct VirtualPort(u8);
|
||||||
|
|
||||||
|
impl VirtualPort{
|
||||||
|
pub fn get_stream_type(self) -> u8 {
|
||||||
|
(self.0 & 0x0F) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_port_number(self) -> u8 {
|
||||||
|
(self.0 & 0xF0) >> 4
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stream_type(self, val: u8) -> Self {
|
||||||
|
let masked_val = val & 0x0F;
|
||||||
|
assert_eq!(masked_val, val);
|
||||||
|
|
||||||
|
Self((self.0 & 0xF0) | masked_val)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn port_number(self, val: u8) -> Self {
|
||||||
|
let masked_val = val & 0x0F;
|
||||||
|
assert_eq!(masked_val, val);
|
||||||
|
|
||||||
|
Self((self.0 & 0x0F) | (masked_val << 4))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for VirtualPort{
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let stream_type = self.get_stream_type();
|
||||||
|
let port_number = self.get_port_number();
|
||||||
|
write!(f, "VirtualPort{{ stream_type: {}, port_number: {} }}", stream_type, port_number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian)]
|
||||||
|
pub struct PRUDPHeader{
|
||||||
|
magic: [u8; 2],
|
||||||
|
version: u8,
|
||||||
|
pub packet_specific_size: u8,
|
||||||
|
pub payload_size: u16,
|
||||||
|
pub source_port: VirtualPort,
|
||||||
|
pub destination_port: VirtualPort,
|
||||||
|
pub types_and_flags: TypesFlags,
|
||||||
|
pub session_id: u8,
|
||||||
|
pub substream_id: u8,
|
||||||
|
pub sequence_id: u16,
|
||||||
|
}
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(EnumTryInto)]
|
||||||
|
enum PacketSpecificData{
|
||||||
|
E = 0x10
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PRUDPPacket{
|
||||||
|
pub header: PRUDPHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
// Invariant: can only contain 0, 1, 2, 3 or 4
|
||||||
|
struct OptionId(u8);
|
||||||
|
|
||||||
|
impl OptionId{
|
||||||
|
fn new(val: u8) -> Result<Self>{
|
||||||
|
// Invariant is upheld because we only create the object if it doesn't violate the invariant
|
||||||
|
match val {
|
||||||
|
0 | 1 | 2 | 3 | 4 => Ok(Self(val)),
|
||||||
|
_ => Err(Error::InvalidOptionId(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn option_type_size(self) -> u8{
|
||||||
|
match self.0{
|
||||||
|
0 => 4,
|
||||||
|
1 => 16,
|
||||||
|
2 => 1,
|
||||||
|
3 => 2,
|
||||||
|
4 => 1,
|
||||||
|
// Getting here would mean that the invariant has been violated, thus this isnt my
|
||||||
|
// problem lmao
|
||||||
|
_ => unsafe { unreachable_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<u8> for OptionId{
|
||||||
|
fn into(self) -> u8 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PRUDPPacket{
|
||||||
|
pub fn new(reader: &mut (impl Read + Seek)) -> Result<Self>{
|
||||||
|
let header: PRUDPHeader = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||||
|
|
||||||
|
if header.magic[0] != 0xEA ||
|
||||||
|
header.magic[1] != 0xD0{
|
||||||
|
return Err(Error::InvalidMagic(u16::from_be_bytes(header.magic)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if header.version != 1{
|
||||||
|
return Err(Error::InvalidVersion(header.version))
|
||||||
|
}
|
||||||
|
|
||||||
|
//discard it for now
|
||||||
|
let _: [u8; 16] = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||||
|
|
||||||
|
assert_eq!(reader.stream_position().ok(), Some(14+16));
|
||||||
|
|
||||||
|
let mut packet_specific_buffer = vec![0u8; header.packet_specific_size as usize];
|
||||||
|
|
||||||
|
reader.read_exact(&mut packet_specific_buffer)?;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//no clue whats up with options but they are broken
|
||||||
|
/*let mut packet_specific_data_cursor = Cursor::new(&packet_specific_buffer);
|
||||||
|
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let Ok(option_id): io::Result<u8> = packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN) else {
|
||||||
|
break
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(value_size): io::Result<u8> = packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN) else {
|
||||||
|
break
|
||||||
|
};
|
||||||
|
|
||||||
|
if value_size == 0 {
|
||||||
|
// skip it if its 0 and dont check?
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let option_id: OptionId = OptionId::new(option_id)?;
|
||||||
|
|
||||||
|
if option_id.option_type_size() != value_size{
|
||||||
|
return Err(Error::InvalidOptionSize {
|
||||||
|
size: value_size,
|
||||||
|
id: option_id.0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut option_data = vec![0u8,value_size];
|
||||||
|
if packet_specific_data_cursor.read_exact(&mut option_data).is_err(){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
let mut packet_payload = vec![0u8; header.payload_size as usize];
|
||||||
|
|
||||||
|
reader.read_exact(&mut packet_payload)?;
|
||||||
|
|
||||||
|
Ok(Self{
|
||||||
|
header
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test{
|
||||||
|
use super::{PRUDPHeader};
|
||||||
|
#[test]
|
||||||
|
fn size_test(){
|
||||||
|
assert_eq!(size_of::<PRUDPHeader>(), 14);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/prudp/server.rs
Normal file
42
src/prudp/server.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use crate::prudp::endpoint::Endpoint;
|
||||||
|
|
||||||
|
pub struct NexServer{
|
||||||
|
pub endpoints: Mutex<Vec<Endpoint>>,
|
||||||
|
_no_outside_construction: PhantomData<()>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NexServer{
|
||||||
|
fn server_thread_entry(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Arc<Self>{
|
||||||
|
let own_impl = NexServer{
|
||||||
|
endpoints: Default::default(),
|
||||||
|
_no_outside_construction: Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let arc = Arc::new(own_impl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test{
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use crate::prudp::server::{NexServer};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test(){
|
||||||
|
let server = NexServer::new();
|
||||||
|
|
||||||
|
let a = (server.deref()).clone();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue