Struct azalea_protocol::connect::Connection

source ·
pub struct Connection<R: ProtocolPacket, W: ProtocolPacket> {
    pub reader: ReadConnection<R>,
    pub writer: WriteConnection<W>,
}
Expand description

A connection that can read and write packets.

§Examples

Join an offline-mode server and go through the handshake.

use azalea_protocol::{
    resolver,
    connect::Connection,
    packets::{
        ClientIntention, PROTOCOL_VERSION,
        login::{
            ClientboundLoginPacket,
            serverbound_hello_packet::ServerboundHelloPacket,
            serverbound_key_packet::ServerboundKeyPacket
        },
        handshaking::client_intention_packet::ClientIntentionPacket
    }
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resolved_address = resolver::resolve_address(&"localhost".try_into().unwrap()).await?;
    let mut conn = Connection::new(&resolved_address).await?;

    // handshake
    conn.write(
        ClientIntentionPacket {
            protocol_version: PROTOCOL_VERSION,
            hostname: resolved_address.ip().to_string(),
            port: resolved_address.port(),
            intention: ClientIntention::Login,
        }
        .get(),
    )
    .await?;

    let mut conn = conn.login();

    // login
    conn.write(
        ServerboundHelloPacket {
            name: "bot".to_string(),
            profile_id: uuid::Uuid::nil(),
        }
        .get(),
    )
    .await?;

    let (conn, game_profile) = loop {
        let packet = conn.read().await?;
        match packet {
            ClientboundLoginPacket::Hello(p) => {
                let e = azalea_crypto::encrypt(&p.public_key, &p.challenge).unwrap();

                conn.write(
                    ServerboundKeyPacket {
                        key_bytes: e.encrypted_public_key,
                        encrypted_challenge: e.encrypted_challenge,
                    }
                    .get(),
                )
                .await?;
                conn.set_encryption_key(e.secret_key);
            }
            ClientboundLoginPacket::LoginCompression(p) => {
                conn.set_compression_threshold(p.compression_threshold);
            }
            ClientboundLoginPacket::GameProfile(p) => {
                break (conn.configuration(), p.game_profile);
            }
            ClientboundLoginPacket::LoginDisconnect(p) => {
                eprintln!("login disconnect: {}", p.reason);
                return Err("login disconnect".into());
            }
            ClientboundLoginPacket::CustomQuery(p) => {}
            ClientboundLoginPacket::CookieRequest(_) => {}
        }
    };

    Ok(())
}

Fields§

§reader: ReadConnection<R>§writer: WriteConnection<W>

Implementations§

source§

impl<R, W> Connection<R, W>

source

pub async fn read(&mut self) -> Result<R, Box<ReadPacketError>>

Read a packet from the other side of the connection.

source

pub fn try_read(&mut self) -> Result<Option<R>, Box<ReadPacketError>>

Try to read a packet from the other side of the connection, or return Ok(None) if there’s no packet to read.

source

pub async fn write(&mut self, packet: W) -> Result<()>

Write a packet to the other side of the connection.

source

pub fn into_split(self) -> (ReadConnection<R>, WriteConnection<W>)

Split the reader and writer into two objects. This doesn’t allocate.

source§

impl Connection<ClientboundHandshakePacket, ServerboundHandshakePacket>

source

pub async fn new(address: &SocketAddr) -> Result<Self, ConnectionError>

Create a new connection to the given address.

source

pub async fn new_with_proxy( address: &SocketAddr, proxy: Proxy ) -> Result<Self, ConnectionError>

Create a new connection to the given address and Socks5 proxy. If you’re not using a proxy, use Self::new instead.

source

pub async fn new_from_stream(stream: TcpStream) -> Result<Self, ConnectionError>

Create a new connection from an existing stream. Useful if you want to set custom options on the stream. Otherwise, just use Self::new.

source

pub fn login(self) -> Connection<ClientboundLoginPacket, ServerboundLoginPacket>

Change our state from handshake to login. This is the state that is used for logging in.

source

pub fn status( self ) -> Connection<ClientboundStatusPacket, ServerboundStatusPacket>

Change our state from handshake to status. This is the state that is used for pinging the server.

source§

impl Connection<ClientboundLoginPacket, ServerboundLoginPacket>

source

pub fn set_compression_threshold(&mut self, threshold: i32)

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.

source

pub fn set_encryption_key(&mut self, key: [u8; 16])

Set the encryption key that is used to encrypt and decrypt packets. It’s the same for both reading and writing.

source

pub fn configuration( self ) -> Connection<ClientboundConfigurationPacket, ServerboundConfigurationPacket>

Change our state from login to configuration. This is the state where the server sends us the registries and resource pack and stuff.

source

pub async fn authenticate( &self, access_token: &str, uuid: &Uuid, private_key: [u8; 16], packet: &ClientboundHelloPacket ) -> Result<(), ClientSessionServerError>

