Skip to main content

minecraft_pinger/common/
protocol.rs

1use crate::error::PingError;
2use bytes::{Buf, BufMut, Bytes, BytesMut};
3
4pub const NEXT_BYTE_MASK: u32 = 0xFFFFFF80;
5pub const CONTINUATION_BIT: u8 = 0x80;
6pub const DATA_MASK: u8 = 0x7F;
7
8pub(crate) fn write_var_int(buffer: &mut BytesMut, x: i32) {
9    let mut ux = x as u32; // cast to u32 for logical shift
10    while (ux & NEXT_BYTE_MASK) != 0 {
11        buffer.put_u8((ux as u8 & DATA_MASK) | CONTINUATION_BIT); // (ux as u8 & DATA_MASK) get the 7 bits of data, | CONTINUATION_BIT set the continuation to 1
12        ux >>= 7; // remove the 7 bits
13    }
14    buffer.put_u8(ux as u8); // put the last, often 0x000..
15}
16
17pub(crate) fn write_string(buffer: &mut BytesMut, string: &str) {
18    write_var_int(buffer, string.len() as i32);
19    buffer.put_slice(string.as_bytes());
20}
21
22pub(crate) fn read_var_int(buf: &mut Bytes) -> Result<i32, PingError> {
23    let mut result = 0i32;
24    let mut shift = 0;
25    loop {
26        let byte = buf.try_get_u8()
27            .map_err(|e| PingError::ReadPacket(format!("Read varint length (v1) {}", e.to_string())))?;
28        result |= ((byte & DATA_MASK) as i32) << shift;
29        if byte & CONTINUATION_BIT == 0 {
30            break;
31        }
32        shift += 7;
33
34        // Security
35        if shift >= 35 {
36            return Err(PingError::ReadPacket("Shift security".to_string()));
37        }
38    }
39    Ok(result)
40}
41
42// Todo: work on code duplication with read_var_int
43pub(crate) async fn read_var_int_stream<R: tokio::io::AsyncReadExt + Unpin>(stream: &mut R) -> Result<i32, PingError> {
44    let mut result = 0i32;
45    let mut shift = 0;
46    loop {
47        let byte = stream.read_u8().await
48            .map_err(|e| PingError::ReadPacket(format!("Read varint length (v2) {}", e.to_string())))?;
49        result |= ((byte & DATA_MASK) as i32) << shift;
50        if byte & CONTINUATION_BIT == 0 {
51            break;
52        }
53        shift += 7;
54
55        // Security
56        if shift >= 35 {
57            return Err(PingError::ReadPacket("Shift security".to_string()));
58        }
59    }
60    Ok(result)
61}
62
63pub(crate) fn read_string(buf: &mut Bytes) -> Result<String, PingError> {
64    let len = read_var_int(buf)? as usize;
65    if buf.remaining() < len {
66        return Err(PingError::ReadPacket("Remaining < len".to_string()));
67    }
68    let bytes = buf.split_to(len);
69    String::from_utf8(bytes.into())
70        .map_err(|_| PingError::ReadPacket("Parse to string error".to_string()))
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use bytes::BytesMut;
77
78    #[test]
79    fn test_var_int_serialization() {
80        let mut buf = BytesMut::new();
81        write_var_int(&mut buf, 255);
82        let mut data = buf.freeze();
83        assert_eq!(read_var_int(&mut data).unwrap(), 255);
84    }
85}