From 732de94d7b9f1bf2bc9239c8138a37c53242b470 Mon Sep 17 00:00:00 2001 From: mat Date: Wed, 15 Dec 2021 13:43:57 -0600 Subject: [PATCH] oh yeah it compiles --- minecraft-client/src/connect.rs | 20 ++- minecraft-client/src/ping.rs | 19 ++- minecraft-protocol/src/connect.rs | 133 ++++++++++-------- minecraft-protocol/src/lib.rs | 2 + minecraft-protocol/src/packets/game/mod.rs | 41 +++++- .../src/packets/handshake/mod.rs | 2 - minecraft-protocol/src/read.rs | 28 ++++ minecraft-protocol/src/write.rs | 27 ++++ 8 files changed, 192 insertions(+), 80 deletions(-) create mode 100644 minecraft-protocol/src/read.rs create mode 100644 minecraft-protocol/src/write.rs diff --git a/minecraft-client/src/connect.rs b/minecraft-client/src/connect.rs index 5329cdc0..e6a23612 100644 --- a/minecraft-client/src/connect.rs +++ b/minecraft-client/src/connect.rs @@ -1,11 +1,10 @@ ///! Connect to Minecraft servers. use minecraft_protocol::{ - connect::Connection, + connect::HandshakeConnection, packets::{ handshake::client_intention_packet::ClientIntentionPacket, - login::serverbound_hello_packet::ServerboundHelloPacket, - status::clientbound_status_response_packet::ClientboundStatusResponsePacket, - ConnectionProtocol, Packet, PROTOCOL_VERSION, + login::{serverbound_hello_packet::ServerboundHelloPacket, LoginPacket}, + ConnectionProtocol, PROTOCOL_VERSION, }, resolver, ServerAddress, }; @@ -15,10 +14,10 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { let resolved_address = resolver::resolve_address(address).await?; - let mut conn = Connection::new(&resolved_address).await?; + let mut conn = HandshakeConnection::new(&resolved_address).await?; // handshake - conn.send_packet( + conn.write( ClientIntentionPacket { protocol_version: PROTOCOL_VERSION, hostname: address.host.clone(), @@ -28,16 +27,15 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { .get(), ) .await; - conn.switch_state(ConnectionProtocol::Login); + let mut conn = conn.login(); // login start - conn.send_packet(ServerboundHelloPacket { username }.get()) - .await; + conn.write(ServerboundHelloPacket { username }.get()).await; // encryption request - let packet = conn.read_packet().await.unwrap(); + let packet = conn.read().await.unwrap(); let encryption_request_packet = match packet { - Packet::ClientboundHelloPacket(p) => p, + LoginPacket::ClientboundHelloPacket(p) => p, _ => Err(format!("Invalid packet type: {:?}", packet))?, }; diff --git a/minecraft-client/src/ping.rs b/minecraft-client/src/ping.rs index 05ea16da..88c0a24a 100644 --- a/minecraft-client/src/ping.rs +++ b/minecraft-client/src/ping.rs @@ -1,13 +1,13 @@ ///! Ping Minecraft servers. use minecraft_protocol::{ - connect::Connection, + connect::HandshakeConnection, packets::{ handshake::client_intention_packet::ClientIntentionPacket, status::{ clientbound_status_response_packet::ClientboundStatusResponsePacket, - serverbound_status_request_packet::ServerboundStatusRequestPacket, + serverbound_status_request_packet::ServerboundStatusRequestPacket, StatusPacket, }, - ConnectionProtocol, Packet, PROTOCOL_VERSION, + ConnectionProtocol, PROTOCOL_VERSION, }, resolver, ServerAddress, }; @@ -17,10 +17,10 @@ pub async fn ping_server( ) -> Result { let resolved_address = resolver::resolve_address(address).await?; - let mut conn = Connection::new(&resolved_address).await?; + let mut conn = HandshakeConnection::new(&resolved_address).await?; // send the client intention packet and switch to the status state - conn.send_packet( + conn.write( ClientIntentionPacket { protocol_version: PROTOCOL_VERSION, hostname: address.host.clone(), @@ -30,16 +30,15 @@ pub async fn ping_server( .get(), ) .await; - conn.switch_state(ConnectionProtocol::Status); + let mut conn = conn.status(); // send the empty status request packet - conn.send_packet(ServerboundStatusRequestPacket {}.get()) - .await; + conn.write(ServerboundStatusRequestPacket {}.get()).await; - let packet = conn.read_packet().await.unwrap(); + let packet = conn.read().await.unwrap(); Ok(match packet { - Packet::ClientboundStatusResponsePacket(p) => p, + StatusPacket::ClientboundStatusResponsePacket(p) => p, _ => Err("Invalid packet type".to_string())?, }) } diff --git a/minecraft-protocol/src/connect.rs b/minecraft-protocol/src/connect.rs index 5b750802..7c6a5f18 100644 --- a/minecraft-protocol/src/connect.rs +++ b/minecraft-protocol/src/connect.rs @@ -1,6 +1,12 @@ //! parse sending and receiving packets with a server. -use crate::packets::ConnectionProtocol; +use crate::packets::game::GamePacket; +use crate::packets::handshake::HandshakePacket; +use crate::packets::login::LoginPacket; +use crate::packets::status::StatusPacket; +use crate::packets::{ConnectionProtocol, ProtocolPacket}; +use crate::read::read_packet; +use crate::write::write_packet; use crate::{mc_buf, packets::Packet, ServerIpAddress}; use tokio::io::AsyncWriteExt; use tokio::{ @@ -13,15 +19,32 @@ pub enum PacketFlow { ServerToClient, } -pub struct Connection { - pub state: ConnectionProtocol, +pub struct HandshakeConnection { pub flow: PacketFlow, /// The buffered writer pub stream: TcpStream, } -impl Connection { - pub async fn new(address: &ServerIpAddress) -> Result { +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 { let ip = address.ip; let port = address.port; @@ -34,67 +57,65 @@ impl Connection { .set_nodelay(true) .expect("Error enabling tcp_nodelay"); - Ok(Connection { - state: ConnectionProtocol::Handshake, + Ok(HandshakeConnection { flow: PacketFlow::ServerToClient, stream, }) } - pub fn switch_state(&mut self, state: ConnectionProtocol) { - self.state = state; + pub fn login(self) -> LoginConnection { + LoginConnection { + flow: self.flow, + stream: self.stream, + } } - pub async fn read_packet(&mut self) -> Result { - // 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 + pub fn status(self) -> StatusConnection { + StatusConnection { + flow: self.flow, + stream: self.stream, + } + } - // 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, &mut self.stream); - - let (_packet_size, _packet_size_varint_size) = mc_buf::read_varint(&mut buf).await?; - - // then, minecraft tells us the packet id as a varint - let (packet_id, _packet_id_size) = mc_buf::read_varint(&mut buf).await?; - - // if we recognize the packet id, parse it - - let packet = Packet::read( - packet_id.try_into().unwrap(), - &self.state, - &self.flow, - &mut buf, - ) - .await?; - - Ok(packet) + pub async fn read(&mut self) -> Result { + read_packet::(&self.flow, &mut self.stream).await } /// Write a packet to the server - pub async fn send_packet(&mut self, packet: Packet) { - // TODO: implement compression - - // packet structure: - // length (varint) + id (varint) + data - - // write the packet id - let mut id_and_data_buf = vec![]; - mc_buf::write_varint(&mut id_and_data_buf, 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 = Vec::new(); - mc_buf::write_varint(&mut complete_buf, id_and_data_buf.len() as i32); - 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(); + pub async fn write(&mut self, packet: HandshakePacket) { + write_packet(packet, &mut self.stream).await; + } +} + +impl GameConnection { + pub async fn read(&mut self) -> Result { + read_packet::(&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 { + read_packet::(&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 { + read_packet::(&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 index 31b50164..684add45 100644 --- a/minecraft-protocol/src/lib.rs +++ b/minecraft-protocol/src/lib.rs @@ -6,7 +6,9 @@ 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 { diff --git a/minecraft-protocol/src/packets/game/mod.rs b/minecraft-protocol/src/packets/game/mod.rs index 08444697..06d8ae4c 100644 --- a/minecraft-protocol/src/packets/game/mod.rs +++ b/minecraft-protocol/src/packets/game/mod.rs @@ -1,2 +1,41 @@ +use async_trait::async_trait; +use tokio::io::BufReader; + +use crate::connect::PacketFlow; + +use super::ProtocolPacket; + #[derive(Clone, Debug)] -pub enum GamePacket {} +pub enum GamePacket +where + Self: Sized, {} + +#[async_trait] +impl ProtocolPacket for GamePacket { + fn id(&self) -> u32 { + match self { + _ => 0x00, + } + } + + fn write(&self, buf: &mut Vec) { + match self { + _ => (), + } + } + + /// Read a packet by its id, ConnectionProtocol, and flow + async fn read( + id: u32, + flow: &PacketFlow, + buf: &mut BufReader, + ) -> Result + 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/mod.rs b/minecraft-protocol/src/packets/handshake/mod.rs index 792588b5..01010e1e 100644 --- a/minecraft-protocol/src/packets/handshake/mod.rs +++ b/minecraft-protocol/src/packets/handshake/mod.rs @@ -1,7 +1,5 @@ pub mod client_intention_packet; -use std::f32::consts::E; - use async_trait::async_trait; use tokio::io::BufReader; diff --git a/minecraft-protocol/src/read.rs b/minecraft-protocol/src/read.rs new file mode 100644 index 00000000..7f4eeaac --- /dev/null +++ b/minecraft-protocol/src/read.rs @@ -0,0 +1,28 @@ +use tokio::{io::BufReader, net::TcpStream}; + +use crate::{connect::PacketFlow, mc_buf, packets::ProtocolPacket}; + +pub async fn read_packet( + flow: &PacketFlow, + stream: &mut TcpStream, +) -> Result { + // 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) = mc_buf::read_varint(&mut buf).await?; + + // then, minecraft tells us the packet id as a varint + let (packet_id, _packet_id_size) = mc_buf::read_varint(&mut buf).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/write.rs b/minecraft-protocol/src/write.rs new file mode 100644 index 00000000..529bb210 --- /dev/null +++ b/minecraft-protocol/src/write.rs @@ -0,0 +1,27 @@ +use tokio::{io::AsyncWriteExt, net::TcpStream}; + +use crate::{mc_buf, 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![]; + mc_buf::write_varint(&mut id_and_data_buf, 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 = Vec::new(); + mc_buf::write_varint(&mut complete_buf, 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(); +}