diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2024-11-27 19:31:40 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-27 19:31:40 -0600 |
| commit | 08958c2278b15ebeac8a964f392ebb792e479b61 (patch) | |
| tree | 4ae3664cea38d7fd1a8f1e95ed06fac04ffe519e /azalea-protocol/src/packets/game/c_commands.rs | |
| parent | 139d77d3c2b0922fba5e9d4fa2bd9819d78bd773 (diff) | |
| download | azalea-drasl-08958c2278b15ebeac8a964f392ebb792e479b61.tar.xz | |
Refactor azalea-protocol (#190)
* 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
Diffstat (limited to 'azalea-protocol/src/packets/game/c_commands.rs')
| -rwxr-xr-x | azalea-protocol/src/packets/game/c_commands.rs | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/azalea-protocol/src/packets/game/c_commands.rs b/azalea-protocol/src/packets/game/c_commands.rs new file mode 100755 index 00000000..1a231559 --- /dev/null +++ b/azalea-protocol/src/packets/game/c_commands.rs @@ -0,0 +1,378 @@ +use std::io::{Cursor, Write}; + +use azalea_buf::{AzBuf, AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError}; +use azalea_core::{bitset::FixedBitSet, resource_location::ResourceLocation}; +use azalea_protocol_macros::ClientboundGamePacket; +use tracing::warn; + +#[derive(Clone, Debug, AzBuf, ClientboundGamePacket)] +pub struct ClientboundCommands { + pub entries: Vec<BrigadierNodeStub>, + #[var] + pub root_index: u32, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct BrigadierNodeStub { + pub is_executable: bool, + pub children: Vec<u32>, + pub redirect_node: Option<u32>, + pub node_type: NodeType, +} + +#[derive(Debug, Clone, Eq)] +pub struct BrigadierNumber<T> { + pub min: Option<T>, + pub max: Option<T>, +} +impl<T> BrigadierNumber<T> { + pub fn new(min: Option<T>, max: Option<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: AzaleaRead> AzaleaRead for BrigadierNumber<T> { + fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> { + let flags = FixedBitSet::<2>::azalea_read(buf)?; + let min = if flags.index(0) { + Some(T::azalea_read(buf)?) + } else { + None + }; + let max = if flags.index(1) { + Some(T::azalea_read(buf)?) + } else { + None + }; + Ok(BrigadierNumber { min, max }) + } +} +impl<T: AzaleaWrite> AzaleaWrite for BrigadierNumber<T> { + fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + let mut flags = FixedBitSet::<2>::new(); + if self.min.is_some() { + flags.set(0); + } + if self.max.is_some() { + flags.set(1); + } + flags.azalea_write(buf)?; + if let Some(min) = &self.min { + min.azalea_write(buf)?; + } + if let Some(max) = &self.max { + max.azalea_write(buf)?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, AzBuf, PartialEq, Eq)] +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, AzBuf, PartialEq)] +pub enum BrigadierParser { + Bool, + Float(BrigadierNumber<f32>), + Double(BrigadierNumber<f64>), + Integer(BrigadierNumber<i32>), + Long(BrigadierNumber<i64>), + String(BrigadierString), + Entity(EntityParser), + GameProfile, + BlockPos, + ColumnPos, + Vec3, + Vec2, + BlockState, + BlockPredicate, + ItemStack, + ItemPredicate, + Color, + FormattedText, + Style, + Message, + NbtCompoundTag, + NbtTag, + NbtPath, + Objective, + ObjectiveCriteria, + Operation, + Particle, + Angle, + Rotation, + ScoreboardSlot, + ScoreHolder { allows_multiple: bool }, + Swizzle, + Team, + ItemSlot, + ItemSlots, + ResourceLocation, + Function, + EntityAnchor, + IntRange, + FloatRange, + Dimension, + GameMode, + Time { min: i32 }, + ResourceOrTag { registry_key: ResourceLocation }, + ResourceOrTagKey { registry_key: ResourceLocation }, + Resource { registry_key: ResourceLocation }, + ResourceKey { registry_key: ResourceLocation }, + TemplateMirror, + TemplateRotation, + Heightmap, + LootTable, + LootPredicate, + LootModifier, + Uuid, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EntityParser { + pub single: bool, + pub players_only: bool, +} +impl AzaleaRead for EntityParser { + fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> { + let flags = FixedBitSet::<2>::azalea_read(buf)?; + Ok(EntityParser { + single: flags.index(0), + players_only: flags.index(1), + }) + } +} +impl AzaleaWrite for EntityParser { + fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + let mut flags = FixedBitSet::<2>::new(); + if self.single { + flags.set(0); + } + if self.players_only { + flags.set(1); + } + flags.azalea_write(buf)?; + Ok(()) + } +} + +// TODO: BrigadierNodeStub should have more stuff +impl AzaleaRead for BrigadierNodeStub { + fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> { + let flags = FixedBitSet::<8>::azalea_read(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 = 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>::azalea_read_var(buf)?; + let redirect_node = if has_redirect { + Some(u32::azalea_read_var(buf)?) + } else { + None + }; + + // argument node + if node_type == 2 { + let name = String::azalea_read(buf)?; + let parser = BrigadierParser::azalea_read(buf)?; + let suggestions_type = if has_suggestions_type { + Some(ResourceLocation::azalea_read(buf)?) + } else { + None + }; + let node = BrigadierNodeStub { + is_executable, + children, + redirect_node, + node_type: NodeType::Argument { + name, + parser, + suggestions_type, + }, + }; + return Ok(node); + } + // literal node + else if node_type == 1 { + let name = String::azalea_read(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 AzaleaWrite for BrigadierNodeStub { + fn azalea_write(&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 => { + flags.azalea_write(buf)?; + + self.children.azalea_write_var(buf)?; + + if let Some(redirect) = self.redirect_node { + redirect.azalea_write_var(buf)?; + } + } + NodeType::Literal { name } => { + flags.set(0); + flags.azalea_write(buf)?; + + self.children.azalea_write_var(buf)?; + + if let Some(redirect) = self.redirect_node { + redirect.azalea_write_var(buf)?; + } + + name.azalea_write(buf)?; + } + NodeType::Argument { + name, + parser, + suggestions_type, + } => { + flags.set(1); + if suggestions_type.is_some() { + flags.set(4); + } + flags.azalea_write(buf)?; + + self.children.azalea_write_var(buf)?; + + if let Some(redirect) = self.redirect_node { + redirect.azalea_write_var(buf)?; + } + + name.azalea_write(buf)?; + parser.azalea_write(buf)?; + + if let Some(suggestion) = suggestions_type { + suggestion.azalea_write(buf)?; + } + } + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum NodeType { + Root, + Literal { + name: String, + }, + Argument { + name: String, + parser: BrigadierParser, + suggestions_type: Option<ResourceLocation>, + }, +} + +impl BrigadierNodeStub { + #[must_use] + pub fn name(&self) -> Option<&str> { + match &self.node_type { + NodeType::Root => None, + 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.azalea_write(&mut buf).unwrap(); + let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); + let read_data = BrigadierNodeStub::azalea_read(&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.azalea_write(&mut buf).unwrap(); + let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); + let read_data = BrigadierNodeStub::azalea_read(&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")), + }, + }; + let mut buf = Vec::new(); + data.azalea_write(&mut buf).unwrap(); + let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); + let read_data = BrigadierNodeStub::azalea_read(&mut data_cursor).unwrap(); + assert_eq!(data, read_data); + } +} |
