diff options
Diffstat (limited to 'minecraft-protocol')
18 files changed, 0 insertions, 1086 deletions
diff --git a/minecraft-protocol/Cargo.toml b/minecraft-protocol/Cargo.toml deleted file mode 100644 index 3cbf663b..00000000 --- a/minecraft-protocol/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -edition = "2021" -name = "minecraft-protocol" -version = "0.1.0" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -async-recursion = "^0.3.2" -async-trait = "0.1.51" -byteorder = "^1.4.3" -bytes = "^1.1.0" -minecraft-chat = {path = "../minecraft-chat"} -serde = {version = "1.0.130", features = ["serde_derive"]} -serde_json = "^1.0.72" -thiserror = "^1.0.30" -tokio = {version = "^1.14.0", features = ["io-util", "net", "macros"]} -tokio-util = "^0.6.9" -trust-dns-resolver = "^0.20.3" diff --git a/minecraft-protocol/src/connect.rs b/minecraft-protocol/src/connect.rs deleted file mode 100644 index f6dd9fe7..00000000 --- a/minecraft-protocol/src/connect.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! parse sending and receiving packets with a server. - -use crate::packets::game::GamePacket; -use crate::packets::handshake::HandshakePacket; -use crate::packets::login::LoginPacket; -use crate::packets::status::StatusPacket; -use crate::read::read_packet; -use crate::write::write_packet; -use crate::ServerIpAddress; -use tokio::net::TcpStream; - -pub enum PacketFlow { - ClientToServer, - ServerToClient, -} - -pub struct HandshakeConnection { - pub flow: PacketFlow, - /// The buffered writer - pub stream: TcpStream, -} - -pub struct GameConnection { - pub flow: PacketFlow, - /// The buffered writer - pub stream: TcpStream, -} - -pub struct StatusConnection { - pub flow: PacketFlow, - /// The buffered writer - pub stream: TcpStream, -} - -pub struct LoginConnection { - pub flow: PacketFlow, - /// The buffered writer - pub stream: TcpStream, -} - -impl HandshakeConnection { - pub async fn new(address: &ServerIpAddress) -> Result<HandshakeConnection, String> { - let ip = address.ip; - let port = address.port; - - let stream = TcpStream::connect(format!("{}:{}", ip, port)) - .await - .map_err(|_| "Failed to connect to server")?; - - // enable tcp_nodelay - stream - .set_nodelay(true) - .expect("Error enabling tcp_nodelay"); - - Ok(HandshakeConnection { - flow: PacketFlow::ServerToClient, - stream, - }) - } - - pub fn login(self) -> LoginConnection { - LoginConnection { - flow: self.flow, - stream: self.stream, - } - } - - pub fn status(self) -> StatusConnection { - StatusConnection { - flow: self.flow, - stream: self.stream, - } - } - - pub async fn read(&mut self) -> Result<HandshakePacket, String> { - read_packet::<HandshakePacket>(&self.flow, &mut self.stream).await - } - - /// Write a packet to the server - pub async fn write(&mut self, packet: HandshakePacket) { - write_packet(packet, &mut self.stream).await; - } -} - -impl GameConnection { - pub async fn read(&mut self) -> Result<GamePacket, String> { - read_packet::<GamePacket>(&self.flow, &mut self.stream).await - } - - /// Write a packet to the server - pub async fn write(&mut self, packet: GamePacket) { - write_packet(packet, &mut self.stream).await; - } -} - -impl StatusConnection { - pub async fn read(&mut self) -> Result<StatusPacket, String> { - read_packet::<StatusPacket>(&self.flow, &mut self.stream).await - } - - /// Write a packet to the server - pub async fn write(&mut self, packet: StatusPacket) { - write_packet(packet, &mut self.stream).await; - } -} - -impl LoginConnection { - pub async fn read(&mut self) -> Result<LoginPacket, String> { - read_packet::<LoginPacket>(&self.flow, &mut self.stream).await - } - - /// Write a packet to the server - pub async fn write(&mut self, packet: LoginPacket) { - write_packet(packet, &mut self.stream).await; - } -} diff --git a/minecraft-protocol/src/lib.rs b/minecraft-protocol/src/lib.rs deleted file mode 100644 index 684add45..00000000 --- a/minecraft-protocol/src/lib.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! This lib is responsible for parsing Minecraft packets. - -use std::net::IpAddr; -use std::str::FromStr; - -pub mod connect; -pub mod mc_buf; -pub mod packets; -pub mod read; -pub mod resolver; -pub mod write; - -#[derive(Debug)] -pub struct ServerAddress { - pub host: String, - pub port: u16, -} - -#[derive(Debug)] -pub struct ServerIpAddress { - pub ip: IpAddr, - pub port: u16, -} - -// impl try_from for ServerAddress -impl<'a> TryFrom<&'a str> for ServerAddress { - type Error = String; - - /// Convert a Minecraft server address (host:port, the port is optional) to a ServerAddress - fn try_from(string: &str) -> Result<Self, Self::Error> { - if string.is_empty() { - return Err("Empty string".to_string()); - } - let mut parts = string.split(':'); - let host = parts.next().ok_or("No host specified")?.to_string(); - // default the port to 25565 - let port = parts.next().unwrap_or("25565"); - let port = u16::from_str(port).map_err(|_| "Invalid port specified")?; - Ok(ServerAddress { host, port }) - } -} - -pub async fn connect(address: ServerAddress) -> Result<(), Box<dyn std::error::Error>> { - let resolved_address = resolver::resolve_address(&address).await; - println!("Resolved address: {:?}", resolved_address); - Ok(()) -} - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/minecraft-protocol/src/mc_buf.rs b/minecraft-protocol/src/mc_buf.rs deleted file mode 100644 index 54ba1f7d..00000000 --- a/minecraft-protocol/src/mc_buf.rs +++ /dev/null @@ -1,203 +0,0 @@ -//! Utilities for reading and writing for the Minecraft protocol - -use std::io::Write; - -use async_trait::async_trait; -use byteorder::{BigEndian, WriteBytesExt}; -use tokio::io::{AsyncRead, AsyncReadExt}; - -// const DEFAULT_NBT_QUOTA: u32 = 2097152; -const MAX_STRING_LENGTH: u16 = 32767; -// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144; - -#[async_trait] -pub trait Writable { - fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error>; - fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error>; - fn write_varint(&mut self, value: i32) -> Result<(), std::io::Error>; - fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error>; - fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error>; - fn write_short(&mut self, n: u16) -> Result<(), std::io::Error>; - fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error>; -} - -#[async_trait] -impl Writable for Vec<u8> { - fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error> { - WriteBytesExt::write_u8(self, n) - } - - fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { - Ok(self.extend_from_slice(bytes)) - } - - fn write_varint(&mut self, mut value: i32) -> Result<(), std::io::Error> { - let mut buffer = [0]; - if value == 0 { - self.write_all(&buffer).unwrap(); - } - while value != 0 { - buffer[0] = (value & 0b0111_1111) as u8; - value = (value >> 7) & (i32::max_value() >> 6); - if value != 0 { - buffer[0] |= 0b1000_0000; - } - self.write_all(&buffer)?; - } - Ok(()) - } - - fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error> { - if string.len() > len { - panic!( - "String too big (was {} bytes encoded, max {})", - string.len(), - len - ); - } - self.write_varint(string.len() as i32); - self.write_bytes(string.as_bytes()) - } - - fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error> { - self.write_utf_with_len(string, MAX_STRING_LENGTH.into()) - } - - fn write_short(&mut self, n: u16) -> Result<(), std::io::Error> { - WriteBytesExt::write_u16::<BigEndian>(self, n) - } - - fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { - self.write_varint(bytes.len() as i32); - self.write_bytes(bytes) - } -} - -#[async_trait] -pub trait Readable { - async fn read_varint(&mut self) -> Result<(i32, u8), String>; - async fn read_byte_array(&mut self) -> Result<Vec<u8>, String>; - async fn read_bytes(&mut self, n: usize) -> Result<Vec<u8>, String>; - async fn read_utf(&mut self) -> Result<String, String>; - async fn read_utf_with_len(&mut self, max_length: u32) -> Result<String, String>; - async fn read_byte(&mut self) -> Result<u8, String>; -} - -#[async_trait] -impl<R> Readable for R -where - R: AsyncRead + std::marker::Unpin + std::marker::Send, -{ - // fast varints stolen from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 - /// Read a single varint from the reader and return the value, along with the number of bytes read - async fn read_varint(&mut self) -> Result<(i32, u8), String> { - let mut buffer = [0]; - let mut ans = 0; - for i in 0..4 { - self.read_exact(&mut buffer) - .await - .map_err(|_| "Invalid VarInt".to_string())?; - ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); - if buffer[0] & 0b1000_0000 == 0 { - return Ok((ans, i + 1)); - } - } - Ok((ans, 5)) - } - - async fn read_byte_array(&mut self) -> Result<Vec<u8>, String> { - let length = self.read_varint().await?.0 as usize; - Ok(self.read_bytes(length).await?) - } - - async fn read_bytes(&mut self, n: usize) -> Result<Vec<u8>, String> { - let mut bytes = vec![0; n]; - match AsyncReadExt::read_exact(self, &mut bytes).await { - Ok(_) => Ok(bytes), - Err(_) => Err("Error reading bytes".to_string()), - } - } - - async fn read_utf(&mut self) -> Result<String, String> { - self.read_utf_with_len(MAX_STRING_LENGTH.into()).await - } - - async fn read_utf_with_len(&mut self, max_length: u32) -> Result<String, String> { - let (length, _length_varint_length) = self.read_varint().await?; - // i don't know why it's multiplied by 4 but it's like that in mojang's code so - if length < 0 { - return Err( - "The received encoded string buffer length is less than zero! Weird string!" - .to_string(), - ); - } - if length as u32 > max_length * 4 { - return Err(format!( - "The received encoded string buffer length is longer than maximum allowed ({} > {})", - length, - max_length * 4 - )); - } - - // this is probably quite inefficient, idk how to do it better - let mut string = String::new(); - let mut buffer = vec![0; length as usize]; - self.read_exact(&mut buffer) - .await - .map_err(|_| "Invalid UTF-8".to_string())?; - - string.push_str(std::str::from_utf8(&buffer).unwrap()); - if string.len() > length as usize { - return Err(format!( - "The received string length is longer than maximum allowed ({} > {})", - length, max_length - )); - } - - Ok(string) - } - - /// Read a single byte from the reader - async fn read_byte(&mut self) -> Result<u8, String> { - match AsyncReadExt::read_u8(self).await { - Ok(r) => Ok(r), - Err(_) => Err("Error reading byte".to_string()), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::io::Cursor; - use tokio::io::BufReader; - - #[test] - fn test_write_varint() { - let mut buf = Vec::new(); - buf.write_varint(123456); - assert_eq!(buf, vec![192, 196, 7]); - - let mut buf = Vec::new(); - buf.write_varint(0); - assert_eq!(buf, vec![0]); - } - - #[tokio::test] - async fn test_read_varint() { - let mut buf = BufReader::new(Cursor::new(vec![192, 196, 7])); - assert_eq!(buf.read_varint().await.unwrap(), (123456, 3)); - - let mut buf = BufReader::new(Cursor::new(vec![0])); - assert_eq!(buf.read_varint().await.unwrap(), (0, 1)); - - let mut buf = BufReader::new(Cursor::new(vec![1])); - assert_eq!(buf.read_varint().await.unwrap(), (1, 1)); - } - - #[tokio::test] - async fn test_read_varint_longer() { - let mut buf = BufReader::new(Cursor::new(vec![138, 56, 0, 135, 56, 123])); - assert_eq!(buf.read_varint().await.unwrap(), (7178, 2)); - } -} diff --git a/minecraft-protocol/src/packets/game/mod.rs b/minecraft-protocol/src/packets/game/mod.rs deleted file mode 100644 index a3ef2541..00000000 --- a/minecraft-protocol/src/packets/game/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -use async_trait::async_trait; -use tokio::io::BufReader; - -use crate::connect::PacketFlow; - -use super::ProtocolPacket; - -#[derive(Clone, Debug)] -pub enum GamePacket -where - Self: Sized, {} - -#[async_trait] -impl ProtocolPacket for GamePacket { - fn id(&self) -> u32 { - 0x00 - } - - fn write(&self, _buf: &mut Vec<u8>) {} - - /// Read a packet by its id, ConnectionProtocol, and flow - async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( - _id: u32, - flow: &PacketFlow, - _buf: &mut BufReader<T>, - ) -> Result<GamePacket, String> - where - Self: Sized, - { - match flow { - PacketFlow::ServerToClient => Err("HandshakePacket::read not implemented".to_string()), - PacketFlow::ClientToServer => Err("HandshakePacket::read not implemented".to_string()), - } - } -} diff --git a/minecraft-protocol/src/packets/handshake/client_intention_packet.rs b/minecraft-protocol/src/packets/handshake/client_intention_packet.rs deleted file mode 100644 index 868626b3..00000000 --- a/minecraft-protocol/src/packets/handshake/client_intention_packet.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::hash::Hash; - -use tokio::io::BufReader; - -use crate::{mc_buf::Writable, packets::ConnectionProtocol}; - -use super::HandshakePacket; - -#[derive(Hash, Clone, Debug)] -pub struct ClientIntentionPacket { - pub protocol_version: u32, - pub hostname: String, - pub port: u16, - /// 1 for status, 2 for login - pub intention: ConnectionProtocol, -} - -impl ClientIntentionPacket { - pub fn get(self) -> HandshakePacket { - HandshakePacket::ClientIntentionPacket(self) - } - - pub fn write(&self, buf: &mut Vec<u8>) { - buf.write_varint(self.protocol_version as i32).unwrap(); - buf.write_utf(&self.hostname).unwrap(); - buf.write_short(self.port).unwrap(); - buf.write_varint(self.intention.clone() as i32).unwrap(); - } - - pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( - _buf: &mut BufReader<T>, - ) -> Result<HandshakePacket, String> { - Err("ClientIntentionPacket::parse not implemented".to_string()) - // Ok(ClientIntentionPacket {}.get()) - } -} diff --git a/minecraft-protocol/src/packets/handshake/mod.rs b/minecraft-protocol/src/packets/handshake/mod.rs deleted file mode 100644 index 01010e1e..00000000 --- a/minecraft-protocol/src/packets/handshake/mod.rs +++ /dev/null @@ -1,49 +0,0 @@ -pub mod client_intention_packet; - -use async_trait::async_trait; -use tokio::io::BufReader; - -use crate::connect::PacketFlow; - -use super::ProtocolPacket; - -#[derive(Clone, Debug)] -pub enum HandshakePacket -where - Self: Sized, -{ - ClientIntentionPacket(client_intention_packet::ClientIntentionPacket), -} - -#[async_trait] -impl ProtocolPacket for HandshakePacket { - fn id(&self) -> u32 { - match self { - HandshakePacket::ClientIntentionPacket(_packet) => 0x00, - } - } - - fn write(&self, buf: &mut Vec<u8>) { - match self { - HandshakePacket::ClientIntentionPacket(packet) => packet.write(buf), - } - } - - /// Read a packet by its id, ConnectionProtocol, and flow - async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( - id: u32, - flow: &PacketFlow, - buf: &mut BufReader<T>, - ) -> Result<HandshakePacket, String> - where - Self: Sized, - { - match flow { - PacketFlow::ServerToClient => Err("HandshakePacket::read not implemented".to_string()), - PacketFlow::ClientToServer => match id { - 0x00 => Ok(client_intention_packet::ClientIntentionPacket::read(buf).await?), - _ => Err(format!("Unknown ClientToServer status packet id: {}", id)), - }, - } - } -} diff --git a/minecraft-protocol/src/packets/login/clientbound_custom_query_packet.rs b/minecraft-protocol/src/packets/login/clientbound_custom_query_packet.rs deleted file mode 100644 index 093176eb..00000000 --- a/minecraft-protocol/src/packets/login/clientbound_custom_query_packet.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::hash::Hash; -use tokio::io::BufReader; - -use crate::mc_buf::{self, Readable, Writable}; - -use super::LoginPacket; - -#[derive(Hash, Clone, Debug)] -pub struct ClientboundCustomQueryPacket { - pub transaction_id: u32, - // TODO: this should be a resource location - pub identifier: String, - pub data: Vec<u8>, -} - -impl ClientboundCustomQueryPacket { - pub fn get(self) -> LoginPacket { - LoginPacket::ClientboundCustomQueryPacket(self) - } - - pub fn write(&self, buf: &mut Vec<u8>) { - buf.write_varint(self.transaction_id as i32).unwrap(); - buf.write_utf(&self.identifier).unwrap(); - buf.write_bytes(&self.data).unwrap(); - } - - pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( - buf: &mut BufReader<T>, - ) -> Result<LoginPacket, String> { - let transaction_id = buf.read_varint().await?.0 as u32; - // TODO: this should be a resource location - let identifier = buf.read_utf().await?; - let data = buf.read_bytes(1048576).await?; - Ok(ClientboundCustomQueryPacket { - transaction_id, - identifier, - data, - } - .get()) - } -} diff --git a/minecraft-protocol/src/packets/login/clientbound_hello_packet.rs b/minecraft-protocol/src/packets/login/clientbound_hello_packet.rs deleted file mode 100644 index 36a48706..00000000 --- a/minecraft-protocol/src/packets/login/clientbound_hello_packet.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::hash::Hash; -use tokio::io::BufReader; - -use crate::mc_buf::Readable; - -use super::LoginPacket; - -#[derive(Hash, Clone, Debug)] -pub struct ClientboundHelloPacket { - pub server_id: String, - pub public_key: Vec<u8>, - pub nonce: Vec<u8>, -} - -impl ClientboundHelloPacket { - pub fn get(self) -> LoginPacket { - LoginPacket::ClientboundHelloPacket(self) - } - - pub fn write(&self, _buf: &mut Vec<u8>) { - panic!("ClientboundHelloPacket::write not implemented") - } - - pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( - buf: &mut BufReader<T>, - ) -> Result<LoginPacket, String> { - let server_id = buf.read_utf_with_len(20).await?; - let public_key = buf.read_byte_array().await?; - let nonce = buf.read_byte_array().await?; - - Ok(ClientboundHelloPacket { - server_id, - public_key, - nonce, - } - .get()) - } -} diff --git a/minecraft-protocol/src/packets/login/mod.rs b/minecraft-protocol/src/packets/login/mod.rs deleted file mode 100644 index f0ed6717..00000000 --- a/minecraft-protocol/src/packets/login/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -pub mod clientbound_custom_query_packet; -pub mod clientbound_hello_packet; -pub mod serverbound_hello_packet; - -use async_trait::async_trait; -use tokio::io::BufReader; - -use crate::connect::PacketFlow; - -use super::ProtocolPacket; - -#[derive(Clone, Debug)] -pub enum LoginPacket -where - Self: Sized, -{ - ClientboundCustomQueryPacket(clientbound_custom_query_packet::ClientboundCustomQueryPacket), - ServerboundHelloPacket(serverbound_hello_packet::ServerboundHelloPacket), - ClientboundHelloPacket(clientbound_hello_packet::ClientboundHelloPacket), -} - -#[async_trait] -impl ProtocolPacket for LoginPacket { - fn id(&self) -> u32 { - match self { - LoginPacket::ClientboundCustomQueryPacket(_packet) => 0x04, - LoginPacket::ServerboundHelloPacket(_packet) => 0x00, - LoginPacket::ClientboundHelloPacket(_packet) => 0x01, - } - } - - fn write(&self, buf: &mut Vec<u8>) { - match self { - LoginPacket::ClientboundCustomQueryPacket(packet) => packet.write(buf), - LoginPacket::ServerboundHelloPacket(packet) => packet.write(buf), - LoginPacket::ClientboundHelloPacket(packet) => packet.write(buf), - } - } - - /// Read a packet by its id, ConnectionProtocol, and flow - async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( - id: u32, - flow: &PacketFlow, - buf: &mut BufReader<T>, - ) -> Result<LoginPacket, String> - where - Self: Sized, - { - Ok(match flow { - PacketFlow::ServerToClient => match id { - 0x01 => clientbound_hello_packet::ClientboundHelloPacket::read(buf).await?, - 0x04 => { - clientbound_custom_query_packet::ClientboundCustomQueryPacket::read(buf).await? - } - _ => return Err(format!("Unknown ServerToClient status packet id: {}", id)), - }, - PacketFlow::ClientToServer => match id { - 0x00 => serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?, - _ => return Err(format!("Unknown ClientToServer status packet id: {}", id)), - }, - }) - } -} diff --git a/minecraft-protocol/src/packets/login/serverbound_hello_packet.rs b/minecraft-protocol/src/packets/login/serverbound_hello_packet.rs deleted file mode 100644 index 32a6dadc..00000000 --- a/minecraft-protocol/src/packets/login/serverbound_hello_packet.rs +++ /dev/null @@ -1,27 +0,0 @@ -use std::hash::Hash; -use tokio::io::BufReader; - -use crate::mc_buf::Writable; - -use super::LoginPacket; - -#[derive(Hash, Clone, Debug)] -pub struct ServerboundHelloPacket { - pub username: String, -} - -impl ServerboundHelloPacket { - pub fn get(self) -> LoginPacket { - LoginPacket::ServerboundHelloPacket(self) - } - - pub fn write(&self, buf: &mut Vec<u8>) { - buf.write_utf(&self.username).unwrap(); - } - - pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( - _buf: &mut BufReader<T>, - ) -> Result<LoginPacket, String> { - Err("ServerboundHelloPacket::read not implemented".to_string()) - } -} diff --git a/minecraft-protocol/src/packets/mod.rs b/minecraft-protocol/src/packets/mod.rs deleted file mode 100644 index a074b570..00000000 --- a/minecraft-protocol/src/packets/mod.rs +++ /dev/null @@ -1,146 +0,0 @@ -pub mod game; -pub mod handshake; -pub mod login; -pub mod status; - -use async_trait::async_trait; -use tokio::io::BufReader; - -use crate::connect::PacketFlow; - -pub const PROTOCOL_VERSION: u32 = 757; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum ConnectionProtocol { - Handshake = -1, - Game = 0, - Status = 1, - Login = 2, -} - -#[derive(Clone, Debug)] -pub enum Packet { - Game(game::GamePacket), - Handshake(handshake::HandshakePacket), - Login(login::LoginPacket), - Status(Box<status::StatusPacket>), -} - -/// An enum of packets for a certain protocol -#[async_trait] -pub trait ProtocolPacket -where - Self: Sized, -{ - fn id(&self) -> u32; - - /// Read a packet by its id, ConnectionProtocol, and flow - async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( - id: u32, - flow: &PacketFlow, - buf: &mut BufReader<T>, - ) -> Result<Self, String> - where - Self: Sized; - - fn write(&self, buf: &mut Vec<u8>); -} - -// impl Packet { -// fn get_inner_packet(&self) -> &dyn PacketTrait { -// match self { -// Packet::ClientIntentionPacket(packet) => packet, -// Packet::ServerboundStatusRequestPacket(packet) => packet, -// Packet::ClientboundStatusResponsePacket(packet) => packet, -// Packet::ServerboundHelloPacket(packet) => packet, -// Packet::ClientboundHelloPacket(packet) => packet, -// } -// } - -// pub fn id(&self) -> u32 { -// match self { -// Packet::ClientIntentionPacket(_packet) => 0x00, -// Packet::ServerboundStatusRequestPacket(_packet) => 0x00, -// Packet::ClientboundStatusResponsePacket(_packet) => 0x00, -// Packet::ServerboundHelloPacket(_packet) => 0x00, -// Packet::ClientboundHelloPacket(_packet) => 0x01, -// } -// } - -// /// Read a packet by its id, ConnectionProtocol, and flow -// pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( -// id: u32, -// protocol: &ConnectionProtocol, -// flow: &PacketFlow, -// buf: &mut BufReader<T>, -// ) -> Result<Packet, String> { -// match protocol { -// ConnectionProtocol::Handshake => match flow { -// PacketFlow::ClientToServer => match id { -// 0x00 => Ok( -// handshake::client_intention_packet::ClientIntentionPacket::read(buf).await?, -// ), -// _ => Err(format!("Unknown ClientToServer handshake packet id: {}", id)), -// } -// PacketFlow::ServerToClient => Err("ServerToClient handshake packets not implemented".to_string()), -// }, - -// ConnectionProtocol::Game => Err("Game protocol not implemented yet".to_string()), - -// ConnectionProtocol::Status => match flow { -// PacketFlow::ServerToClient => match id { -// 0x00 => Ok( -// status::clientbound_status_response_packet::ClientboundStatusResponsePacket -// ::read(buf) -// .await?, -// ), -// _ => Err(format!("Unknown ServerToClient status packet id: {}", id)), -// }, -// PacketFlow::ClientToServer => match id { -// 0x00 => Ok( -// status::serverbound_status_request_packet::ServerboundStatusRequestPacket -// ::read(buf) -// .await?, -// ), -// _ => Err(format!("Unknown ClientToServer status packet id: {}", id)), -// }, -// }, - -// ConnectionProtocol::Login => match flow { -// PacketFlow::ServerToClient => match id { -// 0x01 => Ok( -// login::clientbound_hello_packet::ClientboundHelloPacket::read(buf).await?, -// ), -// _ => Err(format!("Unknown ServerToClient login packet id: {}", id)), -// }, -// PacketFlow::ClientToServer => match id { -// 0x00 => Ok( -// login::serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?, -// ), -// _ => Err(format!("Unknown ClientToServer login packet id: {}", id)), -// }, -// }, -// } -// } - -// pub fn write(&self, buf: &mut Vec<u8>) { -// self.get_inner_packet().write(buf); -// } -// } - -// #[async_trait] -// pub trait PacketTrait -// where -// Self: Sized, -// { -// /// Return a version of the packet that you can actually use for stuff -// fn get(self) -> dyn ProtocolPacket; - -// fn write(&self, buf: &mut Vec<u8>); - -// async fn read<T: AsyncRead + std::marker::Unpin + std::marker::Send, P: ProtocolPacket>( -// buf: &mut BufReader<T>, -// ) -> Result<P, String> -// where -// Self: Sized; -// } diff --git a/minecraft-protocol/src/packets/status/clientbound_status_response_packet.rs b/minecraft-protocol/src/packets/status/clientbound_status_response_packet.rs deleted file mode 100644 index 920e3484..00000000 --- a/minecraft-protocol/src/packets/status/clientbound_status_response_packet.rs +++ /dev/null @@ -1,58 +0,0 @@ -use minecraft_chat::component::Component; -use serde::Deserialize; -use serde_json::Value; -use tokio::io::BufReader; - -use crate::mc_buf::Readable; - -use super::StatusPacket; - -#[derive(Clone, Debug, Deserialize)] -pub struct Version { - pub name: Component, - pub protocol: u32, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct SamplePlayer { - pub id: String, - pub name: String, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct Players { - pub max: u32, - pub online: u32, - pub sample: Vec<SamplePlayer>, -} - -// the entire packet is just json, which is why it has deserialize -#[derive(Clone, Debug, Deserialize)] -pub struct ClientboundStatusResponsePacket { - pub description: Component, - pub favicon: Option<String>, - pub players: Players, - pub version: Version, -} - -impl ClientboundStatusResponsePacket { - pub fn get(self) -> StatusPacket { - StatusPacket::ClientboundStatusResponsePacket(Box::new(self)) - } - - pub fn write(&self, _buf: &mut Vec<u8>) {} - - pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( - buf: &mut BufReader<T>, - ) -> Result<StatusPacket, String> { - let status_string = buf.read_utf().await?; - let status_json: Value = - serde_json::from_str(status_string.as_str()).expect("Server status isn't valid JSON"); - - let packet = ClientboundStatusResponsePacket::deserialize(status_json) - .map_err(|e| e.to_string())? - .get(); - - Ok(packet) - } -} diff --git a/minecraft-protocol/src/packets/status/mod.rs b/minecraft-protocol/src/packets/status/mod.rs deleted file mode 100644 index ac6a34e1..00000000 --- a/minecraft-protocol/src/packets/status/mod.rs +++ /dev/null @@ -1,66 +0,0 @@ -pub mod clientbound_status_response_packet; -pub mod serverbound_status_request_packet; - -use async_trait::async_trait; -use tokio::io::BufReader; - -use crate::connect::PacketFlow; - -use super::ProtocolPacket; - -#[derive(Clone, Debug)] -pub enum StatusPacket -where - Self: Sized, -{ - ServerboundStatusRequestPacket( - serverbound_status_request_packet::ServerboundStatusRequestPacket, - ), - ClientboundStatusResponsePacket( - Box<clientbound_status_response_packet::ClientboundStatusResponsePacket>, - ), -} - -#[async_trait] -impl ProtocolPacket for StatusPacket { - fn id(&self) -> u32 { - match self { - StatusPacket::ServerboundStatusRequestPacket(_packet) => 0x00, - StatusPacket::ClientboundStatusResponsePacket(_packet) => 0x00, - } - } - - fn write(&self, buf: &mut Vec<u8>) { - match self { - StatusPacket::ServerboundStatusRequestPacket(packet) => packet.write(buf), - StatusPacket::ClientboundStatusResponsePacket(packet) => packet.write(buf), - } - } - - /// Read a packet by its id, ConnectionProtocol, and flow - async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( - id: u32, - flow: &PacketFlow, - buf: &mut BufReader<T>, - ) -> Result<StatusPacket, String> - where - Self: Sized, - { - match flow { - PacketFlow::ServerToClient => match id { - 0x00 => Ok( - clientbound_status_response_packet::ClientboundStatusResponsePacket::read(buf) - .await?, - ), - _ => Err(format!("Unknown ServerToClient status packet id: {}", id)), - }, - PacketFlow::ClientToServer => match id { - 0x00 => Ok( - serverbound_status_request_packet::ServerboundStatusRequestPacket::read(buf) - .await?, - ), - _ => Err(format!("Unknown ClientToServer status packet id: {}", id)), - }, - } - } -} diff --git a/minecraft-protocol/src/packets/status/serverbound_status_request_packet.rs b/minecraft-protocol/src/packets/status/serverbound_status_request_packet.rs deleted file mode 100644 index 6a58da1f..00000000 --- a/minecraft-protocol/src/packets/status/serverbound_status_request_packet.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::hash::Hash; -use tokio::io::BufReader; - -use super::StatusPacket; - -#[derive(Hash, Clone, Debug)] -pub struct ServerboundStatusRequestPacket {} - -impl ServerboundStatusRequestPacket { - pub fn get(self) -> StatusPacket { - StatusPacket::ServerboundStatusRequestPacket(self) - } - - pub fn write(&self, _buf: &mut Vec<u8>) { - panic!("ServerboundStatusRequestPacket::write not implemented") - } - - pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>( - _buf: &mut BufReader<T>, - ) -> Result<StatusPacket, String> { - Err("ServerboundStatusRequestPacket::read not implemented".to_string()) - } -} diff --git a/minecraft-protocol/src/read.rs b/minecraft-protocol/src/read.rs deleted file mode 100644 index 6f242e8b..00000000 --- a/minecraft-protocol/src/read.rs +++ /dev/null @@ -1,28 +0,0 @@ -use tokio::{io::BufReader, net::TcpStream}; - -use crate::{connect::PacketFlow, mc_buf::Readable, packets::ProtocolPacket}; - -pub async fn read_packet<P: ProtocolPacket>( - flow: &PacketFlow, - stream: &mut TcpStream, -) -> Result<P, String> { - // what this does: - // 1. reads the first 5 bytes, probably only some of this will be used to get the packet length - // 2. how much we should read = packet length - 5 - // 3. read the rest of the packet and add it to the cursor - // 4. figure out what packet this is and parse it - - // the first thing minecraft sends us is the length as a varint, which can be up to 5 bytes long - let mut buf = BufReader::with_capacity(4 * 1024 * 1024, stream); - - let (_packet_size, _packet_size_varint_size) = buf.read_varint().await?; - - // then, minecraft tells us the packet id as a varint - let (packet_id, _packet_id_size) = buf.read_varint().await?; - - // if we recognize the packet id, parse it - - let packet = P::read(packet_id.try_into().unwrap(), flow, &mut buf).await?; - - Ok(packet) -} diff --git a/minecraft-protocol/src/resolver.rs b/minecraft-protocol/src/resolver.rs deleted file mode 100644 index 24687a6e..00000000 --- a/minecraft-protocol/src/resolver.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::net::IpAddr; - -use crate::{ServerAddress, ServerIpAddress}; -use async_recursion::async_recursion; -use trust_dns_resolver::{ - config::{ResolverConfig, ResolverOpts}, - TokioAsyncResolver, -}; - -/// Resolve a Minecraft server address into an IP address and port. -/// If it's already an IP address, it's returned as-is. -#[async_recursion] -pub async fn resolve_address(address: &ServerAddress) -> Result<ServerIpAddress, String> { - // If the address.host is already in the format of an ip address, return it. - if let Ok(ip) = address.host.parse::<IpAddr>() { - return Ok(ServerIpAddress { - ip, - port: address.port, - }); - } - - // we specify Cloudflare instead of the default resolver because trust_dns_resolver has an issue on Windows where it's really slow using the default resolver - let resolver = - TokioAsyncResolver::tokio(ResolverConfig::cloudflare(), ResolverOpts::default()).unwrap(); - - // first, we do a srv lookup for _minecraft._tcp.<host> - let srv_redirect_result = resolver - .srv_lookup(format!("_minecraft._tcp.{}", address.host).as_str()) - .await; - - // if it resolves that means it's a redirect so we call resolve_address again with the new host - if let Ok(redirect_result) = srv_redirect_result { - let redirect_srv = redirect_result - .iter() - .next() - .ok_or_else(|| "No SRV record found".to_string())?; - let redirect_address = ServerAddress { - host: redirect_srv.target().to_utf8(), - port: redirect_srv.port(), - }; - - println!("redirecting to {:?}", redirect_address); - - return resolve_address(&redirect_address).await; - } - - // there's no redirect, try to resolve this as an ip address - let lookup_ip_result = resolver.lookup_ip(address.host.clone()).await; - let lookup_ip = lookup_ip_result.map_err(|_| "No IP found".to_string())?; - - Ok(ServerIpAddress { - ip: lookup_ip.iter().next().unwrap(), - port: address.port, - }) -} diff --git a/minecraft-protocol/src/write.rs b/minecraft-protocol/src/write.rs deleted file mode 100644 index 3d8540eb..00000000 --- a/minecraft-protocol/src/write.rs +++ /dev/null @@ -1,27 +0,0 @@ -use tokio::{io::AsyncWriteExt, net::TcpStream}; - -use crate::{mc_buf::Writable, packets::ProtocolPacket}; - -pub async fn write_packet(packet: impl ProtocolPacket, stream: &mut TcpStream) { - // TODO: implement compression - - // packet structure: - // length (varint) + id (varint) + data - - // write the packet id - let mut id_and_data_buf = vec![]; - id_and_data_buf.write_varint(packet.id() as i32); - packet.write(&mut id_and_data_buf); - - // write the packet data - - // make a new buffer that has the length at the beginning - // and id+data at the end - let mut complete_buf: Vec<u8> = Vec::new(); - complete_buf.write_varint(id_and_data_buf.len() as i32); - complete_buf.append(&mut id_and_data_buf); - - // finally, write and flush to the stream - stream.write_all(&complete_buf).await.unwrap(); - stream.flush().await.unwrap(); -} |
