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.
|
||||
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))?,
|
||||
};
|
||||
|
||||
|
|
|
@ -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())?,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
pub mod client_intention_packet;
|
||||
|
||||
use std::f32::consts::E;
|
||||
|
||||
use async_trait::async_trait;
|
||||
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