diff options
| author | mat <github@matdoes.dev> | 2022-10-23 22:59:38 -0500 |
|---|---|---|
| committer | mat <github@matdoes.dev> | 2022-10-23 22:59:38 -0500 |
| commit | 3869fd622f7515e934979478c439626981f0cd8a (patch) | |
| tree | 02861b45f20654aa086e7fb69ae54352b3f59810 /azalea-protocol/src | |
| parent | 65da123631b0a2dc078786f60fa6b213e8b430ee (diff) | |
| download | azalea-drasl-3869fd622f7515e934979478c439626981f0cd8a.tar.xz | |
write some more docs for az-protocol
Diffstat (limited to 'azalea-protocol/src')
| -rw-r--r-- | azalea-protocol/src/connect.rs | 98 | ||||
| -rwxr-xr-x | azalea-protocol/src/lib.rs | 25 | ||||
| -rwxr-xr-x | azalea-protocol/src/resolver.rs | 19 |
3 files changed, 113 insertions, 29 deletions
diff --git a/azalea-protocol/src/connect.rs b/azalea-protocol/src/connect.rs index da398ff9..06aedef9 100644 --- a/azalea-protocol/src/connect.rs +++ b/azalea-protocol/src/connect.rs @@ -1,4 +1,4 @@ -//! parse sending and receiving packets with a server. +//! Create connections that communicate with a remote server or client. use crate::packets::game::{ClientboundGamePacket, ServerboundGamePacket}; use crate::packets::handshake::{ClientboundHandshakePacket, ServerboundHandshakePacket}; @@ -8,18 +8,19 @@ use crate::packets::status::{ClientboundStatusPacket, ServerboundStatusPacket}; use crate::packets::ProtocolPacket; use crate::read::{read_packet, ReadPacketError}; use crate::write::write_packet; -use crate::ServerIpAddress; use azalea_auth::sessionserver::SessionServerError; use azalea_crypto::{Aes128CfbDec, Aes128CfbEnc}; use bytes::BytesMut; use std::fmt::Debug; use std::marker::PhantomData; +use std::net::SocketAddr; use thiserror::Error; use tokio::io::AsyncWriteExt; use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; use tokio::net::TcpStream; use uuid::Uuid; +/// The read half of a connection. pub struct ReadConnection<R: ProtocolPacket> { read_stream: OwnedReadHalf, buffer: BytesMut, @@ -28,6 +29,7 @@ pub struct ReadConnection<R: ProtocolPacket> { _reading: PhantomData<R>, } +/// The write half of a connection. pub struct WriteConnection<W: ProtocolPacket> { write_stream: OwnedWriteHalf, compression_threshold: Option<u32>, @@ -35,6 +37,76 @@ pub struct WriteConnection<W: ProtocolPacket> { _writing: PhantomData<W>, } +/// A connection that can read and write packets. +/// +/// # Examples +/// +/// Join an offline-mode server and go through the handshake. +/// ```rust,no_run +/// #[tokio::main] +/// async fn main() -> Result<(), Box<dyn std::error::Error>> { +/// let resolved_address = resolver::resolve_address(address).await?; +/// let mut conn = Connection::new(&resolved_address).await?; +/// +/// // handshake +/// conn.write( +/// ClientIntentionPacket { +/// protocol_version: PROTOCOL_VERSION, +/// hostname: address.host.to_string(), +/// port: address.port, +/// intention: ConnectionProtocol::Login, +/// } +/// .get(), +/// ) +/// .await?; +/// let mut conn = conn.login(); +/// +/// // login +/// conn.write( +/// ServerboundHelloPacket { +/// username, +/// public_key: None, +/// profile_id: None, +/// } +/// .get(), +/// ) +/// .await?; +/// +/// let (conn, game_profile) = loop { +/// let packet_result = conn.read().await; +/// match packet_result { +/// Ok(packet) => match packet { +/// ClientboundLoginPacket::Hello(p) => { +/// let e = azalea_crypto::encrypt(&p.public_key, &p.nonce).unwrap(); +/// +/// conn.write( +/// ServerboundKeyPacket { +/// nonce_or_salt_signature: NonceOrSaltSignature::Nonce(e.encrypted_nonce), +/// key_bytes: e.encrypted_public_key, +/// } +/// .get(), +/// ) +/// .await?; +/// conn.set_encryption_key(e.secret_key); } +/// ClientboundLoginPacket::LoginCompression(p) => { +/// conn.set_compression_threshold(p.compression_threshold); +/// } +/// ClientboundLoginPacket::GameProfile(p) => { +/// break (conn.game(), p.game_profile); +/// } +/// ClientboundLoginPacket::LoginDisconnect(p) => { +/// println!("login disconnect: {}", p.reason); +/// bail!(JoinError::Disconnected(p.reason)); +/// } +/// ClientboundLoginPacket::CustomQuery(p) => {} +/// }, +/// Err(e) => { +/// eprintln!("Error: {:?}", e); +/// bail!("Error: {:?}", e); +/// } +/// } +/// }; +/// ``` pub struct Connection<R: ProtocolPacket, W: ProtocolPacket> { pub reader: ReadConnection<R>, pub writer: WriteConnection<W>, @@ -58,7 +130,7 @@ impl<W> WriteConnection<W> where W: ProtocolPacket + Debug, { - /// Write a packet to the server + /// Write a packet to the server. pub async fn write(&mut self, packet: W) -> std::io::Result<()> { write_packet( &packet, @@ -69,6 +141,7 @@ where .await } + /// End the connection. pub async fn shutdown(&mut self) -> std::io::Result<()> { self.write_stream.shutdown().await } @@ -79,11 +152,12 @@ where R: ProtocolPacket + Debug, W: ProtocolPacket + Debug, { + /// Read a packet from the other side of the connection. pub async fn read(&mut self) -> Result<R, ReadPacketError> { self.reader.read().await } - /// Write a packet to the server + /// Write a packet to the other side of the connection. pub async fn write(&mut self, packet: W) -> std::io::Result<()> { self.writer.write(packet).await } @@ -101,11 +175,9 @@ pub enum ConnectionError { } impl Connection<ClientboundHandshakePacket, ServerboundHandshakePacket> { - pub async fn new(address: &ServerIpAddress) -> Result<Self, ConnectionError> { - let ip = address.ip; - let port = address.port; - - let stream = TcpStream::connect(format!("{}:{}", ip, port)).await?; + /// Create a new connection to the given address. + pub async fn new(address: &SocketAddr) -> Result<Self, ConnectionError> { + let stream = TcpStream::connect(address).await?; // enable tcp_nodelay stream.set_nodelay(true)?; @@ -129,16 +201,21 @@ impl Connection<ClientboundHandshakePacket, ServerboundHandshakePacket> { }) } + /// Change our state from handshake to login. This is the state that is used for logging in. pub fn login(self) -> Connection<ClientboundLoginPacket, ServerboundLoginPacket> { Connection::from(self) } + /// Change our state from handshake to status. This is the state that is used for pinging the server. pub fn status(self) -> Connection<ClientboundStatusPacket, ServerboundStatusPacket> { Connection::from(self) } } impl Connection<ClientboundLoginPacket, ServerboundLoginPacket> { + /// Set our compression threshold, i.e. the maximum size that a packet is + /// allowed to be without getting compressed. If you set it to less than 0 + /// then compression gets disabled. pub fn set_compression_threshold(&mut self, threshold: i32) { // if you pass a threshold of less than 0, compression is disabled if threshold >= 0 { @@ -150,13 +227,14 @@ impl Connection<ClientboundLoginPacket, ServerboundLoginPacket> { } } + /// Set the encryption key that is used to encrypt and decrypt packets. It's the same for both reading and writing. pub fn set_encryption_key(&mut self, key: [u8; 16]) { - // minecraft has a cipher decoder and encoder, i don't think it matters though? let (enc_cipher, dec_cipher) = azalea_crypto::create_cipher(&key); self.reader.dec_cipher = Some(dec_cipher); self.writer.enc_cipher = Some(enc_cipher); } + /// Change our state from login to game. This is the state that's used when you're actually in the game. pub fn game(self) -> Connection<ClientboundGamePacket, ServerboundGamePacket> { Connection::from(self) } diff --git a/azalea-protocol/src/lib.rs b/azalea-protocol/src/lib.rs index 58ffac0a..ab447210 100755 --- a/azalea-protocol/src/lib.rs +++ b/azalea-protocol/src/lib.rs @@ -1,10 +1,13 @@ -//! This lib is responsible for parsing Minecraft packets. +//! A low-level crate to send and receive Minecraft packets. +//! +//! You should probably use [`azalea`] or [`azalea_client`] instead, as +//! azalea_protocol delegates much of the work, such as auth, to the user of +//! the crate. // these two are necessary for thiserror backtraces #![feature(error_generic_member_access)] #![feature(provide_any)] -use std::net::IpAddr; use std::str::FromStr; #[cfg(feature = "connecting")] @@ -15,18 +18,24 @@ pub mod read; pub mod resolver; pub mod write; +/// A host and port. It's possible that the port doesn't resolve to anything. +/// +/// # Examples +/// +/// ServerAddress implements TryFrom<&str>, so you can use it like this: +/// ``` +/// use azalea_protocol::ServerAddress; +/// +/// let addr = ServerAddress::try_from("localhost:25565").unwrap(); +/// assert_eq!(addr.host, "localhost"); +/// assert_eq!(addr.port, 25565); +/// ``` #[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; diff --git a/azalea-protocol/src/resolver.rs b/azalea-protocol/src/resolver.rs index eb081c3f..b3455a59 100755 --- a/azalea-protocol/src/resolver.rs +++ b/azalea-protocol/src/resolver.rs @@ -1,6 +1,6 @@ -use crate::{ServerAddress, ServerIpAddress}; +use crate::ServerAddress; use async_recursion::async_recursion; -use std::net::IpAddr; +use std::net::{IpAddr, SocketAddr}; use thiserror::Error; use trust_dns_resolver::{ config::{ResolverConfig, ResolverOpts}, @@ -18,13 +18,10 @@ pub enum ResolverError { /// 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, ResolverError> { +pub async fn resolve_address(address: &ServerAddress) -> Result<SocketAddr, ResolverError> { // 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, - }); + return Ok(SocketAddr::new(ip, 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 @@ -56,8 +53,8 @@ pub async fn resolve_address(address: &ServerAddress) -> Result<ServerIpAddress, let lookup_ip_result = resolver.lookup_ip(address.host.clone()).await; let lookup_ip = lookup_ip_result.map_err(|_| ResolverError::NoIp)?; - Ok(ServerIpAddress { - ip: lookup_ip.iter().next().unwrap(), - port: address.port, - }) + Ok(SocketAddr::new( + lookup_ip.iter().next().unwrap(), + address.port, + )) } |
