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

update brigadier

This commit is contained in:
mat 2022-04-26 22:15:07 -05:00
parent 9c69d7d5f2
commit f859dbbba0
7 changed files with 328 additions and 7 deletions

View file

@ -117,6 +117,9 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> {
GamePacket::ClientboundUpdateRecipesPacket(p) => { GamePacket::ClientboundUpdateRecipesPacket(p) => {
println!("Got update recipes packet {:?}", p); println!("Got update recipes packet {:?}", p);
} }
GamePacket::ClientboundEntityEventPacket(p) => {
println!("Got entity event packet {:?}", p);
}
}, },
Err(e) => { Err(e) => {
panic!("Error: {:?}", e); panic!("Error: {:?}", e);

View file

@ -28,6 +28,7 @@ pub trait Readable {
async fn read_resource_location(&mut self) -> Result<ResourceLocation, String>; async fn read_resource_location(&mut self) -> Result<ResourceLocation, String>;
async fn read_short(&mut self) -> Result<i16, String>; async fn read_short(&mut self) -> Result<i16, String>;
async fn read_float(&mut self) -> Result<f32, String>; async fn read_float(&mut self) -> Result<f32, String>;
async fn read_double(&mut self) -> Result<f64, String>;
} }
#[async_trait] #[async_trait]
@ -130,7 +131,6 @@ where
self.read_exact(&mut buffer) self.read_exact(&mut buffer)
.await .await
.map_err(|_| "Invalid UTF-8".to_string())?; .map_err(|_| "Invalid UTF-8".to_string())?;
string.push_str(std::str::from_utf8(&buffer).unwrap()); string.push_str(std::str::from_utf8(&buffer).unwrap());
if string.len() > length as usize { if string.len() > length as usize {
return Err(format!( return Err(format!(
@ -200,6 +200,13 @@ where
Err(_) => Err("Error reading float".to_string()), Err(_) => Err("Error reading float".to_string()),
} }
} }
async fn read_double(&mut self) -> Result<f64, String> {
match AsyncReadExt::read_f64(self).await {
Ok(r) => Ok(r),
Err(_) => Err("Error reading double".to_string()),
}
}
} }
#[async_trait] #[async_trait]
@ -399,6 +406,17 @@ impl McBufReadable for f32 {
} }
} }
// f64
#[async_trait]
impl McBufReadable for f64 {
async fn read_into<R>(buf: &mut R) -> Result<Self, String>
where
R: AsyncRead + std::marker::Unpin + std::marker::Send,
{
buf.read_double().await
}
}
// GameType // GameType
#[async_trait] #[async_trait]
impl McBufReadable for GameType { impl McBufReadable for GameType {

View file

@ -42,6 +42,7 @@ pub trait Writable {
location: &ResourceLocation, location: &ResourceLocation,
) -> Result<(), std::io::Error>; ) -> Result<(), std::io::Error>;
fn write_float(&mut self, n: f32) -> Result<(), std::io::Error>; fn write_float(&mut self, n: f32) -> Result<(), std::io::Error>;
fn write_double(&mut self, n: f64) -> Result<(), std::io::Error>;
} }
#[async_trait] #[async_trait]
@ -152,6 +153,10 @@ impl Writable for Vec<u8> {
WriteBytesExt::write_f32::<BigEndian>(self, n) WriteBytesExt::write_f32::<BigEndian>(self, n)
} }
fn write_double(&mut self, n: f64) -> Result<(), std::io::Error> {
WriteBytesExt::write_f64::<BigEndian>(self, n)
}
fn write_resource_location( fn write_resource_location(
&mut self, &mut self,
location: &ResourceLocation, location: &ResourceLocation,
@ -291,6 +296,13 @@ impl McBufWritable for f32 {
} }
} }
// f64
impl McBufWritable for f64 {
fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
buf.write_double(*self)
}
}
// GameType // GameType
impl McBufWritable for GameType { impl McBufWritable for GameType {
fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> { fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {

View file

@ -1,8 +1,9 @@
use super::GamePacket; use super::GamePacket;
use crate::mc_buf::{McBufReadable, Readable}; use crate::mc_buf::{McBufReadable, McBufWritable, Readable};
use async_trait::async_trait; use async_trait::async_trait;
use azalea_core::resource_location::ResourceLocation;
use std::hash::Hash; use std::hash::Hash;
use tokio::io::AsyncRead; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWriteExt};
#[derive(Hash, Clone, Debug)] #[derive(Hash, Clone, Debug)]
pub struct ClientboundDeclareCommandsPacket { pub struct ClientboundDeclareCommandsPacket {
@ -23,6 +24,7 @@ impl ClientboundDeclareCommandsPacket {
buf: &mut T, buf: &mut T,
) -> Result<GamePacket, String> { ) -> Result<GamePacket, String> {
let node_count = buf.read_varint().await?; let node_count = buf.read_varint().await?;
println!("node_count: {}", node_count);
let mut nodes = Vec::with_capacity(node_count as usize); let mut nodes = Vec::with_capacity(node_count as usize);
for _ in 0..node_count { for _ in 0..node_count {
let node = BrigadierNodeStub::read_into(buf).await?; let node = BrigadierNodeStub::read_into(buf).await?;
@ -41,6 +43,268 @@ impl ClientboundDeclareCommandsPacket {
#[derive(Hash, Debug, Clone)] #[derive(Hash, Debug, Clone)]
pub struct BrigadierNodeStub {} pub struct BrigadierNodeStub {}
#[derive(Debug, Clone)]
pub struct BrigadierNumber<T> {
min: Option<T>,
max: Option<T>,
}
#[async_trait]
impl<T: McBufReadable + Send> McBufReadable for BrigadierNumber<T> {
async fn read_into<R>(buf: &mut R) -> Result<Self, String>
where
R: AsyncRead + std::marker::Unpin + std::marker::Send,
{
let flags = buf.read_byte().await?;
let min = if flags & 0x01 != 0 {
Some(T::read_into(buf).await?)
} else {
None
};
let max = if flags & 0x02 != 0 {
Some(T::read_into(buf).await?)
} else {
None
};
Ok(BrigadierNumber { min, max })
}
}
impl<T: McBufWritable> McBufWritable for BrigadierNumber<T> {
fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
let mut flags = 0;
if self.min.is_some() {
flags |= 0x01;
}
if self.max.is_some() {
flags |= 0x02;
}
buf.write_i8(flags);
if let Some(min) = &self.min {
min.write_into(buf)?;
}
if let Some(max) = &self.max {
max.write_into(buf)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy)]
pub enum BrigadierString {
/// Reads a single word
SingleWord = 0,
// If it starts with a ", keeps reading until another " (allowing escaping with \). Otherwise behaves the same as SINGLE_WORD
QuotablePhrase = 1,
// Reads the rest of the content after the cursor. Quotes will not be removed.
GreedyPhrase = 2,
}
#[async_trait]
impl McBufReadable for BrigadierString {
async fn read_into<R>(buf: &mut R) -> Result<Self, String>
where
R: AsyncRead + std::marker::Unpin + std::marker::Send,
{
let id = buf.read_byte().await?;
Ok(match id {
0 => BrigadierString::SingleWord,
1 => BrigadierString::QuotablePhrase,
2 => BrigadierString::GreedyPhrase,
_ => panic!("Unknown BrigadierString id: {}", id),
})
}
}
impl McBufWritable for BrigadierString {
fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
buf.write_i8(*self as i8);
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum BrigadierParser {
Bool,
Double(BrigadierNumber<f64>),
Float(BrigadierNumber<f32>),
Integer(BrigadierNumber<i32>),
Long(BrigadierNumber<i64>),
String(BrigadierString),
Entity { single: bool, players_only: bool },
GameProfile,
BlockPos,
ColumnPos,
Vec3,
Vec2,
BlockState,
BlockPredicate,
ItemStack,
ItemPredicate,
Color,
Component,
Message,
Nbt,
NbtPath,
Objective,
ObjectiveCriteira,
Operation,
Particle,
Rotation,
Angle,
ScoreboardSlot,
ScoreHolder { allows_multiple: bool },
Swizzle,
Team,
ItemSlot,
ResourceLocation,
MobEffect,
Function,
EntityAnchor,
Range { decimals_allowed: bool },
IntRange,
FloatRange,
ItemEnchantment,
EntitySummon,
Dimension,
Uuid,
NbtTag,
NbtCompoundTag,
Time,
ResourceOrTag { registry_key: ResourceLocation },
Resource { registry_key: ResourceLocation },
}
#[async_trait]
impl McBufReadable for BrigadierParser {
async fn read_into<R>(buf: &mut R) -> Result<Self, String>
where
R: AsyncRead + std::marker::Unpin + std::marker::Send,
{
let parser = buf.read_resource_location().await?;
if parser == ResourceLocation::new("brigadier:bool")? {
Ok(BrigadierParser::Bool)
} else if parser == ResourceLocation::new("brigadier:double")? {
Ok(BrigadierParser::Double(
BrigadierNumber::read_into(buf).await?,
))
} else if parser == ResourceLocation::new("brigadier:float")? {
Ok(BrigadierParser::Float(
BrigadierNumber::read_into(buf).await?,
))
} else if parser == ResourceLocation::new("brigadier:integer")? {
Ok(BrigadierParser::Integer(
BrigadierNumber::read_into(buf).await?,
))
} else if parser == ResourceLocation::new("brigadier:long")? {
Ok(BrigadierParser::Long(
BrigadierNumber::read_into(buf).await?,
))
} else if parser == ResourceLocation::new("brigadier:string")? {
Ok(BrigadierParser::String(
BrigadierString::read_into(buf).await?,
))
} else if parser == ResourceLocation::new("minecraft:entity")? {
let flags = buf.read_byte().await?;
Ok(BrigadierParser::Entity {
single: flags & 0x01 != 0,
players_only: flags & 0x02 != 0,
})
} else if parser == ResourceLocation::new("minecraft:game_profile")? {
Ok(BrigadierParser::GameProfile)
} else if parser == ResourceLocation::new("minecraft:block_pos")? {
Ok(BrigadierParser::BlockPos)
} else if parser == ResourceLocation::new("minecraft:column_pos")? {
Ok(BrigadierParser::ColumnPos)
} else if parser == ResourceLocation::new("minecraft:vec3")? {
Ok(BrigadierParser::Vec3)
} else if parser == ResourceLocation::new("minecraft:vec2")? {
Ok(BrigadierParser::Vec2)
} else if parser == ResourceLocation::new("minecraft:block_state")? {
Ok(BrigadierParser::BlockState)
} else if parser == ResourceLocation::new("minecraft:block_predicate")? {
Ok(BrigadierParser::BlockPredicate)
} else if parser == ResourceLocation::new("minecraft:item_stack")? {
Ok(BrigadierParser::ItemStack)
} else if parser == ResourceLocation::new("minecraft:item_predicate")? {
Ok(BrigadierParser::ItemPredicate)
} else if parser == ResourceLocation::new("minecraft:color")? {
Ok(BrigadierParser::Color)
} else if parser == ResourceLocation::new("minecraft:component")? {
Ok(BrigadierParser::Component)
} else if parser == ResourceLocation::new("minecraft:message")? {
Ok(BrigadierParser::Message)
} else if parser == ResourceLocation::new("minecraft:nbt")? {
Ok(BrigadierParser::Nbt)
} else if parser == ResourceLocation::new("minecraft:nbt_path")? {
Ok(BrigadierParser::NbtPath)
} else if parser == ResourceLocation::new("minecraft:objective")? {
Ok(BrigadierParser::Objective)
} else if parser == ResourceLocation::new("minecraft:objective_criteria")? {
Ok(BrigadierParser::ObjectiveCriteira)
} else if parser == ResourceLocation::new("minecraft:operation")? {
Ok(BrigadierParser::Operation)
} else if parser == ResourceLocation::new("minecraft:particle")? {
Ok(BrigadierParser::Particle)
} else if parser == ResourceLocation::new("minecraft:rotation")? {
Ok(BrigadierParser::Rotation)
} else if parser == ResourceLocation::new("minecraft:angle")? {
Ok(BrigadierParser::Angle)
} else if parser == ResourceLocation::new("minecraft:scoreboard_slot")? {
Ok(BrigadierParser::ScoreboardSlot)
} else if parser == ResourceLocation::new("minecraft:score_holder")? {
let flags = buf.read_byte().await?;
Ok(BrigadierParser::ScoreHolder {
allows_multiple: flags & 0x01 != 0,
})
} else if parser == ResourceLocation::new("minecraft:swizzle")? {
Ok(BrigadierParser::Swizzle)
} else if parser == ResourceLocation::new("minecraft:team")? {
Ok(BrigadierParser::Team)
} else if parser == ResourceLocation::new("minecraft:item_slot")? {
Ok(BrigadierParser::ItemSlot)
} else if parser == ResourceLocation::new("minecraft:resource_location")? {
Ok(BrigadierParser::ResourceLocation)
} else if parser == ResourceLocation::new("minecraft:mob_effect")? {
Ok(BrigadierParser::MobEffect)
} else if parser == ResourceLocation::new("minecraft:function")? {
Ok(BrigadierParser::Function)
} else if parser == ResourceLocation::new("minecraft:entity_anchor")? {
Ok(BrigadierParser::EntityAnchor)
} else if parser == ResourceLocation::new("minecraft:range")? {
Ok(BrigadierParser::Range {
decimals_allowed: buf.read_boolean().await?,
})
} else if parser == ResourceLocation::new("minecraft:int_range")? {
Ok(BrigadierParser::IntRange)
} else if parser == ResourceLocation::new("minecraft:float_range")? {
Ok(BrigadierParser::FloatRange)
} else if parser == ResourceLocation::new("minecraft:item_enchantment")? {
Ok(BrigadierParser::ItemEnchantment)
} else if parser == ResourceLocation::new("minecraft:entity_summon")? {
Ok(BrigadierParser::EntitySummon)
} else if parser == ResourceLocation::new("minecraft:dimension")? {
Ok(BrigadierParser::Dimension)
} else if parser == ResourceLocation::new("minecraft:uuid")? {
Ok(BrigadierParser::Uuid)
} else if parser == ResourceLocation::new("minecraft:nbt_tag")? {
Ok(BrigadierParser::NbtTag)
} else if parser == ResourceLocation::new("minecraft:nbt_compound_tag")? {
Ok(BrigadierParser::NbtCompoundTag)
} else if parser == ResourceLocation::new("minecraft:time")? {
Ok(BrigadierParser::Time)
} else if parser == ResourceLocation::new("minecraft:resource_or_tag")? {
Ok(BrigadierParser::ResourceOrTag {
registry_key: buf.read_resource_location().await?,
})
} else if parser == ResourceLocation::new("minecraft:resource")? {
Ok(BrigadierParser::Resource {
registry_key: buf.read_resource_location().await?,
})
} else {
panic!("Unknown Brigadier parser: {}", parser)
}
}
}
// azalea_brigadier::tree::CommandNode // azalea_brigadier::tree::CommandNode
#[async_trait] #[async_trait]
impl McBufReadable for BrigadierNodeStub { impl McBufReadable for BrigadierNodeStub {
@ -49,33 +313,46 @@ impl McBufReadable for BrigadierNodeStub {
R: AsyncRead + std::marker::Unpin + std::marker::Send, R: AsyncRead + std::marker::Unpin + std::marker::Send,
{ {
let flags = u8::read_into(buf).await?; let flags = u8::read_into(buf).await?;
if flags > 31 {
println!(
"Warning: The flags from a Brigadier node are over 31. This is probably a bug."
);
}
let node_type = flags & 0x03; let node_type = flags & 0x03;
let is_executable = flags & 0x04 != 0; let is_executable = flags & 0x04 != 0;
let has_redirect = flags & 0x08 != 0; let has_redirect = flags & 0x08 != 0;
let has_suggestions_type = flags & 0x10 != 0; let has_suggestions_type = flags & 0x10 != 0;
println!("flags: {}, node_type: {}, is_executable: {}, has_redirect: {}, has_suggestions_type: {}", flags, node_type, is_executable, has_redirect, has_suggestions_type);
let children = buf.read_int_id_list().await?; let children = buf.read_int_id_list().await?;
println!("children: {:?}", children);
let redirect_node = if has_redirect { let redirect_node = if has_redirect {
buf.read_varint().await? buf.read_varint().await?
} else { } else {
0 0
}; };
println!("redirect_node: {}", redirect_node);
// argument node
if node_type == 2 { if node_type == 2 {
let name = buf.read_utf().await?; let name = buf.read_utf().await?;
println!("name: {}", name);
let resource_location = if has_suggestions_type { let parser = BrigadierParser::read_into(buf).await?;
let suggestions_type = if has_suggestions_type {
Some(buf.read_resource_location().await?) Some(buf.read_resource_location().await?)
} else { } else {
None None
}; };
println!( println!(
"node_type=2, flags={}, name={}, resource_location={:?}", "node_type=2, flags={}, name={}, parser={:?}, suggestions_type={:?}",
flags, name, resource_location flags, name, parser, suggestions_type
); );
return Ok(BrigadierNodeStub {}); return Ok(BrigadierNodeStub {});
} }
// literal node
if node_type == 1 { if node_type == 1 {
let name = buf.read_utf().await?; let name = buf.read_utf().await?;
println!("node_type=1, flags={}, name={}", flags, name); println!("node_type=1, flags={}, name={}", flags, name);

View file

@ -0,0 +1,9 @@
use azalea_core::{game_type::GameType, resource_location::ResourceLocation};
use packet_macros::GamePacket;
// we can't identify the status in azalea-protocol since they vary depending on the entity
#[derive(Clone, Debug, GamePacket)]
pub struct ClientboundEntityEventPacket {
pub entity_id: i32,
pub entity_status: i8,
}

View file

@ -2,6 +2,7 @@ pub mod clientbound_change_difficulty_packet;
pub mod clientbound_custom_payload_packet; pub mod clientbound_custom_payload_packet;
pub mod clientbound_declare_commands_packet; pub mod clientbound_declare_commands_packet;
pub mod clientbound_disconnect_packet; pub mod clientbound_disconnect_packet;
pub mod clientbound_entity_event_packet;
pub mod clientbound_login_packet; pub mod clientbound_login_packet;
pub mod clientbound_player_abilities_packet; pub mod clientbound_player_abilities_packet;
pub mod clientbound_set_carried_item_packet; pub mod clientbound_set_carried_item_packet;
@ -18,6 +19,7 @@ declare_state_packets!(
0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, 0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket,
0x12: clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket, 0x12: clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket,
0x1a: clientbound_disconnect_packet::ClientboundDisconnectPacket, 0x1a: clientbound_disconnect_packet::ClientboundDisconnectPacket,
0x1b: clientbound_entity_event_packet::ClientboundEntityEventPacket,
0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, 0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket,
0x26: clientbound_login_packet::ClientboundLoginPacket, 0x26: clientbound_login_packet::ClientboundLoginPacket,
0x32: clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket, 0x32: clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket,

View file

@ -3,7 +3,7 @@ async fn main() {
println!("Hello, world!"); println!("Hello, world!");
// let address = "95.111.249.143:10000"; // let address = "95.111.249.143:10000";
let address = "localhost:57308"; let address = "localhost:53810";
// let response = azalea_client::ping::ping_server(&address.try_into().unwrap()) // let response = azalea_client::ping::ping_server(&address.try_into().unwrap())
// .await // .await
// .unwrap(); // .unwrap();