net-mc 0.1.1

A library for sending and receiving minecraft packets
Documentation
mod packets;
use net_mc::conn::Conn;
use net_mc::packet::Packet;
use rand::Rng;
use serde_json::json;
use sha1::Digest;
use std::{net::ToSocketAddrs, time::Duration};

fn main() {
    let http = reqwest::blocking::Client::new();

    let dc =
        ms_auth_mc::DeviceCode::new("389b1b32-b5d5-43b2-bddc-84ce938d6737", None, &http).unwrap();

    if let Some(inner) = &dc.inner {
        println!("{}", inner.message);
    }

    let auth = dc.authenticate(&http).unwrap();

    let addr = "mc.hypixel.net:25565"
        .to_socket_addrs()
        .unwrap()
        .next()
        .unwrap();
    
    let mut conn = Conn::connect_timeout(&addr, Duration::new(30, 0)).unwrap();

    let packet = packets::Handshake {
        protocol_version: 47,
        server_address: String::from("mc.hypixel.net"),
        server_port: 25565,
        next_state: packets::HandshakeState::Login,
    };
    conn.send_packet(&packet).unwrap();
    conn.send_packet(&packets::LoginStart { name: auth.name })
        .unwrap();

    loop {
        let packet = conn.read_packet().unwrap();
        match packet.id {
            packets::Disconnect::ID => {
                let packet = packets::Disconnect::decode(packet).unwrap();
                println!("disconnected: \"{}\"", packet.reason);
                break;
            }
            packets::EncryptionRequest::ID => {
                let packet = packets::EncryptionRequest::decode(packet).unwrap();
                let shared = rand::thread_rng().gen::<[u8; 16]>();

                let shared_e =
                    rsa_public_encrypt_pkcs1::encrypt(&packet.public_key, &shared).unwrap();
                let token_e =
                    rsa_public_encrypt_pkcs1::encrypt(&packet.public_key, &packet.verify_token)
                        .unwrap();

                let mut hasher = sha1::Sha1::new();
                hasher.update(&packet.server_id.as_bytes());
                hasher.update(&shared);
                hasher.update(packet.public_key);
                let mut hash = hasher.finalize();

                let negative = (hash[0] & 0x80) == 0x80;
                if negative {
                    let mut carry = true;
                    for i in (0..hash.len()).rev() {
                        hash[i] = !hash[i];
                        if carry {
                            carry = hash[i] == 0xFF;
                            hash[i] = hash[i].wrapping_add(1);
                        }
                    }
                }

                let hash_str = hash
                    .iter()
                    .map(|b| format!("{:02x}", b))
                    .collect::<Vec<String>>()
                    .join("");

                let hash_val = hash_str.trim_start_matches('0');
                let hash_str = if negative {
                    "-".to_owned() + &hash_val[..]
                } else {
                    hash_val.to_owned()
                };

                let _resp = http
                    .post("https://sessionserver.mojang.com/session/minecraft/join")
                    .json(&json!({
                        "accessToken": auth.token,
                        "selectedProfile": auth.uuid,
                        "serverId": hash_str,
                    }))
                    .send()
                    .unwrap()
                    .bytes()
                    .unwrap();

                conn.send_packet(&packets::EncryptionResponse {
                    shared_secret: shared_e,
                    verify_token: token_e,
                })
                .unwrap();

                conn.enable_encryption(&shared).unwrap();
            }
            packets::LoginSuccess::ID => {
                let packet = packets::LoginSuccess::decode(packet).unwrap();
                println!("logged in as {}", packet.username);
                break;
            }
            packets::SetCompression::ID => {
                let packet = packets::SetCompression::decode(packet).unwrap();
                conn.set_compression_threshhold(packet.threshold)
            }
            _ => {
                println!("Received packet: {:?}", packet.id);
            }
        }
    }
    loop {
        // Keeps the connection alive
        let packet = conn.read_packet().unwrap();
        if packet.id == packets::KeepAlive::ID {
            let packet = packets::KeepAlive::decode(packet).unwrap();
            conn.send_packet(&packets::KeepAlive {
                keep_alive: packet.keep_alive,
            })
            .unwrap();
        }
    }
}