1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16: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) => {
println!("Got update recipes packet {:?}", p);
}
GamePacket::ClientboundEntityEventPacket(p) => {
println!("Got entity event packet {:?}", p);
}
},
Err(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_short(&mut self) -> Result<i16, String>;
async fn read_float(&mut self) -> Result<f32, String>;
async fn read_double(&mut self) -> Result<f64, String>;
}
#[async_trait]
@ -130,7 +131,6 @@ where
self.read_exact(&mut buffer)
.await
.map_err(|_| "Invalid UTF-8".to_string())?;
string.push_str(std::str::from_utf8(&buffer).unwrap());
if string.len() > length as usize {
return Err(format!(
@ -200,6 +200,13 @@ where
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]
@ -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
#[async_trait]
impl McBufReadable for GameType {

View file

@ -42,6 +42,7 @@ pub trait Writable {
location: &ResourceLocation,
) -> 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]
@ -152,6 +153,10 @@ impl Writable for Vec<u8> {
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(
&mut self,
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
impl McBufWritable for GameType {
fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {

View file

@ -1,8 +1,9 @@
use super::GamePacket;
use crate::mc_buf::{McBufReadable, Readable};
use crate::mc_buf::{McBufReadable, McBufWritable, Readable};
use async_trait::async_trait;
use azalea_core::resource_location::ResourceLocation;
use std::hash::Hash;
use tokio::io::AsyncRead;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWriteExt};
#[derive(Hash, Clone, Debug)]
pub struct ClientboundDeclareCommandsPacket {
@ -23,6 +24,7 @@ impl ClientboundDeclareCommandsPacket {
buf: &mut T,
) -> Result<GamePacket, String> {
let node_count = buf.read_varint().await?;
println!("node_count: {}", node_count);
let mut nodes = Vec::with_capacity(node_count as usize);
for _ in 0..node_count {
let node = BrigadierNodeStub::read_into(buf).await?;
@ -41,6 +43,268 @@ impl ClientboundDeclareCommandsPacket {
#[derive(Hash, Debug, Clone)]
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
#[async_trait]
impl McBufReadable for BrigadierNodeStub {
@ -49,33 +313,46 @@ impl McBufReadable for BrigadierNodeStub {
R: AsyncRead + std::marker::Unpin + std::marker::Send,
{
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 is_executable = flags & 0x04 != 0;
let has_redirect = flags & 0x08 != 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?;
println!("children: {:?}", children);
let redirect_node = if has_redirect {
buf.read_varint().await?
} else {
0
};
println!("redirect_node: {}", redirect_node);
// argument node
if node_type == 2 {
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?)
} else {
None
};
println!(
"node_type=2, flags={}, name={}, resource_location={:?}",
flags, name, resource_location
"node_type=2, flags={}, name={}, parser={:?}, suggestions_type={:?}",
flags, name, parser, suggestions_type
);
return Ok(BrigadierNodeStub {});
}
// literal node
if node_type == 1 {
let name = buf.read_utf().await?;
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_declare_commands_packet;
pub mod clientbound_disconnect_packet;
pub mod clientbound_entity_event_packet;
pub mod clientbound_login_packet;
pub mod clientbound_player_abilities_packet;
pub mod clientbound_set_carried_item_packet;
@ -18,6 +19,7 @@ declare_state_packets!(
0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket,
0x12: clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket,
0x1a: clientbound_disconnect_packet::ClientboundDisconnectPacket,
0x1b: clientbound_entity_event_packet::ClientboundEntityEventPacket,
0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket,
0x26: clientbound_login_packet::ClientboundLoginPacket,
0x32: clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket,

View file

@ -3,7 +3,7 @@ async fn main() {
println!("Hello, world!");
// 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())
// .await
// .unwrap();