1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00
azalea/azalea-protocol/src/packets/game/clientbound_commands_packet.rs
mat 6eee543a33
Pathfinder (#25)
Pathfinding is very much not done, but it works enough and I want to get this merged.
TODO: fast replanning, goals that aren't a single node, falling moves (it should be able to play the dropper), parkour moves
2022-11-12 23:54:05 -06:00

293 lines
8.9 KiB
Rust
Executable file

use azalea_buf::BufReadError;
use azalea_buf::McBuf;
use azalea_buf::McBufVarReadable;
use azalea_buf::{McBufReadable, McBufWritable};
use azalea_core::ResourceLocation;
use azalea_protocol_macros::ClientboundGamePacket;
use log::warn;
use std::io::Cursor;
use std::io::Write;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundCommandsPacket {
pub entries: Vec<BrigadierNodeStub>,
#[var]
pub root_index: u32,
}
#[derive(Debug, Clone)]
pub struct BrigadierNodeStub {
pub is_executable: bool,
pub children: Vec<u32>,
pub redirect_node: Option<u32>,
pub node_type: NodeType,
}
#[derive(Debug, Clone)]
pub struct BrigadierNumber<T> {
min: Option<T>,
max: Option<T>,
}
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 {
Some(T::read_from(buf)?)
} else {
None
};
let max = if flags & 0x02 != 0 {
Some(T::read_from(buf)?)
} else {
None
};
Ok(BrigadierNumber { min, max })
}
}
impl<T: McBufWritable> McBufWritable for BrigadierNumber<T> {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
let mut flags: u8 = 0;
if self.min.is_some() {
flags |= 0x01;
}
if self.max.is_some() {
flags |= 0x02;
}
flags.write_into(buf)?;
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, McBuf)]
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,
}
#[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,
IntRange,
FloatRange,
ItemEnchantment,
EntitySummon,
Dimension,
Uuid,
NbtTag,
NbtCompoundTag,
Time,
ResourceOrTag { registry_key: ResourceLocation },
Resource { registry_key: ResourceLocation },
TemplateMirror,
TemplateRotation,
}
impl McBufReadable for BrigadierParser {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
let parser_type = u32::var_read_from(buf)?;
match parser_type {
0 => Ok(BrigadierParser::Bool),
1 => Ok(BrigadierParser::Float(BrigadierNumber::read_from(buf)?)),
2 => Ok(BrigadierParser::Double(BrigadierNumber::read_from(buf)?)),
3 => Ok(BrigadierParser::Integer(BrigadierNumber::read_from(buf)?)),
4 => Ok(BrigadierParser::Long(BrigadierNumber::read_from(buf)?)),
5 => Ok(BrigadierParser::String(BrigadierString::read_from(buf)?)),
6 => {
let flags = u8::read_from(buf)?;
Ok(BrigadierParser::Entity {
single: flags & 0x01 != 0,
players_only: flags & 0x02 != 0,
})
}
7 => Ok(BrigadierParser::GameProfile),
8 => Ok(BrigadierParser::BlockPos),
9 => Ok(BrigadierParser::ColumnPos),
10 => Ok(BrigadierParser::Vec3),
11 => Ok(BrigadierParser::Vec2),
12 => Ok(BrigadierParser::BlockState),
13 => Ok(BrigadierParser::BlockPredicate),
14 => Ok(BrigadierParser::ItemStack),
15 => Ok(BrigadierParser::ItemPredicate),
16 => Ok(BrigadierParser::Color),
17 => Ok(BrigadierParser::Component),
18 => Ok(BrigadierParser::Message),
19 => Ok(BrigadierParser::NbtCompoundTag),
20 => Ok(BrigadierParser::NbtTag),
21 => Ok(BrigadierParser::NbtPath),
22 => Ok(BrigadierParser::Objective),
23 => Ok(BrigadierParser::ObjectiveCriteira),
24 => Ok(BrigadierParser::Operation),
25 => Ok(BrigadierParser::Particle),
26 => Ok(BrigadierParser::Angle),
27 => Ok(BrigadierParser::Rotation),
28 => Ok(BrigadierParser::ScoreboardSlot),
29 => {
let flags = u8::read_from(buf)?;
Ok(BrigadierParser::ScoreHolder {
allows_multiple: flags & 0x01 != 0,
})
}
30 => Ok(BrigadierParser::Swizzle),
31 => Ok(BrigadierParser::Team),
32 => Ok(BrigadierParser::ItemSlot),
33 => Ok(BrigadierParser::ResourceLocation),
34 => Ok(BrigadierParser::MobEffect),
35 => Ok(BrigadierParser::Function),
36 => Ok(BrigadierParser::EntityAnchor),
37 => Ok(BrigadierParser::IntRange),
38 => Ok(BrigadierParser::FloatRange),
39 => Ok(BrigadierParser::ItemEnchantment),
40 => Ok(BrigadierParser::EntitySummon),
41 => Ok(BrigadierParser::Dimension),
42 => Ok(BrigadierParser::Time),
43 => Ok(BrigadierParser::ResourceOrTag {
registry_key: ResourceLocation::read_from(buf)?,
}),
44 => Ok(BrigadierParser::Resource {
registry_key: ResourceLocation::read_from(buf)?,
}),
45 => Ok(BrigadierParser::TemplateMirror),
46 => Ok(BrigadierParser::TemplateRotation),
47 => Ok(BrigadierParser::Uuid),
_ => Err(BufReadError::UnexpectedEnumVariant {
id: parser_type as i32,
}),
}
}
}
// 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 node_type = flags & 0x03;
let is_executable = flags & 0x04 != 0;
let has_redirect = flags & 0x08 != 0;
let has_suggestions_type = flags & 0x10 != 0;
let children = Vec::<u32>::var_read_from(buf)?;
let redirect_node = if has_redirect {
Some(u32::var_read_from(buf)?)
} else {
None
};
// argument node
if node_type == 2 {
let name = String::read_from(buf)?;
let parser = BrigadierParser::read_from(buf)?;
let suggestions_type = if has_suggestions_type {
Some(ResourceLocation::read_from(buf)?)
} else {
None
};
return Ok(BrigadierNodeStub {
is_executable,
children,
redirect_node,
node_type: NodeType::Argument {
name,
parser,
suggestions_type,
},
});
}
// literal node
if node_type == 1 {
let name = String::read_from(buf)?;
return Ok(BrigadierNodeStub {
is_executable,
children,
redirect_node,
node_type: NodeType::Literal { name },
});
}
Ok(BrigadierNodeStub {
is_executable,
children,
redirect_node,
node_type: NodeType::Root,
})
}
}
impl McBufWritable for BrigadierNodeStub {
fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
todo!()
}
}
#[derive(Debug, Clone)]
pub enum NodeType {
Root,
Literal {
name: String,
},
Argument {
name: String,
parser: BrigadierParser,
suggestions_type: Option<ResourceLocation>,
},
}
impl BrigadierNodeStub {
pub fn name(&self) -> Option<&str> {
match &self.node_type {
NodeType::Root => None,
NodeType::Literal { name } => Some(name),
NodeType::Argument { name, .. } => Some(name),
}
}
}