1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00

oh yeah it compiles

This commit is contained in:
mat 2021-12-15 13:43:57 -06:00
parent ace1405007
commit 732de94d7b
8 changed files with 192 additions and 80 deletions

View file

@ -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))?,
};

View file

@ -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<ClientboundStatusResponsePacket, 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?;
// 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())?,
})
}

View file

@ -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<Connection, String> {
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<HandshakeConnection, String> {
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<Packet, String> {
// 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<HandshakePacket, String> {
read_packet::<HandshakePacket>(&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<u8> = 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<GamePacket, String> {
read_packet::<GamePacket>(&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<StatusPacket, String> {
read_packet::<StatusPacket>(&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<LoginPacket, String> {
read_packet::<LoginPacket>(&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;
}
}

View file

@ -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 {

View file

@ -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<u8>) {
match self {
_ => (),
}
}
/// Read a packet by its id, ConnectionProtocol, and flow
async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
id: u32,
flow: &PacketFlow,
buf: &mut BufReader<T>,
) -> Result<GamePacket, String>
where
Self: Sized,
{
match flow {
PacketFlow::ServerToClient => Err("HandshakePacket::read not implemented".to_string()),
PacketFlow::ClientToServer => Err("HandshakePacket::read not implemented".to_string()),
}
}
}

View file

@ -1,7 +1,5 @@
pub mod client_intention_packet;
use std::f32::consts::E;
use async_trait::async_trait;
use tokio::io::BufReader;

View file

@ -0,0 +1,28 @@
use tokio::{io::BufReader, net::TcpStream};
use crate::{connect::PacketFlow, mc_buf, packets::ProtocolPacket};
pub async fn read_packet<P: ProtocolPacket>(
flow: &PacketFlow,
stream: &mut TcpStream,
) -> Result<P, String> {
// 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)
}

View file

@ -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<u8> = 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();
}