mc-gate 0.1.1

Async proxy for Minecraft and HTTP traffic with automated server wake-up.
Documentation
use serde::Serialize;
use std::io::Cursor;
use tokio::io::AsyncReadExt;
use tokio::net::TcpStream;

pub mod codec;
pub mod handler;

#[derive(Serialize)]
pub struct StatusResponse {
    pub version: Version,
    pub players: Players,
    pub description: Description,
}

#[derive(Serialize)]
pub struct Version {
    pub name: String,
    pub protocol: i32,
}

#[derive(Serialize)]
pub struct Players {
    pub max: i32,
    pub online: String,
}

#[derive(Serialize)]
pub struct Description {
    pub text: String,
}

#[derive(Serialize)]
pub struct DisconnectResponse {
    pub text: String,
}

pub struct MinecraftPacket {
    pub id: i32,
    pub data: Vec<u8>,
}

impl MinecraftPacket {
    pub async fn send_json<S: tokio::io::AsyncWriteExt + Unpin, T: serde::Serialize>(
        stream: &mut S,
        id: i32,
        payload: &T,
    ) -> tokio::io::Result<()> {
        let json = serde_json::to_string(payload)
            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
        let mut data = Vec::new();
        codec::write_string(&mut data, &json);
        let packet = Self { id, data }.serialize();
        stream.write_all(&packet).await?;
        stream.flush().await
    }

    pub fn serialize(self) -> Vec<u8> {
        let mut body = Vec::new();
        codec::write_varint(&mut body, self.id);
        body.extend(self.data);

        let mut frame = Vec::new();
        codec::write_varint(&mut frame, body.len() as i32);
        frame.extend(body);
        frame
    }
}

const MAX_HANDSHAKE_SIZE: usize = 512;

pub async fn inspect_handshake(socket: &TcpStream) -> i32 {
    let mut buf = [0u8; MAX_HANDSHAKE_SIZE];
    
    if let Ok(n) = socket.peek(&mut buf[..]).await {
        let mut cur = Cursor::new(&buf[..n]);

        let _ = codec::read_varint(&mut cur).await;
        if let Ok(0x00) = codec::read_varint(&mut cur).await {
            let _ = codec::read_varint(&mut cur).await;
            let _ = codec::read_string(&mut cur).await;
            let _ = cur.read_u16().await;
            return codec::read_varint(&mut cur).await.unwrap_or(1);
        }
    }
    1
}