Authenticate with Minecraft’s servers, which is required to join online-mode servers. This must happen when you get a ClientboundLoginPacket::Hello packet.

§Examples
use azalea_auth::AuthResult;
use azalea_protocol::connect::Connection;
use azalea_protocol::packets::login::{
    ClientboundLoginPacket,
    serverbound_key_packet::ServerboundKeyPacket
};
use uuid::Uuid;

let AuthResult { access_token, profile } = azalea_auth::auth(
    "[email protected]",
    azalea_auth::AuthOpts::default()
).await.expect("Couldn't authenticate");

let mut conn = Connection::new(&resolved_address).await?;

// transition to the login state, in a real program we would have done a handshake first
let mut conn = conn.login();

match conn.read().await? {
    ClientboundLoginPacket::Hello(p) => {
        // tell Mojang we're joining the server & enable encryption
        let e = azalea_crypto::encrypt(&p.public_key, &p.challenge).unwrap();
        conn.authenticate(
            &access_token,
            &profile.id,
            e.secret_key,
            &p
        ).await?;
        conn.write(
            ServerboundKeyPacket {
                key_bytes: e.encrypted_public_key,
                encrypted_challenge: e.encrypted_challenge,
            }.get()
        ).await?;
        conn.set_encryption_key(e.secret_key);
    }
    _ => {}
}
source§

impl Connection<ServerboundHandshakePacket, ClientboundHandshakePacket>

source

pub fn login(self) -> Connection<ServerboundLoginPacket, ClientboundLoginPacket>

Change our state from handshake to login. This is the state that is used for logging in.

source

pub fn status( self ) -> Connection<ServerboundStatusPacket, ClientboundStatusPacket>

Change our state from handshake to status. This is the state that is used for pinging the server.

source§

impl Connection<ServerboundLoginPacket, ClientboundLoginPacket>

source

pub fn set_compression_threshold(&mut self, threshold: i32)

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.

source

pub fn set_encryption_key(&mut self, key: [u8; 16])

Set the encryption key that is used to encrypt and decrypt packets. It’s the same for both reading and writing.

source

pub fn game(self) -> Connection<ServerboundGamePacket, ClientboundGamePacket>

Change our state from login to game. This is the state that’s used when the client is actually in the game.

source

pub async fn authenticate( &self, username: &str, public_key: &[u8], private_key: &[u8; 16], ip: Option<&str> ) -> Result<GameProfile, ServerSessionServerError>

Verify connecting clients have authenticated with Minecraft’s servers. This must happen after the client sends a ServerboundLoginPacket::Key packet.

source§

impl Connection<ClientboundConfigurationPacket, ServerboundConfigurationPacket>

source

pub fn game(self) -> Connection<ClientboundGamePacket, ServerboundGamePacket>

Change our state from configuration to game. This is the state that’s used when the client is actually in the world.

source§

impl Connection<ClientboundGamePacket, ServerboundGamePacket>

source

pub fn configuration( self ) -> Connection<ClientboundConfigurationPacket, ServerboundConfigurationPacket>

Change our state back to configuration.

source§

impl<R1, W1> Connection<R1, W1>

source

pub fn from<R2, W2>(connection: Connection<R1, W1>) -> Connection<R2, W2>

Creates a Connection of a type from a Connection of another type. Useful for servers or custom packets.

source

pub fn wrap(stream: TcpStream) -> Connection<R1, W1>

Convert an existing TcpStream into a Connection. Useful for servers.

source

pub fn unwrap(self) -> Result<TcpStream, ReuniteError>

Convert from a Connection into a TcpStream. Useful for servers.

Auto Trait Implementations§

§

impl<R, W> Freeze for Connection<R, W>

§

impl<R, W> RefUnwindSafe for Connection<R, W>

§

impl<R, W> Send for Connection<R, W>
where R: Send, W: Send,

§

impl<R, W> Sync for Connection<R, W>
where R: Sync, W: Sync,

§

impl<R, W> Unpin for Connection<R, W>
where R: Unpin, W: Unpin,

§

impl<R, W> UnwindSafe for Connection<R, W>
where R: UnwindSafe, W: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> AsAny for T
where T: Any,

§

fn as_any(&self) -> &(dyn Any + 'static)

§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

§

fn type_name(&self) -> &'static str

Gets the type name of self
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> Downcast for T
where T: AsAny + ?Sized,

§

fn is<T>(&self) -> bool
where T: AsAny,

Returns true if the boxed type is the same as T. Read more
§

fn downcast_ref<T>(&self) -> Option<&T>
where T: AsAny,

Forward to the method defined on the type Any.
§

fn downcast_mut<T>(&mut self) -> Option<&mut T>
where T: AsAny,

Forward to the method defined on the type Any.
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more