deepslate-protocol 0.3.1

Minecraft protocol primitives for the Deepslate proxy.
Documentation
//! Status (server list ping) packets.

use bytes::{Buf, BufMut};

use crate::types::{self, ProtocolError};

use super::Packet;

/// Serverbound status request (packet ID 0x00 in STATUS state).
/// This packet has no fields.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StatusRequestPacket;

impl Packet for StatusRequestPacket {
    const PACKET_ID: i32 = 0x00;

    fn decode(_buf: &mut impl Buf) -> Result<Self, ProtocolError> {
        Ok(Self)
    }

    fn encode(&self, _buf: &mut impl BufMut) {}
}

/// Clientbound status response (packet ID 0x00 in STATUS state).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StatusResponsePacket {
    /// JSON string containing the server status.
    pub json: String,
}

impl Packet for StatusResponsePacket {
    const PACKET_ID: i32 = 0x00;

    fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
        let json = types::read_string(buf)?;
        Ok(Self { json })
    }

    fn encode(&self, buf: &mut impl BufMut) {
        types::write_string(buf, &self.json);
    }
}

/// Serverbound ping request (packet ID 0x01 in STATUS state).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PingRequestPacket {
    /// Arbitrary payload echoed by the server.
    pub payload: i64,
}

impl Packet for PingRequestPacket {
    const PACKET_ID: i32 = 0x01;

    fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
        if buf.remaining() < 8 {
            return Err(ProtocolError::UnexpectedEof);
        }
        Ok(Self {
            payload: buf.get_i64(),
        })
    }

    fn encode(&self, buf: &mut impl BufMut) {
        buf.put_i64(self.payload);
    }
}

/// Clientbound pong response (packet ID 0x01 in STATUS state).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PongResponsePacket {
    /// The payload echoed from the ping request.
    pub payload: i64,
}

impl Packet for PongResponsePacket {
    const PACKET_ID: i32 = 0x01;

    fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
        if buf.remaining() < 8 {
            return Err(ProtocolError::UnexpectedEof);
        }
        Ok(Self {
            payload: buf.get_i64(),
        })
    }

    fn encode(&self, buf: &mut impl BufMut) {
        buf.put_i64(self.payload);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn status_request_roundtrip(packet in Just(StatusRequestPacket)) {
            let mut buf = Vec::new();
            packet.encode(&mut buf);
            let decoded = StatusRequestPacket::decode(&mut &buf[..]).unwrap();
            prop_assert_eq!(decoded, packet);
        }

        #[test]
        fn status_response_roundtrip(json in any::<String>()) {
            let json = if json.len() > 1024 { json[..1024].to_string() } else { json };
            let packet = StatusResponsePacket { json };
            let mut buf = Vec::new();
            packet.encode(&mut buf);
            let decoded = StatusResponsePacket::decode(&mut &buf[..]).unwrap();
            prop_assert_eq!(decoded, packet);
        }

        #[test]
        fn ping_request_roundtrip(payload in any::<i64>()) {
            let packet = PingRequestPacket { payload };
            let mut buf = Vec::new();
            packet.encode(&mut buf);
            let decoded = PingRequestPacket::decode(&mut &buf[..]).unwrap();
            prop_assert_eq!(decoded, packet);
        }

        #[test]
        fn pong_response_roundtrip(payload in any::<i64>()) {
            let packet = PongResponsePacket { payload };
            let mut buf = Vec::new();
            packet.encode(&mut buf);
            let decoded = PongResponsePacket::decode(&mut &buf[..]).unwrap();
            prop_assert_eq!(decoded, packet);
        }
    }
}