mc-query 2.0.0

Implementations of Server List Ping, Query, and RCON for minecraft servers
Documentation
use crate::{errors::MinecraftProtocolError, varint::VarInt};
use bytes::{BufMut, Bytes, BytesMut};

#[derive(Debug)]
pub(super) enum PacketId {
    Handshake = 0,
    Status = 1,
}

impl From<PacketId> for u8 {
    fn from(id: PacketId) -> Self {
        match id {
            PacketId::Handshake => 0,
            PacketId::Status => 1,
        }
    }
}

impl TryFrom<u8> for PacketId {
    type Error = MinecraftProtocolError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::Handshake),
            1 => Ok(Self::Status),
            _ => Err(MinecraftProtocolError::InvalidState),
        }
    }
}

impl From<PacketId> for VarInt {
    fn from(id: PacketId) -> Self {
        let number: u8 = id.into();
        VarInt::from(number as i32)
    }
}

#[derive(Debug)]
pub(super) struct Packet {
    id: u8,
    payload: Bytes,
}

impl Packet {
    pub fn builder(id: PacketId) -> PacketBuilder {
        PacketBuilder::new(id)
    }

    pub fn bytes(self) -> Bytes {
        self.into()
    }
}

impl From<Packet> for Bytes {
    fn from(packet: Packet) -> Self {
        let len: i32 = (VarInt::from(packet.id as i32).bytes().len() + packet.payload.len()) as i32;
        let mut bytes = BytesMut::new();

        bytes.extend_from_slice(&VarInt::from(len).bytes());
        bytes.put_u8(packet.id);
        bytes.extend_from_slice(&packet.payload);

        bytes.freeze()
    }
}

#[derive(Debug)]
pub(super) struct PacketBuilder {
    id: PacketId,
    bytes: BytesMut,
}

impl PacketBuilder {
    pub fn new(id: PacketId) -> Self {
        Self {
            id,
            bytes: BytesMut::new(),
        }
    }

    pub fn add_varint(mut self, varint: &VarInt) -> Self {
        self.bytes.extend_from_slice(varint);
        self
    }

    pub fn add_string(self, string: &str) -> Self {
        let mut inst = self.add_varint(&VarInt::from(string.len() as i32));
        inst.bytes.put(string.as_bytes());
        inst
    }

    pub fn add_u16(mut self, short: u16) -> Self {
        self.bytes.put_u16(short);
        self
    }

    pub fn build(self) -> Packet {
        Packet {
            id: self.id.into(),
            payload: self.bytes.freeze(),
        }
    }
}