edition = "2021"
[features]
-all = ["client", "server", "random", "serde"]
+all = ["client", "server", "random", "serde", "conn"]
client = []
+conn = ["dep:mt_rudp", "dep:thiserror"]
random = ["dep:generate-random", "dep:rand"]
serde = ["dep:serde", "dep:serde_arrays", "enumset/serde"]
server = []
test = ["client", "server", "random"]
[dependencies]
+delegate = "0.9.0"
enumset = { git = "https://github.com/Lymia/enumset" }
generate-random = { git = "https://github.com/minetest-rust/generate-random", features = ["enumset"], optional = true }
+mt_rudp = { git = "https://github.com/minetest-rust/mt_rudp", optional = true }
mt_ser = { git = "https://github.com/minetest-rust/mt_ser" }
rand = { version = "0.8.5", optional = true }
serde = { version = "1.0.152", features = ["derive"], optional = true }
serde_arrays = { version = "0.1.0", optional = true }
+thiserror = { version = "1.0.38", optional = true }
[dev-dependencies]
libtest-mimic = "0.6.0"
--- /dev/null
+use super::PktInfo;
+use delegate::delegate;
+use mt_ser::{DefCfg, MtDeserialize, MtSerialize};
+use std::{borrow::Cow, io};
+use thiserror::Error;
+
+pub trait Remote {
+ type UdpSender: mt_rudp::UdpSender;
+ type PktFrom: MtDeserialize;
+ type PktTo: MtSerialize + PktInfo;
+}
+
+#[cfg(feature = "client")]
+pub struct RemoteSrv;
+
+#[cfg(feature = "client")]
+impl Remote for RemoteSrv {
+ type UdpSender = mt_rudp::ToSrv;
+ type PktTo = crate::ToSrvPkt;
+ type PktFrom = crate::ToCltPkt;
+}
+
+#[cfg(feature = "client")]
+pub async fn connect(addr: &str) -> io::Result<(MtSender<RemoteSrv>, MtReceiver<RemoteSrv>)> {
+ let (tx, rx) = mt_rudp::connect(addr).await?;
+ Ok((MtSender(tx), MtReceiver(rx)))
+}
+
+/*
+
+pub struct RemoteClt;
+impl Remote for RemoteClt {
+ type Sender = mt_rudp::ToClt;
+ type To = crate::ToCltPkt;
+ type From = crate::ToSrvPkt;
+}
+
+*/
+
+pub struct MtSender<R: Remote>(pub mt_rudp::RudpSender<R::UdpSender>);
+pub struct MtReceiver<R: Remote>(pub mt_rudp::RudpReceiver<R::UdpSender>);
+
+#[derive(Error, Debug)]
+pub enum RecvError {
+ #[error("connection error: {0}")]
+ ConnError(#[from] mt_rudp::Error),
+ #[error("deserialize error: {0}")]
+ DeserializeError(#[from] mt_ser::DeserializeError),
+}
+
+#[derive(Error, Debug)]
+pub enum SendError {
+ #[error("connection error: {0}")]
+ ConnError(#[from] io::Error),
+ #[error("serialize error: {0}")]
+ SerializeError(#[from] mt_ser::SerializeError),
+}
+
+macro_rules! impl_delegate {
+ ($T:ident) => {
+ impl<R: Remote> $T<R> {
+ delegate! {
+ to self.0 {
+ pub async fn peer_id(&self) -> u16;
+ pub async fn is_server(&self) -> bool;
+ pub async fn close(self);
+ }
+ }
+ }
+ };
+}
+
+impl_delegate!(MtSender);
+impl_delegate!(MtReceiver);
+
+impl<R: Remote> MtReceiver<R> {
+ pub async fn recv(&mut self) -> Option<Result<R::PktFrom, RecvError>> {
+ self.0.recv().await.map(|res| {
+ res.map_err(RecvError::from).and_then(|pkt| {
+ // TODO: warn on trailing data
+ R::PktFrom::mt_deserialize::<DefCfg>(&mut io::Cursor::new(pkt.data))
+ .map_err(RecvError::from)
+ })
+ })
+ }
+}
+
+impl<R: Remote> MtSender<R> {
+ pub async fn send(&self, pkt: &R::PktTo) -> Result<(), SendError> {
+ let mut writer = Vec::new();
+ pkt.mt_serialize::<DefCfg>(&mut writer)?;
+
+ let (chan, unrel) = pkt.pkt_info();
+ self.0
+ .send(mt_rudp::Pkt {
+ chan,
+ unrel,
+ data: Cow::Borrowed(&writer),
+ })
+ .await?;
+
+ Ok(())
+ }
+}
flags: EnumSet<HudFlag>,
mask: EnumSet<HudFlag>,
} = 76,
- SetHotbarParam(HotbarParam) = 77,
+ HotbarParam(HotbarParam) = 77,
Breath {
breath: u16,
} = 78,
} = 97,
MinimapModes(MinimapModesPkt) = 98,
}
+
+impl PktInfo for ToCltPkt {
+ fn pkt_info(&self) -> (u8, bool) {
+ use ToCltPkt::*;
+
+ match self {
+ BlockData { .. } | Media { .. } => (2, true),
+ AddHud { .. }
+ | ChangeHud { .. }
+ | RemoveHud { .. }
+ | HudFlags { .. }
+ | HotbarParam(_) => (1, true),
+ _ => (0, true),
+ }
+ }
+}
#[mt(len = "u8")]
blocks: Vec<[i16; 3]>,
} = 37,
+ HaveMedia {
+ #[mt(len = "u8")]
+ tokens: Vec<u32>,
+ } = 41,
InvAction {
#[mt(len = "()")]
action: String,
#[mt(len = "(DefCfg, (DefCfg, u32))")]
fields: HashMap<String, String>,
} = 60,
- ReqMedia {
+ RequestMedia {
filenames: Vec<String>,
} = 64,
CltReady {
} = 82,
Disco = 0xffff,
}
+
+impl PktInfo for ToSrvPkt {
+ fn pkt_info(&self) -> (u8, bool) {
+ use ToSrvPkt::*;
+
+ match self {
+ Init { .. } => (1, false),
+ Init2 { .. }
+ | RequestMedia { .. }
+ | CltReady { .. }
+ | FirstSrp { .. }
+ | SrpBytesA { .. }
+ | SrpBytesM { .. } => (1, true),
+ PlayerPos { .. } => (0, false),
+ GotBlocks { .. } | HaveMedia { .. } | DeletedBlocks { .. } | RemovedSounds { .. } => {
+ (2, true)
+ }
+ _ => (0, true),
+ }
+ }
+}