mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
More packet fixes, tests, handle error (#61)
* Fix packet, fix tests, fixedbitsets * Clippy: Nightmare Mode * Fix mistake * simplify impl Display and make thing pub --------- Co-authored-by: mat <github@matdoes.dev>
This commit is contained in:
parent
2539f948c7
commit
6e818852d8
28 changed files with 499 additions and 333 deletions
|
@ -24,6 +24,8 @@ pub enum ClientSessionServerError {
|
|||
Unknown(String),
|
||||
#[error("Forbidden operation (expired session?)")]
|
||||
ForbiddenOperation,
|
||||
#[error("RateLimiter disallowed request")]
|
||||
RateLimited,
|
||||
#[error("Unexpected response from sessionserver (status code {status_code}): {body}")]
|
||||
UnexpectedResponse { status_code: u16, body: String },
|
||||
}
|
||||
|
@ -95,6 +97,7 @@ pub async fn join(
|
|||
_ => Err(ClientSessionServerError::Unknown(forbidden.error)),
|
||||
}
|
||||
}
|
||||
StatusCode::TOO_MANY_REQUESTS => Err(ClientSessionServerError::RateLimited),
|
||||
status_code => {
|
||||
// log the headers
|
||||
debug!("Error headers: {:#?}", res.headers());
|
||||
|
|
|
@ -150,7 +150,7 @@ async fn handle_connection(stream: TcpStream) -> anyhow::Result<()> {
|
|||
if let Some(id) = hello.profile_id {
|
||||
id.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
String::new()
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -189,6 +189,7 @@ where
|
|||
}
|
||||
|
||||
/// Split the reader and writer into two objects. This doesn't allocate.
|
||||
#[must_use]
|
||||
pub fn into_split(self) -> (ReadConnection<R>, WriteConnection<W>) {
|
||||
(self.reader, self.writer)
|
||||
}
|
||||
|
@ -229,12 +230,14 @@ impl Connection<ClientboundHandshakePacket, ServerboundHandshakePacket> {
|
|||
|
||||
/// Change our state from handshake to login. This is the state that is used
|
||||
/// for logging in.
|
||||
#[must_use]
|
||||
pub fn login(self) -> Connection<ClientboundLoginPacket, ServerboundLoginPacket> {
|
||||
Connection::from(self)
|
||||
}
|
||||
|
||||
/// Change our state from handshake to status. This is the state that is
|
||||
/// used for pinging the server.
|
||||
#[must_use]
|
||||
pub fn status(self) -> Connection<ClientboundStatusPacket, ServerboundStatusPacket> {
|
||||
Connection::from(self)
|
||||
}
|
||||
|
@ -265,6 +268,7 @@ impl Connection<ClientboundLoginPacket, ServerboundLoginPacket> {
|
|||
|
||||
/// Change our state from login to game. This is the state that's used when
|
||||
/// you're actually in the game.
|
||||
#[must_use]
|
||||
pub fn game(self) -> Connection<ClientboundGamePacket, ServerboundGamePacket> {
|
||||
Connection::from(self)
|
||||
}
|
||||
|
@ -343,12 +347,14 @@ impl Connection<ClientboundLoginPacket, ServerboundLoginPacket> {
|
|||
impl Connection<ServerboundHandshakePacket, ClientboundHandshakePacket> {
|
||||
/// Change our state from handshake to login. This is the state that is used
|
||||
/// for logging in.
|
||||
#[must_use]
|
||||
pub fn login(self) -> Connection<ServerboundLoginPacket, ClientboundLoginPacket> {
|
||||
Connection::from(self)
|
||||
}
|
||||
|
||||
/// Change our state from handshake to status. This is the state that is
|
||||
/// used for pinging the server.
|
||||
#[must_use]
|
||||
pub fn status(self) -> Connection<ServerboundStatusPacket, ClientboundStatusPacket> {
|
||||
Connection::from(self)
|
||||
}
|
||||
|
@ -379,6 +385,7 @@ impl Connection<ServerboundLoginPacket, ClientboundLoginPacket> {
|
|||
|
||||
/// Change our state from login to game. This is the state that's used when
|
||||
/// the client is actually in the game.
|
||||
#[must_use]
|
||||
pub fn game(self) -> Connection<ServerboundGamePacket, ClientboundGamePacket> {
|
||||
Connection::from(self)
|
||||
}
|
||||
|
@ -406,6 +413,7 @@ where
|
|||
{
|
||||
/// Creates a `Connection` of a type from a `Connection` of another type.
|
||||
/// Useful for servers or custom packets.
|
||||
#[must_use]
|
||||
pub fn from<R2, W2>(connection: Connection<R1, W1>) -> Connection<R2, W2>
|
||||
where
|
||||
R2: ProtocolPacket + Debug,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! 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
|
||||
//! `azalea_protocol` delegates much of the work, such as auth, to the user of
|
||||
//! the crate.
|
||||
//!
|
||||
//! [`azalea`]: https://crates.io/crates/azalea
|
||||
|
@ -13,7 +13,7 @@
|
|||
#![feature(error_generic_member_access)]
|
||||
#![feature(provide_any)]
|
||||
|
||||
use std::{net::SocketAddr, str::FromStr};
|
||||
use std::{fmt::Display, net::SocketAddr, str::FromStr};
|
||||
|
||||
#[cfg(feature = "connecting")]
|
||||
pub mod connect;
|
||||
|
@ -27,7 +27,7 @@ pub mod write;
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ServerAddress implements TryFrom<&str>, so you can use it like this:
|
||||
/// `ServerAddress` implements TryFrom<&str>, so you can use it like this:
|
||||
/// ```
|
||||
/// use azalea_protocol::ServerAddress;
|
||||
///
|
||||
|
@ -45,7 +45,7 @@ impl<'a> TryFrom<&'a str> for ServerAddress {
|
|||
type Error = String;
|
||||
|
||||
/// Convert a Minecraft server address (host:port, the port is optional) to
|
||||
/// a ServerAddress
|
||||
/// a `ServerAddress`
|
||||
fn try_from(string: &str) -> Result<Self, Self::Error> {
|
||||
if string.is_empty() {
|
||||
return Err("Empty string".to_string());
|
||||
|
@ -60,9 +60,9 @@ impl<'a> TryFrom<&'a str> for ServerAddress {
|
|||
}
|
||||
|
||||
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.
|
||||
/// 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(),
|
||||
|
@ -71,6 +71,12 @@ impl From<SocketAddr> for ServerAddress {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for ServerAddress {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}:{}", self.host, self.port)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Cursor;
|
||||
|
|
|
@ -2,6 +2,7 @@ use azalea_buf::{
|
|||
BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
|
||||
};
|
||||
use azalea_chat::Component;
|
||||
use azalea_core::FixedBitSet;
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
use std::io::Cursor;
|
||||
use std::io::Write;
|
||||
|
@ -116,28 +117,28 @@ pub struct Properties {
|
|||
|
||||
impl McBufReadable for Properties {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let byte = u8::read_from(buf)?;
|
||||
let set = FixedBitSet::<3>::read_from(buf)?;
|
||||
Ok(Self {
|
||||
darken_screen: byte & 1 != 0,
|
||||
play_music: byte & 2 != 0,
|
||||
create_world_fog: byte & 4 != 0,
|
||||
darken_screen: set.index(0),
|
||||
play_music: set.index(1),
|
||||
create_world_fog: set.index(2),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for Properties {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut byte = 0;
|
||||
let mut set = FixedBitSet::<3>::new();
|
||||
if self.darken_screen {
|
||||
byte |= 1;
|
||||
set.set(0);
|
||||
}
|
||||
if self.play_music {
|
||||
byte |= 2;
|
||||
set.set(1);
|
||||
}
|
||||
if self.create_world_fog {
|
||||
byte |= 4;
|
||||
set.set(2);
|
||||
}
|
||||
u8::write_into(&byte, buf)?;
|
||||
set.write_into(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use azalea_buf::BufReadError;
|
|||
use azalea_buf::McBuf;
|
||||
use azalea_buf::McBufVarReadable;
|
||||
use azalea_buf::{McBufReadable, McBufVarWritable, McBufWritable};
|
||||
use azalea_core::FixedBitSet;
|
||||
use azalea_core::ResourceLocation;
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
use log::warn;
|
||||
|
@ -15,7 +16,7 @@ pub struct ClientboundCommandsPacket {
|
|||
pub root_index: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct BrigadierNodeStub {
|
||||
pub is_executable: bool,
|
||||
pub children: Vec<u32>,
|
||||
|
@ -23,7 +24,7 @@ pub struct BrigadierNodeStub {
|
|||
pub node_type: NodeType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub struct BrigadierNumber<T> {
|
||||
pub min: Option<T>,
|
||||
pub max: Option<T>,
|
||||
|
@ -33,15 +34,29 @@ impl<T> BrigadierNumber<T> {
|
|||
BrigadierNumber { min, max }
|
||||
}
|
||||
}
|
||||
impl<T: PartialEq> PartialEq for BrigadierNumber<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (&self.min, &self.max, &other.min, &other.max) {
|
||||
(Some(f_min), None, Some(s_min), None) => f_min == s_min,
|
||||
(None, Some(f_max), None, Some(s_max)) => f_max == s_max,
|
||||
(Some(f_min), Some(f_max), Some(s_min), Some(s_max)) => {
|
||||
f_min == s_min && f_max == s_max
|
||||
}
|
||||
(None, None, None, None) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: McBufReadable> McBufReadable for BrigadierNumber<T> {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let flags = u8::read_from(buf)?;
|
||||
let min = if flags & 0x01 != 0 {
|
||||
let flags = FixedBitSet::<2>::read_from(buf)?;
|
||||
let min = if flags.index(0) {
|
||||
Some(T::read_from(buf)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let max = if flags & 0x02 != 0 {
|
||||
let max = if flags.index(1) {
|
||||
Some(T::read_from(buf)?)
|
||||
} else {
|
||||
None
|
||||
|
@ -51,12 +66,12 @@ impl<T: McBufReadable> McBufReadable for BrigadierNumber<T> {
|
|||
}
|
||||
impl<T: McBufWritable> McBufWritable for BrigadierNumber<T> {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut flags: u8 = 0;
|
||||
let mut flags = FixedBitSet::<2>::new();
|
||||
if self.min.is_some() {
|
||||
flags |= 0x01;
|
||||
flags.set(0);
|
||||
}
|
||||
if self.max.is_some() {
|
||||
flags |= 0x02;
|
||||
flags.set(1);
|
||||
}
|
||||
flags.write_into(buf)?;
|
||||
if let Some(min) = &self.min {
|
||||
|
@ -69,7 +84,7 @@ impl<T: McBufWritable> McBufWritable for BrigadierNumber<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, McBuf)]
|
||||
#[derive(Debug, Clone, Copy, McBuf, PartialEq, Eq)]
|
||||
pub enum BrigadierString {
|
||||
/// Reads a single word
|
||||
SingleWord = 0,
|
||||
|
@ -80,7 +95,7 @@ pub enum BrigadierString {
|
|||
GreedyPhrase = 2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, McBuf)]
|
||||
#[derive(Debug, Clone, McBuf, PartialEq)]
|
||||
pub enum BrigadierParser {
|
||||
Bool,
|
||||
Float(BrigadierNumber<f32>),
|
||||
|
@ -132,28 +147,28 @@ pub enum BrigadierParser {
|
|||
Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EntityParser {
|
||||
pub single: bool,
|
||||
pub players_only: bool,
|
||||
}
|
||||
impl McBufReadable for EntityParser {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let flags = u8::read_from(buf)?;
|
||||
let flags = FixedBitSet::<2>::read_from(buf)?;
|
||||
Ok(EntityParser {
|
||||
single: flags & 0x01 != 0,
|
||||
players_only: flags & 0x02 != 0,
|
||||
single: flags.index(0),
|
||||
players_only: flags.index(1),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl McBufWritable for EntityParser {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut flags: u8 = 0;
|
||||
let mut flags = FixedBitSet::<2>::new();
|
||||
if self.single {
|
||||
flags |= 0x01;
|
||||
flags.set(0);
|
||||
}
|
||||
if self.players_only {
|
||||
flags |= 0x02;
|
||||
flags.set(1);
|
||||
}
|
||||
flags.write_into(buf)?;
|
||||
Ok(())
|
||||
|
@ -163,17 +178,15 @@ impl McBufWritable for EntityParser {
|
|||
// TODO: BrigadierNodeStub should have more stuff
|
||||
impl McBufReadable for BrigadierNodeStub {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let flags = u8::read_from(buf)?;
|
||||
if flags > 31 {
|
||||
warn!(
|
||||
"Warning: The flags from a Brigadier node are over 31 ({flags}; {flags:#b}). This is probably a bug.",
|
||||
);
|
||||
let flags = FixedBitSet::<8>::read_from(buf)?;
|
||||
if flags.index(5) || flags.index(6) || flags.index(7) {
|
||||
warn!("Warning: The flags from a Brigadier node are over 31. This is probably a bug.",);
|
||||
}
|
||||
|
||||
let node_type = flags & 0x03;
|
||||
let is_executable = flags & 0x04 != 0;
|
||||
let has_redirect = flags & 0x08 != 0;
|
||||
let has_suggestions_type = flags & 0x10 != 0;
|
||||
let node_type = u8::from(flags.index(0)) + (u8::from(flags.index(1)) * 2);
|
||||
let is_executable = flags.index(2);
|
||||
let has_redirect = flags.index(3);
|
||||
let has_suggestions_type = flags.index(4);
|
||||
|
||||
let children = Vec::<u32>::var_read_from(buf)?;
|
||||
let redirect_node = if has_redirect {
|
||||
|
@ -224,16 +237,17 @@ impl McBufReadable for BrigadierNodeStub {
|
|||
|
||||
impl McBufWritable for BrigadierNodeStub {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut flags = FixedBitSet::<4>::new();
|
||||
if self.is_executable {
|
||||
flags.set(2);
|
||||
}
|
||||
if self.redirect_node.is_some() {
|
||||
flags.set(3);
|
||||
}
|
||||
|
||||
match &self.node_type {
|
||||
NodeType::Root => {
|
||||
let mut flags = 0x00;
|
||||
if self.is_executable {
|
||||
flags |= 0x04;
|
||||
}
|
||||
if self.redirect_node.is_some() {
|
||||
flags |= 0x08;
|
||||
}
|
||||
flags.var_write_into(buf)?;
|
||||
flags.write_into(buf)?;
|
||||
|
||||
self.children.var_write_into(buf)?;
|
||||
|
||||
|
@ -242,14 +256,8 @@ impl McBufWritable for BrigadierNodeStub {
|
|||
}
|
||||
}
|
||||
NodeType::Literal { name } => {
|
||||
let mut flags = 0x01;
|
||||
if self.is_executable {
|
||||
flags |= 0x04;
|
||||
}
|
||||
if self.redirect_node.is_some() {
|
||||
flags |= 0x08;
|
||||
}
|
||||
flags.var_write_into(buf)?;
|
||||
flags.set(0);
|
||||
flags.write_into(buf)?;
|
||||
|
||||
self.children.var_write_into(buf)?;
|
||||
|
||||
|
@ -264,17 +272,11 @@ impl McBufWritable for BrigadierNodeStub {
|
|||
parser,
|
||||
suggestions_type,
|
||||
} => {
|
||||
let mut flags = 0x02;
|
||||
if self.is_executable {
|
||||
flags |= 0x04;
|
||||
}
|
||||
if self.redirect_node.is_some() {
|
||||
flags |= 0x08;
|
||||
}
|
||||
flags.set(1);
|
||||
if suggestions_type.is_some() {
|
||||
flags |= 0x10;
|
||||
flags.set(4);
|
||||
}
|
||||
flags.var_write_into(buf)?;
|
||||
flags.write_into(buf)?;
|
||||
|
||||
self.children.var_write_into(buf)?;
|
||||
|
||||
|
@ -294,7 +296,7 @@ impl McBufWritable for BrigadierNodeStub {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum NodeType {
|
||||
Root,
|
||||
Literal {
|
||||
|
@ -308,11 +310,67 @@ pub enum NodeType {
|
|||
}
|
||||
|
||||
impl BrigadierNodeStub {
|
||||
#[must_use]
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
match &self.node_type {
|
||||
NodeType::Root => None,
|
||||
NodeType::Literal { name } => Some(name),
|
||||
NodeType::Argument { name, .. } => Some(name),
|
||||
NodeType::Literal { name } | NodeType::Argument { name, .. } => Some(name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_brigadier_node_stub_root() {
|
||||
let data = BrigadierNodeStub {
|
||||
is_executable: false,
|
||||
children: vec![1, 2],
|
||||
redirect_node: None,
|
||||
node_type: NodeType::Root,
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
data.write_into(&mut buf).unwrap();
|
||||
let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
|
||||
let read_data = BrigadierNodeStub::read_from(&mut data_cursor).unwrap();
|
||||
assert_eq!(data, read_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_brigadier_node_stub_literal() {
|
||||
let data = BrigadierNodeStub {
|
||||
is_executable: true,
|
||||
children: vec![],
|
||||
redirect_node: None,
|
||||
node_type: NodeType::Literal {
|
||||
name: "String".to_string(),
|
||||
},
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
data.write_into(&mut buf).unwrap();
|
||||
let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
|
||||
let read_data = BrigadierNodeStub::read_from(&mut data_cursor).unwrap();
|
||||
assert_eq!(data, read_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_brigadier_node_stub_argument() {
|
||||
let data = BrigadierNodeStub {
|
||||
is_executable: false,
|
||||
children: vec![6, 9],
|
||||
redirect_node: Some(5),
|
||||
node_type: NodeType::Argument {
|
||||
name: "position".to_string(),
|
||||
parser: BrigadierParser::Vec3,
|
||||
suggestions_type: Some(ResourceLocation::new("minecraft:test_suggestion").unwrap()),
|
||||
},
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
data.write_into(&mut buf).unwrap();
|
||||
let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
|
||||
let read_data = BrigadierNodeStub::read_from(&mut data_cursor).unwrap();
|
||||
assert_eq!(data, read_data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,9 +31,9 @@ impl McBufReadable for ClientboundExplodePacket {
|
|||
let mut to_blow = Vec::with_capacity(to_blow_len as usize);
|
||||
for _ in 0..to_blow_len {
|
||||
// the bytes are offsets from the main x y z
|
||||
let x = x_floor + i8::read_from(buf)? as i32;
|
||||
let y = y_floor + i8::read_from(buf)? as i32;
|
||||
let z = z_floor + i8::read_from(buf)? as i32;
|
||||
let x = x_floor + i32::from(i8::read_from(buf)?);
|
||||
let y = y_floor + i32::from(i8::read_from(buf)?);
|
||||
let z = z_floor + i32::from(i8::read_from(buf)?);
|
||||
to_blow.push(BlockPos { x, y, z });
|
||||
}
|
||||
|
||||
|
|
|
@ -1,71 +1,15 @@
|
|||
use azalea_buf::{BufReadError, McBuf};
|
||||
use azalea_buf::{McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable};
|
||||
use azalea_buf::{McBuf, McBufReadable, McBufWritable};
|
||||
use azalea_chat::Component;
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
use std::io::{Cursor, Write};
|
||||
|
||||
#[derive(Clone, Debug, ClientboundGamePacket)]
|
||||
#[derive(Clone, Debug, ClientboundGamePacket, McBuf)]
|
||||
pub struct ClientboundMapItemDataPacket {
|
||||
// #[var]
|
||||
#[var]
|
||||
pub map_id: u32,
|
||||
pub scale: u8,
|
||||
pub locked: bool,
|
||||
pub decorations: Vec<MapDecoration>,
|
||||
pub color_patch: Option<MapPatch>,
|
||||
}
|
||||
|
||||
impl McBufReadable for ClientboundMapItemDataPacket {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let map_id = u32::var_read_from(buf)?;
|
||||
let scale = u8::read_from(buf)?;
|
||||
let locked = bool::read_from(buf)?;
|
||||
let decorations = Option::<Vec<MapDecoration>>::read_from(buf)?.unwrap_or_default();
|
||||
|
||||
let width = u8::read_from(buf)?;
|
||||
let color_patch = if width == 0 {
|
||||
None
|
||||
} else {
|
||||
let height = u8::read_from(buf)?;
|
||||
let start_x = u8::read_from(buf)?;
|
||||
let start_y = u8::read_from(buf)?;
|
||||
let map_colors = Vec::<u8>::read_from(buf)?;
|
||||
Some(MapPatch {
|
||||
width,
|
||||
height,
|
||||
start_x,
|
||||
start_y,
|
||||
map_colors,
|
||||
})
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
map_id,
|
||||
scale,
|
||||
locked,
|
||||
decorations,
|
||||
color_patch,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for ClientboundMapItemDataPacket {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
self.map_id.var_write_into(buf)?;
|
||||
self.scale.write_into(buf)?;
|
||||
self.locked.write_into(buf)?;
|
||||
(!self.decorations.is_empty()).write_into(buf)?;
|
||||
self.decorations.write_into(buf)?;
|
||||
if let Some(color_patch) = &self.color_patch {
|
||||
color_patch.width.write_into(buf)?;
|
||||
color_patch.height.write_into(buf)?;
|
||||
color_patch.start_x.write_into(buf)?;
|
||||
color_patch.start_y.write_into(buf)?;
|
||||
color_patch.map_colors.write_into(buf)?;
|
||||
} else {
|
||||
0u8.write_into(buf)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub decorations: Option<Vec<MapDecoration>>,
|
||||
pub color_patch: OptionalMapPatch,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
|
@ -80,11 +24,35 @@ pub struct MapDecoration {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OptionalMapPatch(pub Option<MapPatch>);
|
||||
|
||||
impl McBufReadable for OptionalMapPatch {
|
||||
fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> {
|
||||
let pos = buf.position();
|
||||
Ok(Self(if u8::read_from(buf)? == 0 {
|
||||
None
|
||||
} else {
|
||||
buf.set_position(pos);
|
||||
Some(MapPatch::read_from(buf)?)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for OptionalMapPatch {
|
||||
fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> {
|
||||
match &self.0 {
|
||||
None => 0u8.write_into(buf),
|
||||
Some(m) => m.write_into(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, McBuf)]
|
||||
pub struct MapPatch {
|
||||
pub start_x: u8,
|
||||
pub start_y: u8,
|
||||
pub width: u8,
|
||||
pub height: u8,
|
||||
pub start_x: u8,
|
||||
pub start_y: u8,
|
||||
pub map_colors: Vec<u8>,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use azalea_buf::{BufReadError, McBuf};
|
||||
use azalea_buf::{McBufReadable, McBufWritable};
|
||||
use azalea_core::FixedBitSet;
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
use std::io::{Cursor, Write};
|
||||
|
||||
|
@ -21,31 +22,31 @@ pub struct PlayerAbilitiesFlags {
|
|||
|
||||
impl McBufReadable for PlayerAbilitiesFlags {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let byte = u8::read_from(buf)?;
|
||||
let set = FixedBitSet::<4>::read_from(buf)?;
|
||||
Ok(PlayerAbilitiesFlags {
|
||||
invulnerable: byte & 1 != 0,
|
||||
flying: byte & 2 != 0,
|
||||
can_fly: byte & 4 != 0,
|
||||
instant_break: byte & 8 != 0,
|
||||
invulnerable: set.index(0),
|
||||
flying: set.index(1),
|
||||
can_fly: set.index(2),
|
||||
instant_break: set.index(3),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for PlayerAbilitiesFlags {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut byte = 0;
|
||||
let mut set = FixedBitSet::<4>::new();
|
||||
if self.invulnerable {
|
||||
byte |= 0b1;
|
||||
set.set(0);
|
||||
}
|
||||
if self.flying {
|
||||
byte |= 0b10;
|
||||
set.set(1);
|
||||
}
|
||||
if self.can_fly {
|
||||
byte |= 0b100;
|
||||
set.set(2);
|
||||
}
|
||||
if self.instant_break {
|
||||
byte |= 0b1000;
|
||||
set.set(3);
|
||||
}
|
||||
u8::write_into(&byte, buf)
|
||||
set.write_into(buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ impl ClientboundPlayerChatPacket {
|
|||
/// Returns the content of the message. If you want to get the Component
|
||||
/// for the whole message including the sender part, use
|
||||
/// [`ClientboundPlayerChatPacket::message`].
|
||||
#[must_use]
|
||||
pub fn content(&self) -> Component {
|
||||
self.unsigned_content
|
||||
.clone()
|
||||
|
@ -97,6 +98,7 @@ impl ClientboundPlayerChatPacket {
|
|||
}
|
||||
|
||||
/// Get the full message, including the sender part.
|
||||
#[must_use]
|
||||
pub fn message(&self) -> Component {
|
||||
let sender = self.chat_type.name.clone();
|
||||
let content = self.content();
|
||||
|
@ -119,6 +121,7 @@ impl ClientboundPlayerChatPacket {
|
|||
}
|
||||
|
||||
impl ChatType {
|
||||
#[must_use]
|
||||
pub fn chat_translation_key(&self) -> &'static str {
|
||||
match self {
|
||||
ChatType::Chat => "chat.type.text",
|
||||
|
@ -131,15 +134,11 @@ impl ChatType {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn narrator_translation_key(&self) -> &'static str {
|
||||
match self {
|
||||
ChatType::Chat => "chat.type.text.narrate",
|
||||
ChatType::SayCommand => "chat.type.text.narrate",
|
||||
ChatType::MsgCommandIncoming => "chat.type.text.narrate",
|
||||
ChatType::MsgCommandOutgoing => "chat.type.text.narrate",
|
||||
ChatType::TeamMsgCommandIncoming => "chat.type.text.narrate",
|
||||
ChatType::TeamMsgCommandOutgoing => "chat.type.text.narrate",
|
||||
ChatType::EmoteCommand => "chat.type.emote",
|
||||
_ => "chat.type.text.narrate",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use azalea_buf::{BufReadError, McBuf};
|
||||
use azalea_buf::{McBufReadable, McBufWritable};
|
||||
use azalea_core::FixedBitSet;
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
use std::io::{Cursor, Write};
|
||||
|
||||
|
@ -29,35 +30,35 @@ pub struct RelativeArguments {
|
|||
|
||||
impl McBufReadable for RelativeArguments {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let byte = u8::read_from(buf)?;
|
||||
let set = FixedBitSet::<5>::read_from(buf)?;
|
||||
Ok(RelativeArguments {
|
||||
x: byte & 0b1 != 0,
|
||||
y: byte & 0b10 != 0,
|
||||
z: byte & 0b100 != 0,
|
||||
y_rot: byte & 0b1000 != 0,
|
||||
x_rot: byte & 0b10000 != 0,
|
||||
x: set.index(0),
|
||||
y: set.index(1),
|
||||
z: set.index(2),
|
||||
y_rot: set.index(3),
|
||||
x_rot: set.index(4),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for RelativeArguments {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut byte = 0;
|
||||
let mut set = FixedBitSet::<5>::new();
|
||||
if self.x {
|
||||
byte |= 0b1;
|
||||
set.set(0);
|
||||
}
|
||||
if self.y {
|
||||
byte |= 0b10;
|
||||
set.set(1);
|
||||
}
|
||||
if self.z {
|
||||
byte |= 0b100;
|
||||
set.set(2);
|
||||
}
|
||||
if self.y_rot {
|
||||
byte |= 0b1000;
|
||||
set.set(3);
|
||||
}
|
||||
if self.x_rot {
|
||||
byte |= 0b10000;
|
||||
set.set(4);
|
||||
}
|
||||
u8::write_into(&byte, buf)
|
||||
set.write_into(buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ impl McBufReadable for BlockStateWithPosition {
|
|||
impl McBufWritable for BlockStateWithPosition {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let data = (self.state as u64) << 12
|
||||
| ((self.pos.x as u64) << 8 | (self.pos.z as u64) << 4 | (self.pos.y as u64));
|
||||
| (u64::from(self.pos.x) << 8 | u64::from(self.pos.z) << 4 | u64::from(self.pos.y));
|
||||
u64::var_write_into(&data, buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ pub enum EquipmentSlot {
|
|||
}
|
||||
|
||||
impl EquipmentSlot {
|
||||
#[must_use]
|
||||
pub fn from_byte(byte: u8) -> Option<Self> {
|
||||
match byte {
|
||||
0 => Some(EquipmentSlot::MainHand),
|
||||
|
|
|
@ -22,7 +22,7 @@ impl McBufReadable for Method {
|
|||
0 => Method::Add(DisplayInfo::read_from(buf)?),
|
||||
1 => Method::Remove,
|
||||
2 => Method::Change(DisplayInfo::read_from(buf)?),
|
||||
id => return Err(BufReadError::UnexpectedEnumVariant { id: id as i32 }),
|
||||
id => return Err(BufReadError::UnexpectedEnumVariant { id: i32::from(id) }),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ impl McBufReadable for Method {
|
|||
2 => Method::Change(Parameters::read_from(buf)?),
|
||||
3 => Method::Join(PlayerList::read_from(buf)?),
|
||||
4 => Method::Leave(PlayerList::read_from(buf)?),
|
||||
id => return Err(BufReadError::UnexpectedEnumVariant { id: id as i32 }),
|
||||
id => return Err(BufReadError::UnexpectedEnumVariant { id: i32::from(id) }),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ impl McBufWritable for ClientboundSetScorePacket {
|
|||
// convert None to an empty string
|
||||
self.objective_name
|
||||
.as_ref()
|
||||
.unwrap_or(&"".to_string())
|
||||
.unwrap_or(&String::new())
|
||||
.write_into(buf)?;
|
||||
if let Method::Change { score } = self.method {
|
||||
score.var_write_into(buf)?;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
|
||||
use azalea_core::ResourceLocation;
|
||||
use azalea_core::{FixedBitSet, ResourceLocation};
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
use std::io::{Cursor, Write};
|
||||
|
||||
|
@ -13,13 +13,13 @@ pub struct ClientboundStopSoundPacket {
|
|||
|
||||
impl McBufReadable for ClientboundStopSoundPacket {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let byte = u8::read_from(buf)?;
|
||||
let source = if byte & 1 != 0 {
|
||||
let set = FixedBitSet::<2>::read_from(buf)?;
|
||||
let source = if set.index(0) {
|
||||
Some(SoundSource::read_from(buf)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let name = if byte & 2 != 0 {
|
||||
let name = if set.index(1) {
|
||||
Some(ResourceLocation::read_from(buf)?)
|
||||
} else {
|
||||
None
|
||||
|
@ -31,14 +31,14 @@ impl McBufReadable for ClientboundStopSoundPacket {
|
|||
|
||||
impl McBufWritable for ClientboundStopSoundPacket {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut byte = 0u8;
|
||||
let mut set = FixedBitSet::<2>::new();
|
||||
if self.source.is_some() {
|
||||
byte |= 1;
|
||||
set.set(0);
|
||||
}
|
||||
if self.name.is_some() {
|
||||
byte |= 2;
|
||||
set.set(1);
|
||||
}
|
||||
byte.write_into(buf)?;
|
||||
set.write_into(buf)?;
|
||||
if let Some(source) = &self.source {
|
||||
source.write_into(buf)?;
|
||||
}
|
||||
|
|
|
@ -17,10 +17,8 @@ pub struct ClientboundUpdateAdvancementsPacket {
|
|||
pub struct Advancement {
|
||||
parent_id: Option<ResourceLocation>,
|
||||
display: Option<DisplayInfo>,
|
||||
// rewards: AdvancementRewards.EMPTY,
|
||||
criteria: HashMap<ResourceLocation, Criterion>,
|
||||
requirements: Vec<Vec<String>>,
|
||||
// requirements_strategy: RequirementsStrategy.AND
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -87,11 +85,11 @@ impl azalea_buf::McBufReadable for DisplayInfo {
|
|||
description,
|
||||
icon,
|
||||
frame,
|
||||
show_toast,
|
||||
hidden,
|
||||
background,
|
||||
x,
|
||||
y,
|
||||
hidden,
|
||||
show_toast,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -114,57 +112,77 @@ pub struct CriterionProgress {
|
|||
date: Option<u64>,
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use super::*;
|
||||
// use azalea_buf::{McBufReadable, McBufWritable};
|
||||
// use azalea_core::ResourceLocation;
|
||||
// use azalea_protocol_macros::ClientboundGamePacket;
|
||||
// use std::io::Cursor;
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use azalea_buf::{McBufReadable, McBufWritable};
|
||||
use azalea_core::ResourceLocation;
|
||||
use std::io::Cursor;
|
||||
|
||||
// #[test]
|
||||
// fn test() {
|
||||
// let mut buf = Cursor::new(Vec::new());
|
||||
// let packet = ClientboundUpdateAdvancementsPacket {
|
||||
// reset: true,
|
||||
// added: [(
|
||||
// ResourceLocation::new("minecraft:test").unwrap(),
|
||||
// Advancement {
|
||||
// parent_id: None,
|
||||
// display: Some(DisplayInfo {
|
||||
// title: Component::from("title".to_string()),
|
||||
// description:
|
||||
// Component::from("description".to_string()), icon:
|
||||
// Slot::Empty, frame: FrameType::Task,
|
||||
// show_toast: true,
|
||||
// hidden: false,
|
||||
// background: None,
|
||||
// x: 0.0,
|
||||
// y: 0.0,
|
||||
// }),
|
||||
// criteria: HashMap::new(),
|
||||
// requirements: Vec::new(),
|
||||
// },
|
||||
// )]
|
||||
// .into_iter()
|
||||
// .collect(),
|
||||
// removed: vec![ResourceLocation::new("minecraft:test2").unwrap()],
|
||||
// progress: [(
|
||||
// ResourceLocation::new("minecraft:test3").unwrap(),
|
||||
// [(
|
||||
// ResourceLocation::new("minecraft:test4").unwrap(),
|
||||
// CriterionProgress {
|
||||
// date: Some(123456789),
|
||||
// },
|
||||
// )]
|
||||
// .into_iter()
|
||||
// .collect(),
|
||||
// )]
|
||||
// .into_iter()
|
||||
// .collect(),
|
||||
// };
|
||||
// packet.write_into(&mut buf).unwrap();
|
||||
// let packet = ClientboundUpdateAdvancementsPacket::read_from(&mut
|
||||
// buf).unwrap(); assert_eq!(packet.reset, true);
|
||||
// }
|
||||
// }
|
||||
#[test]
|
||||
fn test() {
|
||||
let packet = ClientboundUpdateAdvancementsPacket {
|
||||
reset: true,
|
||||
added: [(
|
||||
ResourceLocation::new("minecraft:test").unwrap(),
|
||||
Advancement {
|
||||
parent_id: None,
|
||||
display: Some(DisplayInfo {
|
||||
title: Component::from("title".to_string()),
|
||||
description: Component::from("description".to_string()),
|
||||
icon: Slot::Empty,
|
||||
frame: FrameType::Task,
|
||||
show_toast: true,
|
||||
hidden: false,
|
||||
background: None,
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
}),
|
||||
criteria: HashMap::new(),
|
||||
requirements: Vec::new(),
|
||||
},
|
||||
)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
removed: vec![ResourceLocation::new("minecraft:test2").unwrap()],
|
||||
progress: [(
|
||||
ResourceLocation::new("minecraft:test3").unwrap(),
|
||||
[(
|
||||
ResourceLocation::new("minecraft:test4").unwrap(),
|
||||
CriterionProgress {
|
||||
date: Some(123456789),
|
||||
},
|
||||
)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.write_into(&mut data).unwrap();
|
||||
let mut buf: Cursor<&[u8]> = Cursor::new(&data);
|
||||
|
||||
let read_packet = ClientboundUpdateAdvancementsPacket::read_from(&mut buf).unwrap();
|
||||
assert_eq!(packet.reset, read_packet.reset);
|
||||
assert_eq!(packet.removed, read_packet.removed);
|
||||
|
||||
let advancement = packet
|
||||
.added
|
||||
.get(&ResourceLocation::new("minecraft:test").unwrap())
|
||||
.unwrap()
|
||||
.clone();
|
||||
let read_advancement = read_packet
|
||||
.added
|
||||
.get(&ResourceLocation::new("minecraft:test").unwrap())
|
||||
.unwrap()
|
||||
.clone();
|
||||
assert_eq!(advancement.parent_id, read_advancement.parent_id);
|
||||
|
||||
let display = advancement.display.unwrap();
|
||||
let read_display = read_advancement.display.unwrap();
|
||||
assert_eq!(display.title, read_display.title);
|
||||
assert_eq!(display.description, read_display.description);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use azalea_buf::McBuf;
|
||||
use azalea_buf::{McBuf, McBufReadable, McBufWritable};
|
||||
use azalea_core::FixedBitSet;
|
||||
use azalea_protocol_macros::ServerboundGamePacket;
|
||||
|
||||
#[derive(Clone, Debug, McBuf, ServerboundGamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, ServerboundGamePacket, PartialEq, Eq)]
|
||||
pub struct ServerboundClientInformationPacket {
|
||||
/// The locale of the client.
|
||||
pub language: String,
|
||||
|
@ -14,7 +15,7 @@ pub struct ServerboundClientInformationPacket {
|
|||
/// Whether the messages sent from the server should have colors. Note that
|
||||
/// many servers ignore this and always send colored messages.
|
||||
pub chat_colors: bool,
|
||||
pub model_customisation: u8,
|
||||
pub model_customisation: ModelCustomization,
|
||||
pub main_hand: HumanoidArm,
|
||||
pub text_filtering_enabled: bool,
|
||||
/// Whether the client should show up as "Anonymous Player" in the server
|
||||
|
@ -27,9 +28,9 @@ impl Default for ServerboundClientInformationPacket {
|
|||
Self {
|
||||
language: "en_us".to_string(),
|
||||
view_distance: 8,
|
||||
chat_visibility: ChatVisibility::Full,
|
||||
chat_visibility: ChatVisibility::default(),
|
||||
chat_colors: true,
|
||||
model_customisation: 0,
|
||||
model_customisation: ModelCustomization::default(),
|
||||
main_hand: HumanoidArm::Right,
|
||||
text_filtering_enabled: false,
|
||||
allows_listing: false,
|
||||
|
@ -37,9 +38,10 @@ impl Default for ServerboundClientInformationPacket {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(McBuf, Clone, Copy, Debug)]
|
||||
#[derive(McBuf, Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
pub enum ChatVisibility {
|
||||
/// All chat messages should be sent to the client.
|
||||
#[default]
|
||||
Full = 0,
|
||||
/// Chat messages from other players should be not sent to the client, only
|
||||
/// messages from the server like "Player joined the game" should be sent.
|
||||
|
@ -48,8 +50,125 @@ pub enum ChatVisibility {
|
|||
Hidden = 2,
|
||||
}
|
||||
|
||||
#[derive(McBuf, Clone, Copy, Debug)]
|
||||
#[derive(McBuf, Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
pub enum HumanoidArm {
|
||||
Left = 0,
|
||||
#[default]
|
||||
Right = 1,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct ModelCustomization {
|
||||
pub cape: bool,
|
||||
pub jacket: bool,
|
||||
pub left_sleeve: bool,
|
||||
pub right_sleeve: bool,
|
||||
pub left_pants: bool,
|
||||
pub right_pants: bool,
|
||||
pub hat: bool,
|
||||
}
|
||||
|
||||
impl Default for ModelCustomization {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
cape: true,
|
||||
jacket: true,
|
||||
left_sleeve: true,
|
||||
right_sleeve: true,
|
||||
left_pants: true,
|
||||
right_pants: true,
|
||||
hat: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufReadable for ModelCustomization {
|
||||
fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> {
|
||||
let set = FixedBitSet::<7>::read_from(buf)?;
|
||||
Ok(Self {
|
||||
cape: set.index(0),
|
||||
jacket: set.index(1),
|
||||
left_sleeve: set.index(2),
|
||||
right_sleeve: set.index(3),
|
||||
left_pants: set.index(4),
|
||||
right_pants: set.index(5),
|
||||
hat: set.index(6),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for ModelCustomization {
|
||||
fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> {
|
||||
let mut set = FixedBitSet::<7>::new();
|
||||
if self.cape {
|
||||
set.set(0);
|
||||
}
|
||||
if self.jacket {
|
||||
set.set(1);
|
||||
}
|
||||
if self.left_sleeve {
|
||||
set.set(2);
|
||||
}
|
||||
if self.right_sleeve {
|
||||
set.set(3);
|
||||
}
|
||||
if self.left_pants {
|
||||
set.set(4);
|
||||
}
|
||||
if self.right_pants {
|
||||
set.set(5);
|
||||
}
|
||||
if self.hat {
|
||||
set.set(6);
|
||||
}
|
||||
set.write_into(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
#[test]
|
||||
fn test_client_information_packet() {
|
||||
{
|
||||
let data = ServerboundClientInformationPacket::default();
|
||||
let mut buf = Vec::new();
|
||||
data.write_into(&mut buf).unwrap();
|
||||
let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
|
||||
|
||||
let read_data =
|
||||
ServerboundClientInformationPacket::read_from(&mut data_cursor).unwrap();
|
||||
assert_eq!(read_data, data);
|
||||
}
|
||||
|
||||
{
|
||||
let data = ServerboundClientInformationPacket {
|
||||
language: "en_gb".to_string(),
|
||||
view_distance: 24,
|
||||
chat_visibility: ChatVisibility::Hidden,
|
||||
chat_colors: false,
|
||||
model_customisation: ModelCustomization {
|
||||
cape: false,
|
||||
jacket: false,
|
||||
left_sleeve: true,
|
||||
right_sleeve: false,
|
||||
left_pants: true,
|
||||
right_pants: false,
|
||||
hat: true,
|
||||
},
|
||||
main_hand: HumanoidArm::Left,
|
||||
text_filtering_enabled: true,
|
||||
allows_listing: true,
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
data.write_into(&mut buf).unwrap();
|
||||
let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
|
||||
|
||||
let read_data =
|
||||
ServerboundClientInformationPacket::read_from(&mut data_cursor).unwrap();
|
||||
assert_eq!(read_data, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,9 +63,9 @@ impl McBufReadable for ActionType {
|
|||
let hand = InteractionHand::read_from(buf)?;
|
||||
Ok(ActionType::InteractAt {
|
||||
location: Vec3 {
|
||||
x: x as f64,
|
||||
y: y as f64,
|
||||
z: z as f64,
|
||||
x: f64::from(x),
|
||||
y: f64::from(y),
|
||||
z: f64::from(z),
|
||||
},
|
||||
hand,
|
||||
})
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
use crate::packets::BufReadError;
|
||||
use azalea_buf::{McBufReadable, McBufWritable};
|
||||
use azalea_core::FixedBitSet;
|
||||
use azalea_protocol_macros::ServerboundGamePacket;
|
||||
use std::io::Cursor;
|
||||
|
||||
#[derive(Clone, Debug, ServerboundGamePacket)]
|
||||
pub struct ServerboundPlayerAbilitiesPacket {
|
||||
is_flying: bool,
|
||||
pub is_flying: bool,
|
||||
}
|
||||
|
||||
impl McBufReadable for ServerboundPlayerAbilitiesPacket {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let byte = u8::read_from(buf)?;
|
||||
let set = FixedBitSet::<2>::read_from(buf)?;
|
||||
Ok(Self {
|
||||
is_flying: byte & 2 != 0,
|
||||
is_flying: set.index(1),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for ServerboundPlayerAbilitiesPacket {
|
||||
fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> {
|
||||
let mut byte = 0;
|
||||
let mut set = FixedBitSet::<2>::new();
|
||||
if self.is_flying {
|
||||
byte |= 2;
|
||||
set.set(1);
|
||||
}
|
||||
u8::write_into(&byte, buf)?;
|
||||
Ok(())
|
||||
set.write_into(buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::io::Cursor;
|
|||
|
||||
use azalea_buf::BufReadError;
|
||||
use azalea_buf::{McBufReadable, McBufWritable};
|
||||
use azalea_core::FixedBitSet;
|
||||
use azalea_protocol_macros::ServerboundGamePacket;
|
||||
|
||||
#[derive(Clone, Debug, ServerboundGamePacket)]
|
||||
|
@ -16,14 +17,12 @@ impl McBufReadable for ServerboundPlayerInputPacket {
|
|||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let xxa = f32::read_from(buf)?;
|
||||
let zza = f32::read_from(buf)?;
|
||||
let byte = u8::read_from(buf)?;
|
||||
let is_jumping = byte & 1 != 0;
|
||||
let is_shift_key_down = byte & 2 != 0;
|
||||
let set = FixedBitSet::<2>::read_from(buf)?;
|
||||
Ok(Self {
|
||||
xxa,
|
||||
zza,
|
||||
is_jumping,
|
||||
is_shift_key_down,
|
||||
is_jumping: set.index(0),
|
||||
is_shift_key_down: set.index(1),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -32,14 +31,13 @@ impl McBufWritable for ServerboundPlayerInputPacket {
|
|||
fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> {
|
||||
self.xxa.write_into(buf)?;
|
||||
self.zza.write_into(buf)?;
|
||||
let mut byte = 0u8;
|
||||
let mut set = FixedBitSet::<2>::new();
|
||||
if self.is_jumping {
|
||||
byte |= 1;
|
||||
set.set(0);
|
||||
}
|
||||
if self.is_shift_key_down {
|
||||
byte |= 2;
|
||||
set.set(1);
|
||||
}
|
||||
byte.write_into(buf)?;
|
||||
Ok(())
|
||||
set.write_into(buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::packets::McBufWritable;
|
||||
use azalea_buf::{BufReadError, McBuf, McBufReadable};
|
||||
use azalea_core::BlockPos;
|
||||
use azalea_core::{BlockPos, FixedBitSet};
|
||||
use azalea_protocol_macros::ServerboundGamePacket;
|
||||
use std::io::Cursor;
|
||||
|
||||
|
@ -28,17 +28,14 @@ impl McBufReadable for ServerboundSetCommandBlockPacket {
|
|||
let command = String::read_from(buf)?;
|
||||
let mode = Mode::read_from(buf)?;
|
||||
|
||||
let byte = u8::read_from(buf)?;
|
||||
let track_output = byte & 1 != 0;
|
||||
let conditional = byte & 2 != 0;
|
||||
let automatic = byte & 4 != 0;
|
||||
let set = FixedBitSet::<3>::read_from(buf)?;
|
||||
Ok(Self {
|
||||
pos,
|
||||
command,
|
||||
mode,
|
||||
track_output,
|
||||
conditional,
|
||||
automatic,
|
||||
track_output: set.index(0),
|
||||
conditional: set.index(1),
|
||||
automatic: set.index(2),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -49,17 +46,16 @@ impl McBufWritable for ServerboundSetCommandBlockPacket {
|
|||
self.command.write_into(buf)?;
|
||||
self.mode.write_into(buf)?;
|
||||
|
||||
let mut byte: u8 = 0;
|
||||
let mut set = FixedBitSet::<3>::new();
|
||||
if self.track_output {
|
||||
byte |= 1;
|
||||
set.set(0);
|
||||
}
|
||||
if self.conditional {
|
||||
byte |= 2;
|
||||
set.set(1);
|
||||
}
|
||||
if self.automatic {
|
||||
byte |= 4;
|
||||
set.set(2);
|
||||
}
|
||||
byte.write_into(buf)?;
|
||||
Ok(())
|
||||
set.write_into(buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::packets::BufReadError;
|
||||
use azalea_buf::McBuf;
|
||||
use azalea_buf::{McBufReadable, McBufWritable};
|
||||
use azalea_core::BlockPos;
|
||||
use azalea_core::{BlockPos, FixedBitSet};
|
||||
use azalea_protocol_macros::ServerboundGamePacket;
|
||||
use std::io::{Cursor, Write};
|
||||
|
||||
|
@ -69,28 +69,28 @@ pub struct Flags {
|
|||
|
||||
impl McBufReadable for Flags {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let byte = u8::read_from(buf)?;
|
||||
let set = FixedBitSet::<3>::read_from(buf)?;
|
||||
Ok(Self {
|
||||
ignore_entities: byte & 1 != 0,
|
||||
show_air: byte & 2 != 0,
|
||||
show_bounding_box: byte & 4 != 0,
|
||||
ignore_entities: set.index(0),
|
||||
show_air: set.index(1),
|
||||
show_bounding_box: set.index(2),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for Flags {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut byte = 0;
|
||||
let mut set = FixedBitSet::<3>::new();
|
||||
if self.ignore_entities {
|
||||
byte |= 1;
|
||||
set.set(0);
|
||||
}
|
||||
if self.show_air {
|
||||
byte |= 2;
|
||||
set.set(1);
|
||||
}
|
||||
if self.show_bounding_box {
|
||||
byte |= 4;
|
||||
set.set(2);
|
||||
}
|
||||
u8::write_into(&byte, buf)?;
|
||||
set.write_into(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ impl McBufWritable for BlockHitResult {
|
|||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
self.block_pos.write_into(buf)?;
|
||||
self.direction.write_into(buf)?;
|
||||
f32::write_into(&((self.location.x - (self.block_pos.x as f64)) as f32), buf)?;
|
||||
f32::write_into(&((self.location.y - (self.block_pos.y as f64)) as f32), buf)?;
|
||||
f32::write_into(&((self.location.z - (self.block_pos.z as f64)) as f32), buf)?;
|
||||
f32::write_into(&((self.location.x - f64::from(self.block_pos.x)) as f32), buf)?;
|
||||
f32::write_into(&((self.location.y - f64::from(self.block_pos.y)) as f32), buf)?;
|
||||
f32::write_into(&((self.location.z - f64::from(self.block_pos.z)) as f32), buf)?;
|
||||
self.inside.write_into(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -44,9 +44,9 @@ impl McBufReadable for BlockHitResult {
|
|||
block_pos,
|
||||
direction,
|
||||
location: Vec3 {
|
||||
x: block_pos.x as f64 + cursor_x as f64,
|
||||
y: block_pos.y as f64 + cursor_y as f64,
|
||||
z: block_pos.z as f64 + cursor_z as f64,
|
||||
x: f64::from(block_pos.x) + f64::from(cursor_x),
|
||||
y: f64::from(block_pos.y) + f64::from(cursor_y),
|
||||
z: f64::from(block_pos.z) + f64::from(cursor_z),
|
||||
},
|
||||
inside,
|
||||
})
|
||||
|
|
|
@ -21,6 +21,7 @@ pub enum ConnectionProtocol {
|
|||
}
|
||||
|
||||
impl ConnectionProtocol {
|
||||
#[must_use]
|
||||
pub fn from_i32(i: i32) -> Option<Self> {
|
||||
match i {
|
||||
-1 => Some(ConnectionProtocol::Handshake),
|
||||
|
@ -39,7 +40,7 @@ where
|
|||
{
|
||||
fn id(&self) -> u32;
|
||||
|
||||
/// Read a packet by its id, ConnectionProtocol, and flow
|
||||
/// Read a packet by its id, `ConnectionProtocol`, and flow
|
||||
fn read(id: u32, buf: &mut Cursor<&[u8]>) -> Result<Self, Box<ReadPacketError>>;
|
||||
|
||||
fn write(&self, buf: &mut impl Write) -> Result<(), std::io::Error>;
|
||||
|
|
|
@ -76,8 +76,8 @@ pub enum FrameSplitterError {
|
|||
ConnectionClosed,
|
||||
}
|
||||
|
||||
/// Read a length, then read that amount of bytes from BytesMut. If there's not
|
||||
/// enough data, return None
|
||||
/// Read a length, then read that amount of bytes from `BytesMut`. If there's
|
||||
/// not enough data, return None
|
||||
fn parse_frame(buffer: &mut BytesMut) -> Result<BytesMut, FrameSplitterError> {
|
||||
// copy the buffer first and read from the copy, then once we make sure
|
||||
// the packet is all good we read it fully
|
||||
|
@ -258,41 +258,28 @@ where
|
|||
Ok(packet)
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use super::*;
|
||||
// use crate::packets::game::{clientbound_player_chat_packet::ChatType,
|
||||
// ClientboundGamePacket}; use std::io::Cursor;
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::packets::game::ClientboundGamePacket;
|
||||
use std::io::Cursor;
|
||||
|
||||
// #[tokio::test]
|
||||
// async fn test_read_packet() {
|
||||
// let mut buf: Cursor<&[u8]> = Cursor::new(&[
|
||||
// 51, 0, 12, 177, 250, 155, 132, 106, 60, 218, 161, 217, 90, 157,
|
||||
// 105, 57, 206, 20, 0, 5, 104, 101, 108, 108, 111, 0, 0, 0, 0, 0,
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 116, 123, 34, 101, 120,
|
||||
// 116, 114, 97, 34, 58, 91, 123, 34, 99, 111, 108, 111, 114, 34, 58,
|
||||
// 34, 103, 114, 97, 121, 34, 44, 34, 116, 101, 120, 116, 34, 58,
|
||||
// 34, 91, 77, 69, 77, 66, 69, 82, 93, 32, 112, 108, 97, 121, 101,
|
||||
// 114, 49, 34, 125, 44, 123, 34, 116, 101, 120, 116, 34, 58, 34,
|
||||
// 32, 34, 125, 44, 123, 34, 99, 111, 108, 111, 114, 34, 58, 34, 103,
|
||||
// 114, 97, 121, 34, 44, 34, 116, 101, 120, 116, 34, 58, 34, 92,
|
||||
// 117, 48, 48, 51, 101, 32, 104, 101, 108, 108, 111, 34, 125, 93,
|
||||
// 44, 34, 116, 101, 120, 116, 34, 58, 34, 34, 125, 0, 7, 64, 123,
|
||||
// 34, 101, 120, 116, 114, 97, 34, 58, 91, 123, 34, 99, 111, 108, 111, 114,
|
||||
// 34, 58, 34, 103, 114, 97, 121, 34, 44, 34, 116, 101, 120, 116,
|
||||
// 34, 58, 34, 91, 77, 69, 77, 66, 69, 82, 93, 32, 112, 108, 97,
|
||||
// 121, 101, 114, 49, 34, 125, 93, 44, 34, 116, 101, 120, 116, 34,
|
||||
// 58, 34, 34, 125, 0, ]);
|
||||
// let packet = packet_decoder::<ClientboundGamePacket>(&mut
|
||||
// buf).unwrap(); match &packet {
|
||||
// ClientboundGamePacket::PlayerChat(m) => {
|
||||
// assert_eq!(
|
||||
// m.chat_type.chat_type,
|
||||
// ChatType::Chat,
|
||||
// "Enums should default if they're invalid"
|
||||
// );
|
||||
// }
|
||||
// _ => panic!("Wrong packet type"),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
#[tokio::test]
|
||||
async fn test_read_packet() {
|
||||
let mut buf: Cursor<&[u8]> = Cursor::new(&[
|
||||
56, 64, 85, 58, 141, 138, 71, 146, 193, 64, 88, 0, 0, 0, 0, 0, 0, 64, 60, 224, 105, 34,
|
||||
119, 8, 228, 67, 50, 51, 68, 194, 177, 230, 101, 0, 17, 0,
|
||||
]);
|
||||
let packet = packet_decoder::<ClientboundGamePacket>(&mut buf).unwrap();
|
||||
match &packet {
|
||||
ClientboundGamePacket::PlayerPosition(p) => {
|
||||
assert_eq!(p.id, 17);
|
||||
assert_eq!(p.x, 84.91488892545296);
|
||||
assert_eq!(p.y, 96.0);
|
||||
assert_eq!(p.z, 28.876604227124417);
|
||||
assert_eq!(p.dismount_vehicle, false);
|
||||
}
|
||||
_ => panic!("Wrong packet type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ pub enum ResolverError {
|
|||
|
||||
/// Resolve a Minecraft server address into an IP address and port.
|
||||
/// If it's already an IP address, it's returned as-is.
|
||||
#[must_use]
|
||||
#[async_recursion]
|
||||
pub async fn resolve_address(address: &ServerAddress) -> Result<SocketAddr, ResolverError> {
|
||||
// If the address.host is already in the format of an ip address, return it.
|
||||
|
|
Loading…
Add table
Reference in a new issue