diff --git a/bot/src/main.rs b/bot/src/main.rs index def38c3d..68fd41bf 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -3,7 +3,7 @@ use minecraft_protocol::ServerAddress; use tokio::runtime::Runtime; async fn bot() { - let address = ServerAddress::parse(&"play.wynncraft.com".to_string()).unwrap(); + let address = ServerAddress::parse(&"mc.hypixel.net".to_string()).unwrap(); minecraft_protocol::server_status_pinger::ping_server(&address) .await .unwrap(); diff --git a/minecraft-protocol/src/connection.rs b/minecraft-protocol/src/connection.rs index 4fa1cde7..79058a75 100644 --- a/minecraft-protocol/src/connection.rs +++ b/minecraft-protocol/src/connection.rs @@ -1,6 +1,9 @@ -use crate::ServerIpAddress; +use crate::{friendly_byte_buf::FriendlyByteBuf, packets::Packet, ServerIpAddress}; use bytes::BytesMut; -use tokio::{io::BufWriter, net::TcpStream}; +use tokio::{ + io::{AsyncWriteExt, BufWriter}, + net::TcpStream, +}; pub enum PacketFlow { ClientToServer, @@ -35,4 +38,30 @@ impl Connection { buffer: BytesMut::with_capacity(4 * 1024 * 1024), }) } + + /// Write a packet to the server + pub async fn send_packet(&mut self, packet: &dyn Packet) { + // packet structure: + // length + id + data + + // Is this efficient? I have no idea, probably not. + // getting rid of the FriendlyByteBuffer struct might help + + // write the packet id + let mut id_and_data_buf = vec![packet.get_id()]; + + // write the packet data + let mut id_and_data_friendly_buf = FriendlyByteBuf::new(&mut id_and_data_buf); + packet.write(&mut id_and_data_friendly_buf); + + // add the packet length to the beginning + let mut complete_buf: Vec = Vec::new(); + let mut complete_friendly_buf = FriendlyByteBuf::new(&mut complete_buf); + complete_friendly_buf.write_varint(id_and_data_buf.len() as u32); + complete_buf.append(&mut id_and_data_buf); + + // finally, write and flush to the stream + self.stream.write_all(&complete_buf).await.unwrap(); + self.stream.flush().await.unwrap(); + } } diff --git a/minecraft-protocol/src/friendly_byte_buf.rs b/minecraft-protocol/src/friendly_byte_buf.rs index 2babe398..586b0857 100644 --- a/minecraft-protocol/src/friendly_byte_buf.rs +++ b/minecraft-protocol/src/friendly_byte_buf.rs @@ -1,22 +1,24 @@ //! Minecraft calls it a "friendly byte buffer". use byteorder::{BigEndian, WriteBytesExt}; -// use std::io::Write; -const MAX_VARINT_SIZE: u32 = 5; -const MAX_VARLONG_SIZE: u32 = 10; -const DEFAULT_NBT_QUOTA: u32 = 2097152; +// const MAX_VARINT_SIZE: u32 = 5; +// const MAX_VARLONG_SIZE: u32 = 10; +// const DEFAULT_NBT_QUOTA: u32 = 2097152; const MAX_STRING_LENGTH: u16 = 32767; -const MAX_COMPONENT_STRING_LENGTH: u32 = 262144; +// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144; pub struct FriendlyByteBuf<'a> { source: &'a mut Vec, } -impl FriendlyByteBuf<'_> { +impl<'a> FriendlyByteBuf<'a> { + pub fn new(source: &'a mut Vec) -> FriendlyByteBuf<'a> { + FriendlyByteBuf { source } + } + pub fn write_byte(&mut self, n: u8) { self.source.write_u8(n).unwrap(); - println!("write_byte: {}", n); } pub fn write_bytes(&mut self, bytes: &[u8]) { diff --git a/minecraft-protocol/src/packets/client_intention_packet.rs b/minecraft-protocol/src/packets/client_intention_packet.rs index a9a4d86a..30f76387 100644 --- a/minecraft-protocol/src/packets/client_intention_packet.rs +++ b/minecraft-protocol/src/packets/client_intention_packet.rs @@ -5,17 +5,19 @@ use crate::friendly_byte_buf::FriendlyByteBuf; use super::{ConnectionProtocol, Packet}; #[derive(Hash)] -pub struct ClientIntentionPacket { - protocol_version: u32, - hostname: String, - port: u16, +pub struct ClientIntentionPacket<'a> { + pub protocol_version: u32, + pub hostname: &'a String, + pub port: u16, /// 1 for status, 2 for login - intention: ConnectionProtocol, + pub intention: ConnectionProtocol, } // implement "Packet" for "ClientIntentionPacket" -impl Packet for ClientIntentionPacket { - const ID: u8 = 0x00; +impl<'a> Packet for ClientIntentionPacket<'a> { + fn get_id(&self) -> u8 { + 0x00 + } // implement "from_reader" for "ClientIntentionPacket" fn write(&self, buf: &mut FriendlyByteBuf) { @@ -25,4 +27,3 @@ impl Packet for ClientIntentionPacket { buf.write_varint(self.intention.clone() as u32); } } - diff --git a/minecraft-protocol/src/packets/mod.rs b/minecraft-protocol/src/packets/mod.rs index 2ccdeb44..bdb80c2f 100644 --- a/minecraft-protocol/src/packets/mod.rs +++ b/minecraft-protocol/src/packets/mod.rs @@ -1,6 +1,7 @@ -pub mod client_intention_packet; - -use std::collections::HashMap; +mod client_intention_packet; +pub use client_intention_packet::ClientIntentionPacket; +mod serverbound_status_request_packet; +pub use serverbound_status_request_packet::ServerboundStatusRequestPacket; use crate::friendly_byte_buf::FriendlyByteBuf; @@ -13,9 +14,8 @@ pub enum ConnectionProtocol { } pub trait Packet { - /// The id of the packet, this is always a byte in vanilla. - /// This might be bigger than a u8 if using modpacks with lots of custom packets? - const ID: u8; - + /// Get the id of the packet, this is always a byte. + fn get_id(&self) -> u8; + fn write(&self, friendly_byte_buf: &mut FriendlyByteBuf) -> (); } diff --git a/minecraft-protocol/src/server_status_pinger.rs b/minecraft-protocol/src/server_status_pinger.rs index 86d0cae9..e9393179 100644 --- a/minecraft-protocol/src/server_status_pinger.rs +++ b/minecraft-protocol/src/server_status_pinger.rs @@ -1,81 +1,22 @@ -use crate::{connection::Connection, resolver, ServerAddress}; -use tokio::{ - io::{AsyncReadExt, AsyncWriteExt, BufWriter}, - net::TcpStream, -}; - -struct ServerStatus {} - -async fn write_byte(buf: &mut Vec, n: u8) { - buf.write_u8(n).await.unwrap(); - println!("write_byte: {}", n); -} - -async fn write_bytes(buf: &mut Vec, bytes: &[u8]) { - buf.write_all(bytes).await.unwrap(); - println!("write_bytes: {:?}", buf); -} - -async fn write_varint(buf: &mut Vec, mut n: u32) { - loop { - if (n & 0xFFFFFF80) == 0 { - write_byte(buf, n as u8).await; - return (); - } - write_byte(buf, (n & 0x7F | 0x80) as u8).await; - n >>= 7; - } -} - -async fn write_utf(buf: &mut Vec, string: &[u8], len: usize) { - if string.len() > len { - panic!( - "String too big (was {} bytes encoded, max {})", - string.len(), - len - ); - } - write_varint(buf, string.len() as u32).await; - write_bytes(buf, string).await; -} - -async fn write_short(buf: &mut Vec, n: u16) { - buf.write_u16(n).await.unwrap(); - println!("write_short: {}", n); -} +use crate::{connection::Connection, resolver, ServerAddress, packets::{ClientIntentionPacket, ServerboundStatusRequestPacket, ConnectionProtocol}}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; pub async fn ping_server(address: &ServerAddress) -> Result<(), String> { let resolved_address = resolver::resolve_address(&address).await?; let mut conn = Connection::new(&resolved_address).await?; - // protocol version is 757 - - // client intention packet - // friendlyByteBuf.writeVarInt(this.protocolVersion); - // friendlyByteBuf.writeUtf(this.hostName); - // friendlyByteBuf.writeShort(this.port); - // friendlyByteBuf.writeVarInt(this.intention.getId()); - println!("resolved_address {}", &resolved_address.ip); println!("writing intention packet {}", address.host); - let mut buf: Vec = vec![0x00]; // 0 is the packet id for handshake - write_varint(&mut buf, 757).await; - write_utf(&mut buf, address.host.as_bytes(), 32767).await; - write_short(&mut buf, address.port).await; - write_varint(&mut buf, 1).await; + conn.send_packet(&ClientIntentionPacket { + protocol_version: 757, + hostname: &address.host, + port: address.port, + intention: ConnectionProtocol::Status, + }).await; + conn.send_packet(&ServerboundStatusRequestPacket {}).await; - let mut full_buffer = vec![]; - write_varint(&mut full_buffer, buf.len() as u32).await; // length of 1st packet id + data as VarInt - full_buffer.append(&mut buf); - full_buffer.extend_from_slice(&[ - 1, // length of 2nd packet id + data as VarInt - 0x00, // 2nd packet id: 0 for request as VarInt - ]); - - conn.stream.write_all(&full_buffer).await.unwrap(); - conn.stream.flush().await.unwrap(); // log what the server sends back loop { @@ -95,6 +36,4 @@ pub async fn ping_server(address: &ServerAddress) -> Result<(), String> { } } } - - Ok(()) }