mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
* start updating to 1.21.4 * fix block codegen and stop using block data from burger * rename packet related modules and structs to be simpler * ItemSlot -> ItemStack for more consistency with mojmap * .get() -> .into_packet() * simplify declare_state_packets by removing packet ids * rename read_from and write_into to azalea_read and azalea_write * rename McBufReadable and McBufWritable to AzaleaRead and AzaleaWrite * McBuf -> AzBuf * remove most uses of into_variant * update codegen and use resourcelocation names for packets * implement #[limit(i)] attribute for AzBuf derive macro * fixes for 1.21.4 * fix examples * update some physics code and fix ChatType * remove unused imports in codegen * re-add some things to migrate.py and update +mc version numbers automatically * downgrade to 1.21.3 lol
191 lines
5.5 KiB
Rust
191 lines
5.5 KiB
Rust
//! A low-level crate to send and receive Minecraft packets.
|
|
//!
|
|
//! You should probably use [`azalea`] or [`azalea_client`] instead, as
|
|
//! `azalea_protocol` delegates much of the work, such as auth, to the user of
|
|
//! the crate.
|
|
//!
|
|
//! [`azalea`]: https://crates.io/crates/azalea
|
|
//! [`azalea_client`]: https://crates.io/crates/azalea-client
|
|
//!
|
|
//! See [`crate::connect::Connection`] for an example.
|
|
|
|
// these two are necessary for thiserror backtraces
|
|
#![feature(error_generic_member_access)]
|
|
|
|
use std::{fmt::Display, net::SocketAddr, str::FromStr};
|
|
|
|
pub mod common;
|
|
#[cfg(feature = "connecting")]
|
|
pub mod connect;
|
|
#[cfg(feature = "packets")]
|
|
pub mod packets;
|
|
pub mod read;
|
|
pub mod resolver;
|
|
pub mod write;
|
|
|
|
/// A host and port. It's possible that the port doesn't resolve to anything.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// `ServerAddress` implements TryFrom<&str>, so you can use it like this:
|
|
/// ```
|
|
/// use azalea_protocol::ServerAddress;
|
|
///
|
|
/// let addr = ServerAddress::try_from("localhost:25565").unwrap();
|
|
/// assert_eq!(addr.host, "localhost");
|
|
/// assert_eq!(addr.port, 25565);
|
|
/// ```
|
|
#[derive(Debug, Clone)]
|
|
pub struct ServerAddress {
|
|
pub host: String,
|
|
pub port: u16,
|
|
}
|
|
|
|
impl TryFrom<&'_ str> for ServerAddress {
|
|
type Error = String;
|
|
|
|
/// Convert a Minecraft server address (host:port, the port is optional) to
|
|
/// a `ServerAddress`
|
|
fn try_from(string: &str) -> Result<Self, Self::Error> {
|
|
if string.is_empty() {
|
|
return Err("Empty string".to_string());
|
|
}
|
|
let mut parts = string.split(':');
|
|
let host = parts.next().ok_or("No host specified")?.to_string();
|
|
// default the port to 25565
|
|
let port = parts.next().unwrap_or("25565");
|
|
let port = u16::from_str(port).map_err(|_| "Invalid port specified")?;
|
|
Ok(ServerAddress { host, port })
|
|
}
|
|
}
|
|
|
|
impl From<SocketAddr> for ServerAddress {
|
|
/// Convert an existing `SocketAddr` into a `ServerAddress`. This just
|
|
/// converts the ip to a string and passes along the port. The resolver
|
|
/// will realize it's already an IP address and not do any DNS requests.
|
|
fn from(addr: SocketAddr) -> Self {
|
|
ServerAddress {
|
|
host: addr.ip().to_string(),
|
|
port: addr.port(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for ServerAddress {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}:{}", self.host, self.port)
|
|
}
|
|
}
|
|
|
|
/// Serde deserialization for ServerAddress. This is useful for config file
|
|
/// usage.
|
|
impl<'de> serde::Deserialize<'de> for ServerAddress {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let string = String::deserialize(deserializer)?;
|
|
ServerAddress::try_from(string.as_str()).map_err(serde::de::Error::custom)
|
|
}
|
|
}
|
|
|
|
/// Serde serialization for ServerAddress. This uses the Display impl, so it
|
|
/// will serialize to a string.
|
|
impl serde::Serialize for ServerAddress {
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: serde::Serializer,
|
|
{
|
|
serializer.serialize_str(&self.to_string())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::io::Cursor;
|
|
|
|
use bytes::BytesMut;
|
|
use uuid::Uuid;
|
|
|
|
use crate::{
|
|
packets::{
|
|
game::s_chat::{LastSeenMessagesUpdate, ServerboundChat},
|
|
login::{s_hello::ServerboundHello, ServerboundLoginPacket},
|
|
Packet,
|
|
},
|
|
read::{compression_decoder, read_packet},
|
|
write::{compression_encoder, serialize_packet, write_packet},
|
|
};
|
|
|
|
#[tokio::test]
|
|
async fn test_hello_packet() {
|
|
let packet = ServerboundHello {
|
|
name: "test".to_string(),
|
|
profile_id: Uuid::nil(),
|
|
};
|
|
let mut stream = Vec::new();
|
|
write_packet(&packet.into_variant(), &mut stream, None, &mut None)
|
|
.await
|
|
.unwrap();
|
|
|
|
let mut stream = Cursor::new(stream);
|
|
|
|
let _ = read_packet::<ServerboundLoginPacket, _>(
|
|
&mut stream,
|
|
&mut BytesMut::new(),
|
|
None,
|
|
&mut None,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_double_hello_packet() {
|
|
let packet = ServerboundHello {
|
|
name: "test".to_string(),
|
|
profile_id: Uuid::nil(),
|
|
}
|
|
.into_variant();
|
|
let mut stream = Vec::new();
|
|
write_packet(&packet, &mut stream, None, &mut None)
|
|
.await
|
|
.unwrap();
|
|
write_packet(&packet, &mut stream, None, &mut None)
|
|
.await
|
|
.unwrap();
|
|
let mut stream = Cursor::new(stream);
|
|
|
|
let mut buffer = BytesMut::new();
|
|
|
|
let _ = read_packet::<ServerboundLoginPacket, _>(&mut stream, &mut buffer, None, &mut None)
|
|
.await
|
|
.unwrap();
|
|
let _ = read_packet::<ServerboundLoginPacket, _>(&mut stream, &mut buffer, None, &mut None)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_read_long_compressed_chat() {
|
|
let compression_threshold = 256;
|
|
|
|
let buf = serialize_packet(
|
|
&ServerboundChat {
|
|
message: "a".repeat(256),
|
|
timestamp: 0,
|
|
salt: 0,
|
|
signature: None,
|
|
last_seen_messages: LastSeenMessagesUpdate::default(),
|
|
}
|
|
.into_variant(),
|
|
)
|
|
.unwrap();
|
|
|
|
let buf = compression_encoder(&buf, compression_threshold).unwrap();
|
|
|
|
println!("{:?}", buf);
|
|
|
|
compression_decoder(&mut Cursor::new(&buf), compression_threshold).unwrap();
|
|
}
|
|
}
|