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:
parent
ace1405007
commit
732de94d7b
8 changed files with 192 additions and 80 deletions
|
@ -1,11 +1,10 @@
|
||||||
///! Connect to Minecraft servers.
|
///! Connect to Minecraft servers.
|
||||||
use minecraft_protocol::{
|
use minecraft_protocol::{
|
||||||
connect::Connection,
|
connect::HandshakeConnection,
|
||||||
packets::{
|
packets::{
|
||||||
handshake::client_intention_packet::ClientIntentionPacket,
|
handshake::client_intention_packet::ClientIntentionPacket,
|
||||||
login::serverbound_hello_packet::ServerboundHelloPacket,
|
login::{serverbound_hello_packet::ServerboundHelloPacket, LoginPacket},
|
||||||
status::clientbound_status_response_packet::ClientboundStatusResponsePacket,
|
ConnectionProtocol, PROTOCOL_VERSION,
|
||||||
ConnectionProtocol, Packet, PROTOCOL_VERSION,
|
|
||||||
},
|
},
|
||||||
resolver, ServerAddress,
|
resolver, ServerAddress,
|
||||||
};
|
};
|
||||||
|
@ -15,10 +14,10 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> {
|
||||||
|
|
||||||
let resolved_address = resolver::resolve_address(address).await?;
|
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
|
// handshake
|
||||||
conn.send_packet(
|
conn.write(
|
||||||
ClientIntentionPacket {
|
ClientIntentionPacket {
|
||||||
protocol_version: PROTOCOL_VERSION,
|
protocol_version: PROTOCOL_VERSION,
|
||||||
hostname: address.host.clone(),
|
hostname: address.host.clone(),
|
||||||
|
@ -28,16 +27,15 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> {
|
||||||
.get(),
|
.get(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
conn.switch_state(ConnectionProtocol::Login);
|
let mut conn = conn.login();
|
||||||
|
|
||||||
// login start
|
// login start
|
||||||
conn.send_packet(ServerboundHelloPacket { username }.get())
|
conn.write(ServerboundHelloPacket { username }.get()).await;
|
||||||
.await;
|
|
||||||
|
|
||||||
// encryption request
|
// encryption request
|
||||||
let packet = conn.read_packet().await.unwrap();
|
let packet = conn.read().await.unwrap();
|
||||||
let encryption_request_packet = match packet {
|
let encryption_request_packet = match packet {
|
||||||
Packet::ClientboundHelloPacket(p) => p,
|
LoginPacket::ClientboundHelloPacket(p) => p,
|
||||||
_ => Err(format!("Invalid packet type: {:?}", packet))?,
|
_ => Err(format!("Invalid packet type: {:?}", packet))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
///! Ping Minecraft servers.
|
///! Ping Minecraft servers.
|
||||||
use minecraft_protocol::{
|
use minecraft_protocol::{
|
||||||
connect::Connection,
|
connect::HandshakeConnection,
|
||||||
packets::{
|
packets::{
|
||||||
handshake::client_intention_packet::ClientIntentionPacket,
|
handshake::client_intention_packet::ClientIntentionPacket,
|
||||||
status::{
|
status::{
|
||||||
clientbound_status_response_packet::ClientboundStatusResponsePacket,
|
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,
|
resolver, ServerAddress,
|
||||||
};
|
};
|
||||||
|
@ -17,10 +17,10 @@ pub async fn ping_server(
|
||||||
) -> Result<ClientboundStatusResponsePacket, String> {
|
) -> Result<ClientboundStatusResponsePacket, String> {
|
||||||
let resolved_address = resolver::resolve_address(address).await?;
|
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
|
// send the client intention packet and switch to the status state
|
||||||
conn.send_packet(
|
conn.write(
|
||||||
ClientIntentionPacket {
|
ClientIntentionPacket {
|
||||||
protocol_version: PROTOCOL_VERSION,
|
protocol_version: PROTOCOL_VERSION,
|
||||||
hostname: address.host.clone(),
|
hostname: address.host.clone(),
|
||||||
|
@ -30,16 +30,15 @@ pub async fn ping_server(
|
||||||
.get(),
|
.get(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
conn.switch_state(ConnectionProtocol::Status);
|
let mut conn = conn.status();
|
||||||
|
|
||||||
// send the empty status request packet
|
// send the empty status request packet
|
||||||
conn.send_packet(ServerboundStatusRequestPacket {}.get())
|
conn.write(ServerboundStatusRequestPacket {}.get()).await;
|
||||||
.await;
|
|
||||||
|
|
||||||
let packet = conn.read_packet().await.unwrap();
|
let packet = conn.read().await.unwrap();
|
||||||
|
|
||||||
Ok(match packet {
|
Ok(match packet {
|
||||||
Packet::ClientboundStatusResponsePacket(p) => p,
|
StatusPacket::ClientboundStatusResponsePacket(p) => p,
|
||||||
_ => Err("Invalid packet type".to_string())?,
|
_ => Err("Invalid packet type".to_string())?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
//! parse sending and receiving packets with a server.
|
//! 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 crate::{mc_buf, packets::Packet, ServerIpAddress};
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
|
@ -13,15 +19,32 @@ pub enum PacketFlow {
|
||||||
ServerToClient,
|
ServerToClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Connection {
|
pub struct HandshakeConnection {
|
||||||
pub state: ConnectionProtocol,
|
|
||||||
pub flow: PacketFlow,
|
pub flow: PacketFlow,
|
||||||
/// The buffered writer
|
/// The buffered writer
|
||||||
pub stream: TcpStream,
|
pub stream: TcpStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connection {
|
pub struct GameConnection {
|
||||||
pub async fn new(address: &ServerIpAddress) -> Result<Connection, String> {
|
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 ip = address.ip;
|
||||||
let port = address.port;
|
let port = address.port;
|
||||||
|
|
||||||
|
@ -34,67 +57,65 @@ impl Connection {
|
||||||
.set_nodelay(true)
|
.set_nodelay(true)
|
||||||
.expect("Error enabling tcp_nodelay");
|
.expect("Error enabling tcp_nodelay");
|
||||||
|
|
||||||
Ok(Connection {
|
Ok(HandshakeConnection {
|
||||||
state: ConnectionProtocol::Handshake,
|
|
||||||
flow: PacketFlow::ServerToClient,
|
flow: PacketFlow::ServerToClient,
|
||||||
stream,
|
stream,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn switch_state(&mut self, state: ConnectionProtocol) {
|
pub fn login(self) -> LoginConnection {
|
||||||
self.state = state;
|
LoginConnection {
|
||||||
|
flow: self.flow,
|
||||||
|
stream: self.stream,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_packet(&mut self) -> Result<Packet, String> {
|
pub fn status(self) -> StatusConnection {
|
||||||
// what this does:
|
StatusConnection {
|
||||||
// 1. reads the first 5 bytes, probably only some of this will be used to get the packet length
|
flow: self.flow,
|
||||||
// 2. how much we should read = packet length - 5
|
stream: self.stream,
|
||||||
// 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
|
pub async fn read(&mut self) -> Result<HandshakePacket, String> {
|
||||||
let mut buf = BufReader::with_capacity(4 * 1024 * 1024, &mut self.stream);
|
read_packet::<HandshakePacket>(&self.flow, &mut self.stream).await
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a packet to the server
|
/// Write a packet to the server
|
||||||
pub async fn send_packet(&mut self, packet: Packet) {
|
pub async fn write(&mut self, packet: HandshakePacket) {
|
||||||
// TODO: implement compression
|
write_packet(packet, &mut self.stream).await;
|
||||||
|
}
|
||||||
// packet structure:
|
}
|
||||||
// length (varint) + id (varint) + data
|
|
||||||
|
impl GameConnection {
|
||||||
// write the packet id
|
pub async fn read(&mut self) -> Result<GamePacket, String> {
|
||||||
let mut id_and_data_buf = vec![];
|
read_packet::<GamePacket>(&self.flow, &mut self.stream).await
|
||||||
mc_buf::write_varint(&mut id_and_data_buf, packet.id() as i32);
|
}
|
||||||
packet.write(&mut id_and_data_buf);
|
|
||||||
|
/// Write a packet to the server
|
||||||
// write the packet data
|
pub async fn write(&mut self, packet: GamePacket) {
|
||||||
|
write_packet(packet, &mut self.stream).await;
|
||||||
// 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);
|
impl StatusConnection {
|
||||||
complete_buf.append(&mut id_and_data_buf);
|
pub async fn read(&mut self) -> Result<StatusPacket, String> {
|
||||||
|
read_packet::<StatusPacket>(&self.flow, &mut self.stream).await
|
||||||
// finally, write and flush to the stream
|
}
|
||||||
self.stream.write_all(&complete_buf).await.unwrap();
|
|
||||||
self.stream.flush().await.unwrap();
|
/// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,9 @@ use std::str::FromStr;
|
||||||
pub mod connect;
|
pub mod connect;
|
||||||
pub mod mc_buf;
|
pub mod mc_buf;
|
||||||
pub mod packets;
|
pub mod packets;
|
||||||
|
pub mod read;
|
||||||
pub mod resolver;
|
pub mod resolver;
|
||||||
|
pub mod write;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ServerAddress {
|
pub struct ServerAddress {
|
||||||
|
|
|
@ -1,2 +1,41 @@
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use tokio::io::BufReader;
|
||||||
|
|
||||||
|
use crate::connect::PacketFlow;
|
||||||
|
|
||||||
|
use super::ProtocolPacket;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
pub mod client_intention_packet;
|
pub mod client_intention_packet;
|
||||||
|
|
||||||
use std::f32::consts::E;
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use tokio::io::BufReader;
|
use tokio::io::BufReader;
|
||||||
|
|
||||||
|
|
28
minecraft-protocol/src/read.rs
Normal file
28
minecraft-protocol/src/read.rs
Normal 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)
|
||||||
|
}
|
27
minecraft-protocol/src/write.rs
Normal file
27
minecraft-protocol/src/write.rs
Normal 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();
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue