]> git.lizzy.rs Git - mt_rudp.git/commitdiff
a
authorLizzy Fleckenstein <eliasfleckenstein@web.de>
Thu, 22 Dec 2022 17:34:23 +0000 (18:34 +0100)
committerLizzy Fleckenstein <eliasfleckenstein@web.de>
Thu, 22 Dec 2022 17:34:23 +0000 (18:34 +0100)
.gitignore [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
src/main.rs [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..4fffb2f
--- /dev/null
@@ -0,0 +1,2 @@
+/target
+/Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..bedb394
--- /dev/null
@@ -0,0 +1,8 @@
+[package]
+name = "mt_rudp"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+byteorder = "1.4.3"
+num_enum = "0.5.7"
diff --git a/src/main.rs b/src/main.rs
new file mode 100644 (file)
index 0000000..bda3f38
--- /dev/null
@@ -0,0 +1,267 @@
+#![feature(yeet_expr)]
+#![feature(cursor_remaining)]
+use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
+use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
+use std::{
+    cell::Cell,
+    fmt,
+    io::{self, Write},
+    net,
+    sync::{mpsc, Arc},
+    thread,
+};
+
+pub const PROTO_ID: u32 = 0x4f457403;
+pub const UDP_PKT_SIZE: usize = 512;
+pub const NUM_CHANNELS: usize = 3;
+pub const REL_BUFFER: usize = 0x8000;
+pub const INIT_SEQNUM: u16 = 65500;
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum PeerID {
+    Nil = 0,
+    Srv,
+    CltMin,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, TryFromPrimitive)]
+#[repr(u8)]
+pub enum PktType {
+    Ctl = 0,
+    Orig,
+    Split,
+    Rel,
+}
+
+#[derive(Debug)]
+pub struct Pkt<T> {
+    unrel: bool,
+    chan: u8,
+    data: T,
+}
+
+#[derive(Debug)]
+pub enum Error {
+    IoError(io::Error),
+    InvalidProtoId(u32),
+    InvalidPeerID,
+    InvalidChannel(u8),
+    InvalidType(u8),
+    LocalHangup,
+}
+
+impl From<io::Error> for Error {
+    fn from(err: io::Error) -> Self {
+        Self::IoError(err)
+    }
+}
+
+impl From<TryFromPrimitiveError<PktType>> for Error {
+    fn from(err: TryFromPrimitiveError<PktType>) -> Self {
+        Self::InvalidType(err.number)
+    }
+}
+
+impl From<mpsc::SendError<PktResult>> for Error {
+    fn from(err: mpsc::SendError<PktResult>) -> Self {
+        Self::LocalHangup
+    }
+}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use Error::*;
+        write!(f, "RUDP Error: ")?;
+
+        match self {
+            IoError(err) => write!(f, "IO Error: {}", err),
+            InvalidProtoId(id) => write!(f, "Invalid Protocol ID: {id}"),
+            InvalidPeerID => write!(f, "Invalid Peer ID"),
+            InvalidChannel(ch) => write!(f, "Invalid Channel: {ch}"),
+            InvalidType(tp) => write!(f, "Invalid Type: {tp}"),
+            LocalHangup => write!(f, "Local packet receiver hung up"),
+        }
+    }
+}
+
+#[derive(Debug)]
+struct Channel {}
+
+#[derive(Debug)]
+struct RecvChannel<'a> {
+    packets: Vec<Option<Vec<u8>>>, // used to be called char **
+    seqnum: u16,
+    chan: &'a Channel,
+}
+
+pub type PktResult = Result<Pkt<Vec<u8>>, Error>;
+type PktSender = mpsc::Sender<PktResult>;
+
+#[derive(Debug)]
+struct ConnInner {
+    sock: net::UdpSocket,
+    id: u16,
+    remote_id: u16,
+    chans: Vec<Channel>,
+}
+
+impl ConnInner {
+    pub fn send(&self, pkt: Pkt<&[u8]>) -> io::Result<()> {
+        let mut buf = Vec::with_capacity(4 + 2 + 1 + 1 + pkt.data.len());
+        buf.write_u32::<BigEndian>(PROTO_ID)?;
+        buf.write_u16::<BigEndian>(self.remote_id)?;
+        buf.write_u8(pkt.chan as u8)?;
+        buf.write_u8(PktType::Orig as u8)?;
+        buf.write(pkt.data)?;
+
+        self.sock.send(&buf)?;
+
+        Ok(())
+    }
+
+    fn recv_loop(&self, tx: PktSender) {
+        let mut inbox = [0; UDP_PKT_SIZE];
+
+        let mut recv_chans = self.channels.map(|chan| RecvChannel {
+            chan,
+            packets: (0..REL_BUFFER).map(|_| Cell::new(None)),
+            seqnum: INIT_SEQNUM,
+        });
+
+        loop {
+            if let Err(err) = self.recv_pkt(&mut inbox, &mut recv_chans, &tx) {
+                if !tx.send(Err(err)).is_ok() {
+                    break;
+                }
+            }
+        }
+    }
+
+    fn recv_pkt(
+        &self,
+        buffer: &mut [u8],
+        chans: &mut Vec<RecvChannel>,
+        tx: &PktSender,
+    ) -> Result<(), Error> {
+        use Error::*;
+        use PktType::*;
+
+        // todo: reset timeout
+        let len = self.sock.recv(buffer)?;
+        let mut cursor = io::Cursor::new(&buffer[..len]);
+
+        let proto_id = cursor.read_u32::<BigEndian>()?;
+        if proto_id != PROTO_ID {
+            do yeet InvalidProtoId(proto_id);
+        }
+
+        let peer_id = cursor.read_u16::<BigEndian>()?;
+
+        let n_channel = cursor.read_u8()?;
+        let mut channel = self
+            .chans
+            .get_mut(n_channel as usize)
+            .ok_or(InvalidChannel(n_channel))?;
+
+        self.process_pkt(cursor, channel);
+    }
+
+    fn process_pkt(
+        &self,
+        mut cursor: io::Cursor<&[u8]>,
+        chan: &mut RecvChannel,
+    ) -> Result<(), Error> {
+        match cursor.read_u8()?.try_into()? {
+            Ctl => {
+                dbg!(cursor.remaining_slice());
+            }
+            Orig => {
+                tx.send(Ok(Pkt {
+                    chan: n_channel,
+                    unrel: true,
+                    data: cursor.remaining_slice().into(),
+                }))?;
+            }
+            Split => {
+                dbg!(cursor.remaining_slice());
+            }
+            Rel => {
+                let seqnum = cursor.read_u16::<BigEndian>()?;
+                chan.packets[seqnum].set(cursor.remaining_slice().into());
+
+                while Some(pkt) = chan.packets[chan.seqnum].take() {
+                    self.process_pkt(io::Cursor::new(&pkt), chan)?;
+                    chan.seqnum.overflowing_add(1);
+                }
+            }
+        }
+
+        Ok(())
+    }
+}
+
+#[derive(Debug)]
+pub struct Conn {
+    inner: Arc<ConnInner>,
+    rx: mpsc::Receiver<PktResult>,
+}
+
+impl Conn {
+    pub fn connect(addr: &str) -> io::Result<Self> {
+        let (tx, rx) = mpsc::channel();
+
+        let inner = Arc::new(ConnInner {
+            sock: net::UdpSocket::bind("0.0.0.0:0")?,
+            id: PeerID::Srv as u16,
+            remote_id: PeerID::Nil as u16,
+            chans: (0..NUM_CHANNELS).map(|_| Channel {}).collect(),
+        });
+
+        inner.sock.connect(addr)?;
+
+        let recv_inner = Arc::clone(&inner);
+        thread::spawn(move || {
+            recv_inner.recv_loop(tx);
+        });
+
+        Ok(Conn { inner, rx })
+    }
+
+    pub fn send(&self, pkt: Pkt<&[u8]>) -> io::Result<()> {
+        self.inner.send(pkt)
+    }
+
+    pub fn recv(&self) -> Result<PktResult, mpsc::RecvError> {
+        self.rx.recv()
+    }
+}
+
+fn main() {
+    //println!("{}", x.deep_size_of());
+    let conn = Conn::connect("127.0.0.1:30000").expect("the spanish inquisition");
+
+    let mut mtpkt = vec![];
+    mtpkt.write_u16::<BigEndian>(2).unwrap(); // high level type
+    mtpkt.write_u8(29).unwrap(); // serialize ver
+    mtpkt.write_u16::<BigEndian>(0).unwrap(); // compression modes
+    mtpkt.write_u16::<BigEndian>(40).unwrap(); // MinProtoVer
+    mtpkt.write_u16::<BigEndian>(40).unwrap(); // MaxProtoVer
+    mtpkt.write_u16::<BigEndian>(3).unwrap(); // player name length
+    mtpkt.write(b"foo").unwrap(); // player name
+
+    conn.send(Pkt {
+        unrel: true,
+        chan: 1,
+        data: &mtpkt,
+    })
+    .unwrap();
+
+    while let Ok(result) = conn.recv() {
+        match result {
+            Ok(pkt) => {
+                io::stdout().write(pkt.data.as_slice()).unwrap();
+            }
+            Err(err) => eprintln!("Error: {}", err),
+        }
+    }
+}