deepslate-protocol 0.1.0

Minecraft protocol primitives for the Deepslate proxy.
Documentation
//! Handshake packet (serverbound).

use bytes::{Buf, BufMut};

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

use super::Packet;

/// The intent declared in the handshake, determining the next protocol state.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HandshakeIntent {
    /// Server list ping (STATUS).
    Status = 1,
    /// Player login (LOGIN).
    Login = 2,
    /// Server transfer (1.20.5+).
    Transfer = 3,
}

impl HandshakeIntent {
    /// Parse from the protocol integer value.
    ///
    /// # Errors
    ///
    /// Returns `ProtocolError::InvalidData` if the value is not recognized.
    pub fn from_id(id: i32) -> Result<Self, ProtocolError> {
        match id {
            1 => Ok(Self::Status),
            2 => Ok(Self::Login),
            3 => Ok(Self::Transfer),
            _ => Err(ProtocolError::InvalidData(format!(
                "unknown handshake intent: {id}"
            ))),
        }
    }
}

/// Serverbound handshake packet (packet ID 0x00 in HANDSHAKE state).
#[derive(Debug, Clone)]
pub struct HandshakePacket {
    /// The protocol version the client is using.
    pub protocol_version: i32,
    /// The server address the client connected to.
    pub server_address: String,
    /// The server port the client connected to.
    pub server_port: u16,
    /// The intended next state.
    pub next_state: HandshakeIntent,
}

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

    fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
        let protocol_version = varint::read_var_int(buf)?;
        let server_address = types::read_string_max(buf, 255)?;
        if buf.remaining() < 2 {
            return Err(ProtocolError::UnexpectedEof);
        }
        let server_port = buf.get_u16();
        let next_state_id = varint::read_var_int(buf)?;
        let next_state = HandshakeIntent::from_id(next_state_id)?;

        Ok(Self {
            protocol_version,
            server_address,
            server_port,
            next_state,
        })
    }

    fn encode(&self, buf: &mut impl BufMut) {
        varint::write_var_int(buf, self.protocol_version);
        types::write_string(buf, &self.server_address);
        buf.put_u16(self.server_port);
        varint::write_var_int(buf, self.next_state as i32);
    }
}