use bytes::{Buf, BufMut};
use crate::types::{self, ProtocolError};
use crate::varint;
use super::Packet;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HandshakeIntent {
Status = 1,
Login = 2,
Transfer = 3,
}
impl HandshakeIntent {
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}"
))),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HandshakePacket {
pub protocol_version: i32,
pub server_address: String,
pub server_port: u16,
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);
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
fn handshake_intent_strategy() -> impl Strategy<Value = HandshakeIntent> {
prop_oneof![
Just(HandshakeIntent::Status),
Just(HandshakeIntent::Login),
Just(HandshakeIntent::Transfer),
]
}
proptest! {
#[test]
fn handshake_roundtrip(
protocol_version: i32,
server_address: String,
server_port: u16,
next_state in handshake_intent_strategy()
) {
let server_address = if server_address.len() > 255 { server_address[..255].to_string() } else { server_address };
let packet = HandshakePacket {
protocol_version,
server_address,
server_port,
next_state,
};
let mut buf = Vec::new();
packet.encode(&mut buf);
let decoded = HandshakePacket::decode(&mut &buf[..]).unwrap();
prop_assert_eq!(decoded, packet);
}
}
}