diff options
author | Lizzy Fleckenstein <eliasfleckenstein@web.de> | 2022-12-22 18:34:23 +0100 |
---|---|---|
committer | Lizzy Fleckenstein <eliasfleckenstein@web.de> | 2022-12-22 18:34:23 +0100 |
commit | 774d5c3d8af304e67297cc476aa51d95d0555072 (patch) | |
tree | dca76cb110353d6e7f55e68b4932fd021da09784 /src/main.rs | |
download | mt_rudp-774d5c3d8af304e67297cc476aa51d95d0555072.tar.xz |
a
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..bda3f38 --- /dev/null +++ b/src/main.rs @@ -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), + } + } +} |